Code

fix the font dropdown: patch by Felipe with my changes
[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_DIALOG_LAYERS,
727         SP_VERB_EDIT_CLONE,
728         SP_VERB_EDIT_COPY,
729         SP_VERB_EDIT_CUT,
730         SP_VERB_EDIT_DUPLICATE,
731         SP_VERB_EDIT_PASTE,
732         SP_VERB_EDIT_REDO,
733         SP_VERB_EDIT_UNDO,
734         SP_VERB_EDIT_UNLINK_CLONE,
735         SP_VERB_FILE_EXPORT,
736         SP_VERB_FILE_IMPORT,
737         SP_VERB_FILE_NEW,
738         SP_VERB_FILE_OPEN,
739         SP_VERB_FILE_PRINT,
740         SP_VERB_FILE_SAVE,
741         SP_VERB_OBJECT_TO_CURVE,
742         SP_VERB_SELECTION_GROUP,
743         SP_VERB_SELECTION_OUTLINE,
744         SP_VERB_SELECTION_UNGROUP,
745         SP_VERB_ZOOM_1_1,
746         SP_VERB_ZOOM_1_2,
747         SP_VERB_ZOOM_2_1,
748         SP_VERB_ZOOM_DRAWING,
749         SP_VERB_ZOOM_IN,
750         SP_VERB_ZOOM_NEXT,
751         SP_VERB_ZOOM_OUT,
752         SP_VERB_ZOOM_PAGE,
753         SP_VERB_ZOOM_PAGE_WIDTH,
754         SP_VERB_ZOOM_PREV,
755         SP_VERB_ZOOM_SELECTION,
756     };
758     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
760     static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
761     Glib::RefPtr<Gtk::ActionGroup> mainActions;
762     if ( groups.find(desktop) != groups.end() ) {
763         mainActions = groups[desktop];
764     }
766     if ( !mainActions ) {
767         mainActions = Gtk::ActionGroup::create("main");
768         groups[desktop] = mainActions;
769     }
771     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
772         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
773         if ( verb ) {
774             if (!mainActions->get_action(verb->get_id())) {
775                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
776                 mainActions->add(Glib::wrap(act));
777             }
778         }
779     }
781     if ( !mainActions->get_action("ToolZoom") ) {
782         GtkTooltips *tt = gtk_tooltips_new();
783         for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
784             Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
785             if ( va ) {
786                 mainActions->add(va);
787                 if ( i == 0 ) {
788                     va->set_active(true);
789                 }
790             }
791         }
792     }
795     return mainActions;
799 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
801     gtk_widget_set_size_request( widget,
802                                  widget->allocation.width,
803                                  widget->allocation.height );
806 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
808     gtk_widget_set_size_request( widget, -1, -1 );
813 GtkWidget *
814 sp_tool_toolbox_new()
816     GtkTooltips *tt = gtk_tooltips_new();
817     GtkWidget* tb = gtk_toolbar_new();
818     gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
819     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
821     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
822     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
824     gtk_widget_set_sensitive(tb, FALSE);
826     GtkWidget *hb = gtk_handle_box_new();
827     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
828     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
829     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
831     gtk_container_add(GTK_CONTAINER(hb), tb);
832     gtk_widget_show(GTK_WIDGET(tb));
834     sigc::connection* conn = new sigc::connection;
835     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
837     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
838     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
840     return hb;
843 GtkWidget *
844 sp_aux_toolbox_new()
846     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
848     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
850     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
852     gtk_widget_set_sensitive(tb, FALSE);
854     GtkWidget *hb = gtk_handle_box_new();
855     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
856     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
857     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
859     gtk_container_add(GTK_CONTAINER(hb), tb);
860     gtk_widget_show(GTK_WIDGET(tb));
862     sigc::connection* conn = new sigc::connection;
863     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
865     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
866     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
868     return hb;
871 //####################################
872 //# Commands Bar
873 //####################################
875 GtkWidget *
876 sp_commands_toolbox_new()
878     GtkWidget *tb = gtk_toolbar_new();
880     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
881     gtk_widget_set_sensitive(tb, FALSE);
883     GtkWidget *hb = gtk_handle_box_new();
884     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
885     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
886     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
888     gtk_container_add(GTK_CONTAINER(hb), tb);
889     gtk_widget_show(GTK_WIDGET(tb));
891     sigc::connection* conn = new sigc::connection;
892     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
894     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
895     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
897     return hb;
900 GtkWidget *
901 sp_snap_toolbox_new()
903         GtkWidget *tb = gtk_vbox_new(FALSE, 0);
904         gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
905         g_object_set_data(G_OBJECT(tb), "desktop", NULL);
907         //GtkWidget *tb = gtk_toolbar_new();
908     //g_object_set_data(G_OBJECT(tb), "desktop", NULL);
910     gtk_widget_set_sensitive(tb, FALSE);
912     GtkWidget *hb = gtk_handle_box_new();
913     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
914     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
915     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
917     gtk_container_add(GTK_CONTAINER(hb), tb);
918     gtk_widget_show(GTK_WIDGET(tb));
920     sigc::connection* conn = new sigc::connection;
921     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
923     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
924     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
926     return hb;
929 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
930                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
931                                                        Glib::ustring const &path, gdouble def,
932                                                        GtkWidget *focusTarget,
933                                                        GtkWidget *us,
934                                                        GObject *dataKludge,
935                                                        gboolean altx, gchar const *altx_mark,
936                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
937                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
938                                                        void (*callback)(GtkAdjustment *, GObject *),
939                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
941     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
942     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs->getDouble(path, def) * factor,
943                                                              lower, upper, step, page, 0 ) );
944     if (us) {
945         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
946     }
948     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
950     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
951     if ( shortLabel ) {
952         g_object_set( act, "short_label", shortLabel, NULL );
953     }
955     if ( (descrCount > 0) && descrLabels && descrValues ) {
956         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
957     }
959     if ( focusTarget ) {
960         ege_adjustment_action_set_focuswidget( act, focusTarget );
961     }
963     if ( altx && altx_mark ) {
964         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
965     }
967     if ( dataKludge ) {
968         // Rather lame, but it's the only place where we need to get the entry name
969         // but we don't have an Entry
970         g_object_set_data( dataKludge, prefs->getEntry(path).getEntryName().data(), adj );
971     }
973     // Using a cast just to make sure we pass in the right kind of function pointer
974     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
976     return act;
980 //####################################
981 //# node editing callbacks
982 //####################################
984 /**
985  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
986  */
987 static ShapeEditor *get_current_shape_editor()
989     if (!SP_ACTIVE_DESKTOP) {
990         return NULL;
991     }
993     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
995     if (!SP_IS_NODE_CONTEXT(event_context)) {
996         return NULL;
997     }
999     return event_context->shape_editor;
1003 void
1004 sp_node_path_edit_add(void)
1006     ShapeEditor *shape_editor = get_current_shape_editor();
1007     if (shape_editor) shape_editor->add_node();
1010 void
1011 sp_node_path_edit_delete(void)
1013     ShapeEditor *shape_editor = get_current_shape_editor();
1014     if (shape_editor) shape_editor->delete_nodes_preserving_shape();
1017 void
1018 sp_node_path_edit_delete_segment(void)
1020     ShapeEditor *shape_editor = get_current_shape_editor();
1021     if (shape_editor) shape_editor->delete_segment();
1024 void
1025 sp_node_path_edit_break(void)
1027     ShapeEditor *shape_editor = get_current_shape_editor();
1028     if (shape_editor) shape_editor->break_at_nodes();
1031 void
1032 sp_node_path_edit_join(void)
1034     ShapeEditor *shape_editor = get_current_shape_editor();
1035     if (shape_editor) shape_editor->join_nodes();
1038 void
1039 sp_node_path_edit_join_segment(void)
1041     ShapeEditor *shape_editor = get_current_shape_editor();
1042     if (shape_editor) shape_editor->join_segments();
1045 void
1046 sp_node_path_edit_toline(void)
1048     ShapeEditor *shape_editor = get_current_shape_editor();
1049     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1052 void
1053 sp_node_path_edit_tocurve(void)
1055     ShapeEditor *shape_editor = get_current_shape_editor();
1056     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1059 void
1060 sp_node_path_edit_cusp(void)
1062     ShapeEditor *shape_editor = get_current_shape_editor();
1063     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1066 void
1067 sp_node_path_edit_smooth(void)
1069     ShapeEditor *shape_editor = get_current_shape_editor();
1070     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1073 void
1074 sp_node_path_edit_symmetrical(void)
1076     ShapeEditor *shape_editor = get_current_shape_editor();
1077     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1080 void
1081 sp_node_path_edit_auto(void)
1083     ShapeEditor *shape_editor = get_current_shape_editor();
1084     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_AUTO);
1087 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1088     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1089     bool show = gtk_toggle_action_get_active( act );
1090     prefs->setBool("/tools/nodes/show_handles",  show);
1091     ShapeEditor *shape_editor = get_current_shape_editor();
1092     if (shape_editor) shape_editor->show_handles(show);
1095 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1096     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1097     bool show = gtk_toggle_action_get_active( act );
1098     prefs->setBool("/tools/nodes/show_helperpath",  show);
1099     ShapeEditor *shape_editor = get_current_shape_editor();
1100     if (shape_editor) shape_editor->show_helperpath(show);
1103 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1104     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1107 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1108     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1111 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1112     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1115 /* is called when the node selection is modified */
1116 static void
1117 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1119     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1120     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1121     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1122     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1124     // quit if run by the attr_changed listener
1125     if (g_object_get_data( tbl, "freeze" )) {
1126         return;
1127     }
1129     // in turn, prevent listener from responding
1130     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1132     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1133     SPUnit const *unit = tracker->getActiveUnit();
1135     ShapeEditor *shape_editor = get_current_shape_editor();
1136     if (shape_editor && shape_editor->has_nodepath()) {
1137         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1138         int n_selected = 0;
1139         if (nodepath) {
1140             n_selected = nodepath->numSelected();
1141         }
1143         if (n_selected == 0) {
1144             gtk_action_set_sensitive(xact, FALSE);
1145             gtk_action_set_sensitive(yact, FALSE);
1146         } else {
1147             gtk_action_set_sensitive(xact, TRUE);
1148             gtk_action_set_sensitive(yact, TRUE);
1149             Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1150             Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1152             if (n_selected == 1) {
1153                 Geom::Point sel_node = nodepath->singleSelectedCoords();
1154                 if (oldx != sel_node[Geom::X] || oldy != sel_node[Geom::Y]) {
1155                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[Geom::X], *unit));
1156                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[Geom::Y], *unit));
1157                 }
1158             } else {
1159                 boost::optional<Geom::Coord> x = sp_node_selected_common_coord(nodepath, Geom::X);
1160                 boost::optional<Geom::Coord> y = sp_node_selected_common_coord(nodepath, Geom::Y);
1161                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1162                     /* Note: Currently x and y will always have a value, even if the coordinates of the
1163                        selected nodes don't coincide (in this case we use the coordinates of the center
1164                        of the bounding box). So the entries are never set to zero. */
1165                     // FIXME: Maybe we should clear the entry if several nodes are selected
1166                     //        instead of providing a kind of average value
1167                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1168                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1169                 }
1170             }
1171         }
1172     } else {
1173         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1174         gtk_action_set_sensitive(xact, FALSE);
1175         gtk_action_set_sensitive(yact, FALSE);
1176     }
1178     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1181 static void
1182 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1184     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1185     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1187     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1188     SPUnit const *unit = tracker->getActiveUnit();
1190     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1191         prefs->setDouble(Glib::ustring("/tools/nodes/") + value_name, sp_units_get_pixels(adj->value, *unit));
1192     }
1194     // quit if run by the attr_changed listener
1195     if (g_object_get_data( tbl, "freeze" )) {
1196         return;
1197     }
1199     // in turn, prevent listener from responding
1200     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1202     ShapeEditor *shape_editor = get_current_shape_editor();
1203     if (shape_editor && shape_editor->has_nodepath()) {
1204         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1205         if (!strcmp(value_name, "x")) {
1206             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::X);
1207         }
1208         if (!strcmp(value_name, "y")) {
1209             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::Y);
1210         }
1211     }
1213     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1216 static void
1217 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1219     sp_node_path_value_changed(adj, tbl, "x");
1222 static void
1223 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1225     sp_node_path_value_changed(adj, tbl, "y");
1228 void
1229 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1231     {
1232     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1233     SPItem *item = selection->singleItem();
1234     if (item && SP_IS_LPE_ITEM(item)) {
1235        if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1236            gtk_action_set_sensitive(w, TRUE);
1237        } else {
1238            gtk_action_set_sensitive(w, FALSE);
1239        }
1240     } else {
1241        gtk_action_set_sensitive(w, FALSE);
1242     }
1243     }
1245     {
1246     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1247     SPItem *item = selection->singleItem();
1248     if (item && item->clip_ref && item->clip_ref->getObject()) {
1249        gtk_action_set_sensitive(w, TRUE);
1250     } else {
1251        gtk_action_set_sensitive(w, FALSE);
1252     }
1253     }
1255     {
1256     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1257     SPItem *item = selection->singleItem();
1258     if (item && item->mask_ref && item->mask_ref->getObject()) {
1259        gtk_action_set_sensitive(w, TRUE);
1260     } else {
1261        gtk_action_set_sensitive(w, FALSE);
1262     }
1263     }
1266 void
1267 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1269     sp_node_toolbox_sel_changed (selection, tbl);
1274 //################################
1275 //##    Node Editing Toolbox    ##
1276 //################################
1278 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1280     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1281     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1282     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1283     g_object_set_data( holder, "tracker", tracker );
1285     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
1287     {
1288         InkAction* inky = ink_action_new( "NodeInsertAction",
1289                                           _("Insert node"),
1290                                           _("Insert new nodes into selected segments"),
1291                                           INKSCAPE_ICON_NODE_ADD,
1292                                           secondarySize );
1293         g_object_set( inky, "short_label", _("Insert"), NULL );
1294         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1295         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1296     }
1298     {
1299         InkAction* inky = ink_action_new( "NodeDeleteAction",
1300                                           _("Delete node"),
1301                                           _("Delete selected nodes"),
1302                                           INKSCAPE_ICON_NODE_DELETE,
1303                                           secondarySize );
1304         g_object_set( inky, "short_label", _("Delete"), NULL );
1305         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1306         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1307     }
1309     {
1310         InkAction* inky = ink_action_new( "NodeJoinAction",
1311                                           _("Join endnodes"),
1312                                           _("Join selected endnodes"),
1313                                           INKSCAPE_ICON_NODE_JOIN,
1314                                           secondarySize );
1315         g_object_set( inky, "short_label", _("Join"), NULL );
1316         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1317         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1318     }
1320     {
1321         InkAction* inky = ink_action_new( "NodeBreakAction",
1322                                           _("Break nodes"),
1323                                           _("Break path at selected nodes"),
1324                                           INKSCAPE_ICON_NODE_BREAK,
1325                                           secondarySize );
1326         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1327         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1328     }
1331     {
1332         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1333                                           _("Join with segment"),
1334                                           _("Join selected endnodes with a new segment"),
1335                                           INKSCAPE_ICON_NODE_JOIN_SEGMENT,
1336                                           secondarySize );
1337         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1338         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1339     }
1341     {
1342         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1343                                           _("Delete segment"),
1344                                           _("Delete segment between two non-endpoint nodes"),
1345                                           INKSCAPE_ICON_NODE_DELETE_SEGMENT,
1346                                           secondarySize );
1347         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1348         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1349     }
1351     {
1352         InkAction* inky = ink_action_new( "NodeCuspAction",
1353                                           _("Node Cusp"),
1354                                           _("Make selected nodes corner"),
1355                                           INKSCAPE_ICON_NODE_TYPE_CUSP,
1356                                           secondarySize );
1357         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1358         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1359     }
1361     {
1362         InkAction* inky = ink_action_new( "NodeSmoothAction",
1363                                           _("Node Smooth"),
1364                                           _("Make selected nodes smooth"),
1365                                           INKSCAPE_ICON_NODE_TYPE_SMOOTH,
1366                                           secondarySize );
1367         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1368         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1369     }
1371     {
1372         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1373                                           _("Node Symmetric"),
1374                                           _("Make selected nodes symmetric"),
1375                                           INKSCAPE_ICON_NODE_TYPE_SYMMETRIC,
1376                                           secondarySize );
1377         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1378         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1379     }
1381     {
1382         InkAction* inky = ink_action_new( "NodeAutoAction",
1383                                           _("Node Auto"),
1384                                           _("Make selected nodes auto-smooth"),
1385                                           INKSCAPE_ICON_NODE_TYPE_AUTO_SMOOTH,
1386                                           secondarySize );
1387         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_auto), 0 );
1388         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1389     }
1391     {
1392         InkAction* inky = ink_action_new( "NodeLineAction",
1393                                           _("Node Line"),
1394                                           _("Make selected segments lines"),
1395                                           INKSCAPE_ICON_NODE_SEGMENT_LINE,
1396                                           secondarySize );
1397         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1398         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1399     }
1401     {
1402         InkAction* inky = ink_action_new( "NodeCurveAction",
1403                                           _("Node Curve"),
1404                                           _("Make selected segments curves"),
1405                                           INKSCAPE_ICON_NODE_SEGMENT_CURVE,
1406                                           secondarySize );
1407         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1408         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1409     }
1411     {
1412         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1413                                                       _("Show Handles"),
1414                                                       _("Show the Bezier handles of selected nodes"),
1415                                                       INKSCAPE_ICON_SHOW_NODE_HANDLES,
1416                                                       Inkscape::ICON_SIZE_DECORATION );
1417         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1418         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1419         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_handles", true) );
1420     }
1422     {
1423         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1424                                                       _("Show Outline"),
1425                                                       _("Show the outline of the path"),
1426                                                       INKSCAPE_ICON_SHOW_PATH_OUTLINE,
1427                                                       Inkscape::ICON_SIZE_DECORATION );
1428         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1429         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1430         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_helperpath", false) );
1431     }
1433     {
1434         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1435                                           _("Next path effect parameter"),
1436                                           _("Show next path effect parameter for editing"),
1437                                           INKSCAPE_ICON_PATH_EFFECT_PARAMETER_NEXT,
1438                                           Inkscape::ICON_SIZE_DECORATION );
1439         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1440         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1441         g_object_set_data( holder, "nodes_lpeedit", inky);
1442     }
1444     {
1445         InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1446                                           _("Edit clipping path"),
1447                                           _("Edit the clipping path of the object"),
1448                                           INKSCAPE_ICON_PATH_CLIP_EDIT,
1449                                           Inkscape::ICON_SIZE_DECORATION );
1450         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1451         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1452         g_object_set_data( holder, "nodes_clippathedit", inky);
1453     }
1455     {
1456         InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1457                                           _("Edit mask path"),
1458                                           _("Edit the mask of the object"),
1459                                           INKSCAPE_ICON_PATH_MASK_EDIT,
1460                                           Inkscape::ICON_SIZE_DECORATION );
1461         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1462         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1463         g_object_set_data( holder, "nodes_maskedit", inky);
1464     }
1466     /* X coord of selected node(s) */
1467     {
1468         EgeAdjustmentAction* eact = 0;
1469         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1470         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1471         eact = create_adjustment_action( "NodeXAction",
1472                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1473                                          "/tools/nodes/Xcoord", 0,
1474                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1475                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1476                                          labels, values, G_N_ELEMENTS(labels),
1477                                          sp_node_path_x_value_changed );
1478         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1479         g_object_set_data( holder, "nodes_x_action", eact );
1480         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1481         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1482     }
1484     /* Y coord of selected node(s) */
1485     {
1486         EgeAdjustmentAction* eact = 0;
1487         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1488         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1489         eact = create_adjustment_action( "NodeYAction",
1490                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1491                                          "/tools/nodes/Ycoord", 0,
1492                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1493                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1494                                          labels, values, G_N_ELEMENTS(labels),
1495                                          sp_node_path_y_value_changed );
1496         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1497         g_object_set_data( holder, "nodes_y_action", eact );
1498         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1499         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1500     }
1502     // add the units menu
1503     {
1504         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1505         gtk_action_group_add_action( mainActions, act );
1506     }
1509     sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1511     //watch selection
1512     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1514     sigc::connection *c_selection_changed =
1515         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1516                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1517     pool->add_connection ("selection-changed", c_selection_changed);
1519     sigc::connection *c_selection_modified =
1520         new sigc::connection (sp_desktop_selection (desktop)->connectModified
1521                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1522     pool->add_connection ("selection-modified", c_selection_modified);
1524     sigc::connection *c_subselection_changed =
1525         new sigc::connection (desktop->connectToolSubselectionChanged
1526                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1527     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1529     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1531     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1532 } // end of sp_node_toolbox_prep()
1535 //########################
1536 //##    Zoom Toolbox    ##
1537 //########################
1539 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1541     // no custom GtkAction setup needed
1542 } // end of sp_zoom_toolbox_prep()
1544 void
1545 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1547     toolbox_set_desktop(toolbox,
1548                         desktop,
1549                         setup_tool_toolbox,
1550                         update_tool_toolbox,
1551                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1552                                                                          "event_context_connection")));
1556 void
1557 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1559     toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1560                         desktop,
1561                         setup_aux_toolbox,
1562                         update_aux_toolbox,
1563                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1564                                                                          "event_context_connection")));
1567 void
1568 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1570     toolbox_set_desktop(toolbox,
1571                         desktop,
1572                         setup_commands_toolbox,
1573                         update_commands_toolbox,
1574                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1575                                                                          "event_context_connection")));
1578 void
1579 sp_snap_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1581     toolbox_set_desktop(toolbox,
1582                         desktop,
1583                         setup_snap_toolbox,
1584                         update_snap_toolbox,
1585                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1586                                                                          "event_context_connection")));
1590 static void
1591 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1593     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1594     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1596     if (old_desktop) {
1597         GList *children, *iter;
1599         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1600         for ( iter = children ; iter ; iter = iter->next ) {
1601             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1602         }
1603         g_list_free(children);
1604     }
1606     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1608     if (desktop) {
1609         gtk_widget_set_sensitive(toolbox, TRUE);
1610         setup_func(toolbox, desktop);
1611         update_func(desktop, desktop->event_context, toolbox);
1612         *conn = desktop->connectEventContextChanged
1613             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1614     } else {
1615         gtk_widget_set_sensitive(toolbox, FALSE);
1616     }
1618 } // end of toolbox_set_desktop()
1621 static void
1622 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1624     gchar const * descr =
1625         "<ui>"
1626         "  <toolbar name='ToolToolbar'>"
1627         "    <toolitem action='ToolSelector' />"
1628         "    <toolitem action='ToolNode' />"
1629         "    <toolitem action='ToolTweak' />"
1630         "    <toolitem action='ToolZoom' />"
1631         "    <toolitem action='ToolRect' />"
1632         "    <toolitem action='Tool3DBox' />"
1633         "    <toolitem action='ToolArc' />"
1634         "    <toolitem action='ToolStar' />"
1635         "    <toolitem action='ToolSpiral' />"
1636         "    <toolitem action='ToolPencil' />"
1637         "    <toolitem action='ToolPen' />"
1638         "    <toolitem action='ToolCalligraphic' />"
1639         "    <toolitem action='ToolEraser' />"
1640 //        "    <toolitem action='ToolLPETool' />"
1641         "    <toolitem action='ToolPaintBucket' />"
1642         "    <toolitem action='ToolText' />"
1643         "    <toolitem action='ToolConnector' />"
1644         "    <toolitem action='ToolGradient' />"
1645         "    <toolitem action='ToolDropper' />"
1646         "  </toolbar>"
1647         "</ui>";
1648     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1649     GtkUIManager* mgr = gtk_ui_manager_new();
1650     GError* errVal = 0;
1651     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1653     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1654     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1656     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" );
1657     if ( prefs->getBool("/toolbox/icononly", true) ) {
1658         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1659     }
1660     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
1661     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1663     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
1664     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1666     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1668     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1669     if ( child ) {
1670         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1671     }
1673     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1674 //     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
1678 static void
1679 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1681     gchar const *const tname = ( eventcontext
1682                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1683                                  : NULL );
1684     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1686     for (int i = 0 ; tools[i].type_name ; i++ ) {
1687         Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1688         if ( act ) {
1689             bool setActive = tname && !strcmp(tname, tools[i].type_name);
1690             Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1691             if ( verbAct ) {
1692                 verbAct->set_active(setActive);
1693             }
1694         }
1695     }
1698 static void
1699 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1701     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1702     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1703     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1704     GtkUIManager* mgr = gtk_ui_manager_new();
1705     GError* errVal = 0;
1706     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1707     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1709     std::map<std::string, GtkWidget*> dataHolders;
1711     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1712         if ( aux_toolboxes[i].prep_func ) {
1713             // converted to GtkActions and UIManager
1715             GtkWidget* kludge = gtk_toolbar_new();
1716             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1717             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1718             dataHolders[aux_toolboxes[i].type_name] = kludge;
1719             aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1720         } else {
1722             GtkWidget *sub_toolbox = 0;
1723             if (aux_toolboxes[i].create_func == NULL)
1724                 sub_toolbox = sp_empty_toolbox_new(desktop);
1725             else {
1726                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1727             }
1729             gtk_size_group_add_widget( grouper, sub_toolbox );
1731             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1732             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1734         }
1735     }
1737     // Second pass to create toolbars *after* all GtkActions are created
1738     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1739         if ( aux_toolboxes[i].prep_func ) {
1740             // converted to GtkActions and UIManager
1742             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1744             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1745             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1747             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1748             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1749             g_free( tmp );
1750             tmp = 0;
1752             if ( prefs->getBool( "/toolbox/icononly", true) ) {
1753                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1754             }
1756             Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
1757             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1759             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1761             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1762                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1763                 swatch->setDesktop( desktop );
1764                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1765                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1766                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1767                 gtk_table_attach( GTK_TABLE(holder), swatch_, 1, 2, 0, 1, (GtkAttachOptions)(GTK_SHRINK | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), AUX_BETWEEN_BUTTON_GROUPS, 0 );
1768             }
1770             gtk_widget_show_all( holder );
1771             sp_set_font_size_smaller( holder );
1773             gtk_size_group_add_widget( grouper, holder );
1775             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1776             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1777         }
1778     }
1780     g_object_unref( G_OBJECT(grouper) );
1783 static void
1784 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1786     gchar const *tname = ( eventcontext
1787                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1788                            : NULL );
1789     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1790         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1791         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1792             gtk_widget_show_all(sub_toolbox);
1793             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1794         } else {
1795             gtk_widget_hide(sub_toolbox);
1796         }
1797     }
1800 static void
1801 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1803     gchar const * descr =
1804         "<ui>"
1805         "  <toolbar name='CommandsToolbar'>"
1806         "    <toolitem action='FileNew' />"
1807         "    <toolitem action='FileOpen' />"
1808         "    <toolitem action='FileSave' />"
1809         "    <toolitem action='FilePrint' />"
1810         "    <separator />"
1811         "    <toolitem action='FileImport' />"
1812         "    <toolitem action='FileExport' />"
1813         "    <separator />"
1814         "    <toolitem action='EditUndo' />"
1815         "    <toolitem action='EditRedo' />"
1816         "    <separator />"
1817         "    <toolitem action='EditCopy' />"
1818         "    <toolitem action='EditCut' />"
1819         "    <toolitem action='EditPaste' />"
1820         "    <separator />"
1821         "    <toolitem action='ZoomSelection' />"
1822         "    <toolitem action='ZoomDrawing' />"
1823         "    <toolitem action='ZoomPage' />"
1824         "    <separator />"
1825         "    <toolitem action='EditDuplicate' />"
1826         "    <toolitem action='EditClone' />"
1827         "    <toolitem action='EditUnlinkClone' />"
1828         "    <separator />"
1829         "    <toolitem action='SelectionGroup' />"
1830         "    <toolitem action='SelectionUnGroup' />"
1831         "    <separator />"
1832         "    <toolitem action='DialogFillStroke' />"
1833         "    <toolitem action='DialogText' />"
1834         "    <toolitem action='DialogLayers' />"
1835         "    <toolitem action='DialogXMLEditor' />"
1836         "    <toolitem action='DialogAlignDistribute' />"
1837         "    <separator />"
1838         "    <toolitem action='DialogPreferences' />"
1839         "    <toolitem action='DialogDocumentProperties' />"
1840         "  </toolbar>"
1841         "</ui>";
1842     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1843     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1845     GtkUIManager* mgr = gtk_ui_manager_new();
1846     GError* errVal = 0;
1848     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1849     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1851     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1852     if ( prefs->getBool("/toolbox/icononly", true) ) {
1853         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1854     }
1856     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
1857     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1859     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1860     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1863     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1865     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1866     if ( child ) {
1867         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1868     }
1870     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1873 static void
1874 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1878 void toggle_snap_callback (GtkToggleAction *act, gpointer data) { //data points to the toolbox
1880         if (g_object_get_data(G_OBJECT(data), "freeze" )) {
1881                 return;
1882         }
1884         gpointer ptr = g_object_get_data(G_OBJECT(data), "desktop");
1885         g_assert(ptr != NULL);
1887         SPDesktop *dt = reinterpret_cast<SPDesktop*>(ptr);
1888         SPNamedView *nv = sp_desktop_namedview(dt);
1889         SPDocument *doc = SP_OBJECT_DOCUMENT(nv);
1891         if (dt == NULL || nv == NULL) {
1892                 g_warning("No desktop or namedview specified (in toggle_snap_callback)!");
1893                 return;
1894         }
1896         Inkscape::XML::Node *repr = SP_OBJECT_REPR(nv);
1898         if (repr == NULL) {
1899                 g_warning("This namedview doesn't have a xml representation attached!");
1900                 return;
1901         }
1903         bool saved = sp_document_get_undo_sensitive(doc);
1904         sp_document_set_undo_sensitive(doc, false);
1906         bool v = false;
1907         SPAttributeEnum attr = (SPAttributeEnum) GPOINTER_TO_INT(g_object_get_data(G_OBJECT(act), "SP_ATTR_INKSCAPE"));
1909         switch (attr) {
1910         case SP_ATTR_INKSCAPE_SNAP_GLOBAL:
1911                 dt->toggleSnapGlobal();
1912                 break;
1913         case SP_ATTR_INKSCAPE_SNAP_BBOX:
1914                 v = nv->snap_manager.snapprefs.getSnapModeBBox();
1915                 sp_repr_set_boolean(repr, "inkscape:snap-bbox", !v);
1916                 break;
1917         case SP_ATTR_INKSCAPE_BBOX_PATHS:
1918                 v = nv->snap_manager.snapprefs.getSnapToBBoxPath();
1919                 sp_repr_set_boolean(repr, "inkscape:bbox-paths", !v);
1920                 break;
1921         case SP_ATTR_INKSCAPE_BBOX_NODES:
1922                 v = nv->snap_manager.snapprefs.getSnapToBBoxNode();
1923                 sp_repr_set_boolean(repr, "inkscape:bbox-nodes", !v);
1924                 break;
1925         case SP_ATTR_INKSCAPE_SNAP_NODES:
1926                 v = nv->snap_manager.snapprefs.getSnapModeNode();
1927                 sp_repr_set_boolean(repr, "inkscape:snap-nodes", !v);
1928                 break;
1929         case SP_ATTR_INKSCAPE_OBJECT_PATHS:
1930                 v = nv->snap_manager.snapprefs.getSnapToItemPath();
1931                 sp_repr_set_boolean(repr, "inkscape:object-paths", !v);
1932                 break;
1933         case SP_ATTR_INKSCAPE_OBJECT_NODES:
1934                 v = nv->snap_manager.snapprefs.getSnapToItemNode();
1935                 sp_repr_set_boolean(repr, "inkscape:object-nodes", !v);
1936                 break;
1937         case SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES:
1938                 v = nv->snap_manager.snapprefs.getSnapSmoothNodes();
1939                 sp_repr_set_boolean(repr, "inkscape:snap-smooth-nodes", !v);
1940                 break;
1941         case SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS:
1942                 v = nv->snap_manager.snapprefs.getSnapIntersectionCS();
1943                 sp_repr_set_boolean(repr, "inkscape:snap-intersection-paths", !v);
1944                 break;
1945         case SP_ATTR_INKSCAPE_SNAP_CENTER:
1946                 v = nv->snap_manager.snapprefs.getIncludeItemCenter();
1947                 sp_repr_set_boolean(repr, "inkscape:snap-center", !v);
1948                 break;
1949         case SP_ATTR_INKSCAPE_SNAP_GRIDS:
1950                 v = nv->snap_manager.snapprefs.getSnapToGrids();
1951                 sp_repr_set_boolean(repr, "inkscape:snap-grids", !v);
1952                 break;
1953         case SP_ATTR_INKSCAPE_SNAP_TO_GUIDES:
1954                 v = nv->snap_manager.snapprefs.getSnapToGuides();
1955                 sp_repr_set_boolean(repr, "inkscape:snap-to-guides", !v);
1956                 break;
1957         case SP_ATTR_INKSCAPE_SNAP_PAGE:
1958                 v = nv->snap_manager.snapprefs.getSnapToPageBorder();
1959                 sp_repr_set_boolean(repr, "inkscape:snap-page", !v);
1960                 break;
1961         /*case SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE:
1962                 v = nv->snap_manager.snapprefs.getSnapIntersectionGG();
1963                 sp_repr_set_boolean(repr, "inkscape:snap-intersection-grid-guide", !v);
1964                 break;*/
1965         case SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS:
1966                 v = nv->snap_manager.snapprefs.getSnapLineMidpoints();
1967                 sp_repr_set_boolean(repr, "inkscape:snap-midpoints", !v);
1968                 break;
1969         case SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS:
1970                 v = nv->snap_manager.snapprefs.getSnapObjectMidpoints();
1971                 sp_repr_set_boolean(repr, "inkscape:snap-object-midpoints", !v);
1972                 break;
1973         case SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS:
1974                 v = nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints();
1975                 sp_repr_set_boolean(repr, "inkscape:snap-bbox-edge-midpoints", !v);
1976                 break;
1977         case SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS:
1978                 v = nv->snap_manager.snapprefs.getSnapBBoxMidpoints();
1979                 sp_repr_set_boolean(repr, "inkscape:snap-bbox-midpoints", !v);
1980                 break;
1981         default:
1982                 g_warning("toggle_snap_callback has been called with an ID for which no action has been defined");
1983                 break;
1984         }
1986         // The snapping preferences are stored in the document, and therefore toggling makes the document dirty
1987         doc->setModifiedSinceSave();
1989         sp_document_set_undo_sensitive(doc, saved);
1992 void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1994         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1995         Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
1997         gchar const * descr =
1998                 "<ui>"
1999                 "  <toolbar name='SnapToolbar'>"
2000                 "    <toolitem action='ToggleSnapGlobal' />"
2001                         "    <separator />"
2002                         "    <toolitem action='ToggleSnapFromBBoxCorner' />"
2003                         "    <toolitem action='ToggleSnapToBBoxPath' />"
2004                         "    <toolitem action='ToggleSnapToBBoxNode' />"
2005                         "    <toolitem action='ToggleSnapToFromBBoxEdgeMidpoints' />"
2006                         "    <toolitem action='ToggleSnapToFromBBoxCenters' />"
2007                         "    <separator />"
2008                         "    <toolitem action='ToggleSnapFromNode' />"
2009                         "    <toolitem action='ToggleSnapToItemPath' />"
2010                         "    <toolitem action='ToggleSnapToPathIntersections' />"
2011                         "    <toolitem action='ToggleSnapToItemNode' />"
2012                         "    <toolitem action='ToggleSnapToSmoothNodes' />"
2013                         "    <toolitem action='ToggleSnapToFromLineMidpoints' />"
2014                         "    <toolitem action='ToggleSnapToFromObjectCenters' />"
2015                         "    <toolitem action='ToggleSnapToFromRotationCenter' />"
2016                         "    <separator />"
2017                         "    <toolitem action='ToggleSnapToPageBorder' />"
2018                         "    <toolitem action='ToggleSnapToGrids' />"
2019                         "    <toolitem action='ToggleSnapToGuides' />"
2020                   //"    <toolitem action='ToggleSnapToGridGuideIntersections' />"
2021                         "  </toolbar>"
2022                 "</ui>";
2024         Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
2026         {
2027                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapGlobal",
2028                     _("Snap"), _("Enable snapping"), INKSCAPE_ICON_SNAP, secondarySize,
2029                     SP_ATTR_INKSCAPE_SNAP_GLOBAL);
2031                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2032                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2033         }
2035         {
2036                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromBBoxCorner",
2037                     _("Bounding box"), _("Snap bounding box corners"), INKSCAPE_ICON_SNAP_BOUNDING_BOX,
2038                     secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX);
2040                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2041                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2042         }
2044         {
2045                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxPath",
2046                     _("Bounding box edges"), _("Snap to edges of a bounding box"),
2047                     INKSCAPE_ICON_SNAP_BOUNDING_BOX_EDGES, secondarySize, SP_ATTR_INKSCAPE_BBOX_PATHS);
2049                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2050                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2051         }
2053         {
2054                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxNode",
2055                     _("Bounding box corners"), _("Snap to bounding box corners"),
2056                     INKSCAPE_ICON_SNAP_BOUNDING_BOX_CORNERS, secondarySize, SP_ATTR_INKSCAPE_BBOX_NODES);
2058                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2059                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2060         }
2062         {
2063                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxEdgeMidpoints",
2064                     _("BBox Edge Midpoints"), _("Snap from and to midpoints of bounding box edges"),
2065                     INKSCAPE_ICON_SNAP_BOUNDING_BOX_MIDPOINTS, secondarySize,
2066                     SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS);
2068                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2069                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2070         }
2072         {
2073                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxCenters",
2074                     _("BBox Centers"), _("Snapping from and to centers of bounding boxes"),
2075                     INKSCAPE_ICON_SNAP_BOUNDING_BOX_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS);
2077                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2078                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2079         }
2081         {
2082                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromNode",
2083                     _("Nodes"), _("Snap nodes or handles"), INKSCAPE_ICON_SNAP_NODES, secondarySize, SP_ATTR_INKSCAPE_SNAP_NODES);
2085                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2086                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2087         }
2089         {
2090                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemPath",
2091                     _("Paths"), _("Snap to paths"), INKSCAPE_ICON_SNAP_NODES_PATH, secondarySize,
2092                     SP_ATTR_INKSCAPE_OBJECT_PATHS);
2094                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2095                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2096         }
2098         {
2099                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPathIntersections",
2100                     _("Path intersections"), _("Snap to path intersections"),
2101                     INKSCAPE_ICON_SNAP_NODES_INTERSECTION, secondarySize, SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS);
2103                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2104                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2105         }
2107         {
2108                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemNode",
2109                     _("To nodes"), _("Snap to cusp nodes"), INKSCAPE_ICON_SNAP_NODES_CUSP, secondarySize,
2110                     SP_ATTR_INKSCAPE_OBJECT_NODES);
2112                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2113                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2114         }
2116         {
2117                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToSmoothNodes",
2118                     _("Smooth nodes"), _("Snap to smooth nodes"), INKSCAPE_ICON_SNAP_NODES_SMOOTH,
2119                     secondarySize, SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES);
2121                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2122                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2123         }
2125         {
2126                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromLineMidpoints",
2127                     _("Line Midpoints"), _("Snap from and to midpoints of line segments"),
2128                     INKSCAPE_ICON_SNAP_NODES_MIDPOINT, secondarySize, SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS);
2130                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2131                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2132         }
2134         {
2135                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromObjectCenters",
2136                     _("Object Centers"), _("Snap from and to centers of objects"),
2137                     INKSCAPE_ICON_SNAP_NODES_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS);
2139                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2140                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2141         }
2143         {
2144                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromRotationCenter",
2145                     _("Rotation Centers"), _("Snap from and to an item's rotation center"),
2146                     INKSCAPE_ICON_SNAP_NODES_ROTATION_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_CENTER);
2148                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2149                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2150         }
2152         {
2153                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPageBorder",
2154                     _("Page border"), _("Snap to the page border"), INKSCAPE_ICON_SNAP_PAGE,
2155                     secondarySize, SP_ATTR_INKSCAPE_SNAP_PAGE);
2157                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2158                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2159         }
2161         {
2162                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGrids",
2163                     _("Grids"), _("Snap to grids"), INKSCAPE_ICON_GRID_RECTANGULAR, secondarySize,
2164                     SP_ATTR_INKSCAPE_SNAP_GRIDS);
2166                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2167                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2168         }
2170         {
2171                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGuides",
2172                     _("Guides"), _("Snap to guides"), INKSCAPE_ICON_GUIDES, secondarySize,
2173                     SP_ATTR_INKSCAPE_SNAP_TO_GUIDES);
2175                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2176                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2177         }
2179         /*{
2180                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGridGuideIntersections",
2181                     _("Grid/guide intersections"), _("Snap to intersections of a grid with a guide"),
2182                     INKSCAPE_ICON_SNAP_GRID_GUIDE_INTERSECTIONS, secondarySize,
2183                     SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE);
2185                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2186                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2187         }*/
2189     GtkUIManager* mgr = gtk_ui_manager_new();
2190     GError* errVal = 0;
2192     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
2193     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
2195     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/SnapToolbar" );
2196     if ( prefs->getBool("/toolbox/icononly", true) ) {
2197         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
2198     }
2200     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/secondary");
2201     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
2203     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
2204     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
2206     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
2208     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
2209     if ( child ) {
2210         gtk_container_remove( GTK_CONTAINER(toolbox), child );
2211     }
2213     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
2217 void update_snap_toolbox(SPDesktop *desktop, SPEventContext */*eventcontext*/, GtkWidget *toolbox)
2219         g_assert(desktop != NULL);
2220         g_assert(toolbox != NULL);
2222         SPNamedView *nv = sp_desktop_namedview(desktop);
2223         if (nv == NULL) {
2224                 g_warning("Namedview cannot be retrieved (in update_snap_toolbox)!");
2225                 return;
2226         }
2228         Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2230         Glib::RefPtr<Gtk::Action> act1 = mainActions->get_action("ToggleSnapGlobal");
2231         Glib::RefPtr<Gtk::Action> act2 = mainActions->get_action("ToggleSnapFromBBoxCorner");
2232         Glib::RefPtr<Gtk::Action> act3 = mainActions->get_action("ToggleSnapToBBoxPath");
2233         Glib::RefPtr<Gtk::Action> act4 = mainActions->get_action("ToggleSnapToBBoxNode");
2234         Glib::RefPtr<Gtk::Action> act4b = mainActions->get_action("ToggleSnapToFromBBoxEdgeMidpoints");
2235         Glib::RefPtr<Gtk::Action> act4c = mainActions->get_action("ToggleSnapToFromBBoxCenters");
2236         Glib::RefPtr<Gtk::Action> act5 = mainActions->get_action("ToggleSnapFromNode");
2237         Glib::RefPtr<Gtk::Action> act6 = mainActions->get_action("ToggleSnapToItemPath");
2238         Glib::RefPtr<Gtk::Action> act6b = mainActions->get_action("ToggleSnapToPathIntersections");
2239         Glib::RefPtr<Gtk::Action> act7 = mainActions->get_action("ToggleSnapToItemNode");
2240         Glib::RefPtr<Gtk::Action> act8 = mainActions->get_action("ToggleSnapToSmoothNodes");
2241         Glib::RefPtr<Gtk::Action> act9 = mainActions->get_action("ToggleSnapToFromLineMidpoints");
2242         Glib::RefPtr<Gtk::Action> act10 = mainActions->get_action("ToggleSnapToFromObjectCenters");
2243         Glib::RefPtr<Gtk::Action> act11 = mainActions->get_action("ToggleSnapToFromRotationCenter");
2244         Glib::RefPtr<Gtk::Action> act12 = mainActions->get_action("ToggleSnapToPageBorder");
2245         //Glib::RefPtr<Gtk::Action> act13 = mainActions->get_action("ToggleSnapToGridGuideIntersections");
2246         Glib::RefPtr<Gtk::Action> act14 = mainActions->get_action("ToggleSnapToGrids");
2247         Glib::RefPtr<Gtk::Action> act15 = mainActions->get_action("ToggleSnapToGuides");
2250         if (!act1) {
2251                 return; // The snap actions haven't been defined yet (might be the case during startup)
2252         }
2254         // The ..._set_active calls below will toggle the buttons, but this shouldn't lead to
2255         // changes in our document because we're only updating the UI;
2256         // Setting the "freeze" parameter to true will block the code in toggle_snap_callback()
2257         g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(TRUE));
2259         bool const c1 = nv->snap_manager.snapprefs.getSnapEnabledGlobally();
2260         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act1->gobj()), c1);
2262         bool const c2 = nv->snap_manager.snapprefs.getSnapModeBBox();
2263         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act2->gobj()), c2);
2264         gtk_action_set_sensitive(GTK_ACTION(act2->gobj()), c1);
2266         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act3->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxPath());
2267         gtk_action_set_sensitive(GTK_ACTION(act3->gobj()), c1 && c2);
2268         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxNode());
2269         gtk_action_set_sensitive(GTK_ACTION(act4->gobj()), c1 && c2);
2270         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4b->gobj()), nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints());
2271         gtk_action_set_sensitive(GTK_ACTION(act4b->gobj()), c1 && c2);
2272         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4c->gobj()), nv->snap_manager.snapprefs.getSnapBBoxMidpoints());
2273         gtk_action_set_sensitive(GTK_ACTION(act4c->gobj()), c1 && c2);
2275         bool const c3 = nv->snap_manager.snapprefs.getSnapModeNode();
2276         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act5->gobj()), c3);
2277         gtk_action_set_sensitive(GTK_ACTION(act5->gobj()), c1);
2279         bool const c4 = nv->snap_manager.snapprefs.getSnapToItemPath();
2280         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6->gobj()), c4);
2281         gtk_action_set_sensitive(GTK_ACTION(act6->gobj()), c1 && c3);
2282         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6b->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionCS());
2283         gtk_action_set_sensitive(GTK_ACTION(act6b->gobj()), c1 && c3 && c4);
2284         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act7->gobj()), nv->snap_manager.snapprefs.getSnapToItemNode());
2285         gtk_action_set_sensitive(GTK_ACTION(act7->gobj()), c1 && c3);
2286         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act8->gobj()), nv->snap_manager.snapprefs.getSnapSmoothNodes());
2287         gtk_action_set_sensitive(GTK_ACTION(act8->gobj()), c1 && c3);
2288         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act9->gobj()), nv->snap_manager.snapprefs.getSnapLineMidpoints());
2289         gtk_action_set_sensitive(GTK_ACTION(act9->gobj()), c1 && c3);
2290         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act10->gobj()), nv->snap_manager.snapprefs.getSnapObjectMidpoints());
2291         gtk_action_set_sensitive(GTK_ACTION(act10->gobj()), c1 && c3);
2292         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act11->gobj()), nv->snap_manager.snapprefs.getIncludeItemCenter());
2293         gtk_action_set_sensitive(GTK_ACTION(act11->gobj()), c1 && c3);
2295         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act12->gobj()), nv->snap_manager.snapprefs.getSnapToPageBorder());
2296         gtk_action_set_sensitive(GTK_ACTION(act12->gobj()), c1);
2297         //gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act13->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionGG());
2298         //gtk_action_set_sensitive(GTK_ACTION(act13->gobj()), c1);
2300         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act14->gobj()), nv->snap_manager.snapprefs.getSnapToGrids());
2301         gtk_action_set_sensitive(GTK_ACTION(act14->gobj()), c1);
2302         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act15->gobj()), nv->snap_manager.snapprefs.getSnapToGuides());
2303         gtk_action_set_sensitive(GTK_ACTION(act15->gobj()), c1);
2306         g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(FALSE)); // unfreeze (see above)
2309 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
2311     gtk_widget_show(toolbox_toplevel);
2312     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
2314     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
2315     if (!shown_toolbox) {
2316         return;
2317     }
2318     gtk_widget_show(toolbox);
2320     gtk_widget_show_all(shown_toolbox);
2323 static GtkWidget *
2324 sp_empty_toolbox_new(SPDesktop *desktop)
2326     GtkWidget *tbl = gtk_toolbar_new();
2327     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
2328     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
2330     gtk_widget_show_all(tbl);
2331     sp_set_font_size_smaller (tbl);
2333     return tbl;
2336 #define MODE_LABEL_WIDTH 70
2338 //########################
2339 //##       Star         ##
2340 //########################
2342 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2344     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2346     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2347         // do not remember prefs if this call is initiated by an undo change, because undoing object
2348         // creation sets bogus values to its attributes before it is deleted
2349         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2350         prefs->setInt("/tools/shapes/star/magnitude", (gint)adj->value);
2351     }
2353     // quit if run by the attr_changed listener
2354     if (g_object_get_data( dataKludge, "freeze" )) {
2355         return;
2356     }
2358     // in turn, prevent listener from responding
2359     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2361     bool modmade = false;
2363     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2364     GSList const *items = selection->itemList();
2365     for (; items != NULL; items = items->next) {
2366         if (SP_IS_STAR((SPItem *) items->data)) {
2367             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2368             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
2369             sp_repr_set_svg_double(repr, "sodipodi:arg2",
2370                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
2371                                     + M_PI / (gint)adj->value));
2372             SP_OBJECT((SPItem *) items->data)->updateRepr();
2373             modmade = true;
2374         }
2375     }
2376     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2377                                    _("Star: Change number of corners"));
2379     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2382 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2384     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2386     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2387         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2388         prefs->setDouble("/tools/shapes/star/proportion", adj->value);
2389     }
2391     // quit if run by the attr_changed listener
2392     if (g_object_get_data( dataKludge, "freeze" )) {
2393         return;
2394     }
2396     // in turn, prevent listener from responding
2397     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2399     bool modmade = false;
2400     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2401     GSList const *items = selection->itemList();
2402     for (; items != NULL; items = items->next) {
2403         if (SP_IS_STAR((SPItem *) items->data)) {
2404             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2406             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2407             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2408             if (r2 < r1) {
2409                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
2410             } else {
2411                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
2412             }
2414             SP_OBJECT((SPItem *) items->data)->updateRepr();
2415             modmade = true;
2416         }
2417     }
2419     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2420                                    _("Star: Change spoke ratio"));
2422     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2425 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
2427     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2428     bool flat = ege_select_one_action_get_active( act ) == 0;
2430     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2431         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2432         prefs->setBool( "/tools/shapes/star/isflatsided", flat);
2433     }
2435     // quit if run by the attr_changed listener
2436     if (g_object_get_data( dataKludge, "freeze" )) {
2437         return;
2438     }
2440     // in turn, prevent listener from responding
2441     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2443     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2444     GSList const *items = selection->itemList();
2445     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2446     bool modmade = false;
2448     if ( prop_action ) {
2449         gtk_action_set_sensitive( prop_action, !flat );
2450     }
2452     for (; items != NULL; items = items->next) {
2453         if (SP_IS_STAR((SPItem *) items->data)) {
2454             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2455             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
2456             SP_OBJECT((SPItem *) items->data)->updateRepr();
2457             modmade = true;
2458         }
2459     }
2461     if (modmade) {
2462         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2463                          flat ? _("Make polygon") : _("Make star"));
2464     }
2466     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2469 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2471     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2473     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2474         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2475         prefs->setDouble("/tools/shapes/star/rounded", (gdouble) adj->value);
2476     }
2478     // quit if run by the attr_changed listener
2479     if (g_object_get_data( dataKludge, "freeze" )) {
2480         return;
2481     }
2483     // in turn, prevent listener from responding
2484     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2486     bool modmade = false;
2488     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2489     GSList const *items = selection->itemList();
2490     for (; items != NULL; items = items->next) {
2491         if (SP_IS_STAR((SPItem *) items->data)) {
2492             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2493             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2494             SP_OBJECT(items->data)->updateRepr();
2495             modmade = true;
2496         }
2497     }
2498     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2499                                    _("Star: Change rounding"));
2501     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2504 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2506     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2508     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2509         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2510         prefs->setDouble("/tools/shapes/star/randomized", (gdouble) adj->value);
2511     }
2513     // quit if run by the attr_changed listener
2514     if (g_object_get_data( dataKludge, "freeze" )) {
2515         return;
2516     }
2518     // in turn, prevent listener from responding
2519     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2521     bool modmade = false;
2523     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2524     GSList const *items = selection->itemList();
2525     for (; items != NULL; items = items->next) {
2526         if (SP_IS_STAR((SPItem *) items->data)) {
2527             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2528             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2529             SP_OBJECT(items->data)->updateRepr();
2530             modmade = true;
2531         }
2532     }
2533     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2534                                    _("Star: Change randomization"));
2536     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2540 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2541                                        gchar const */*old_value*/, gchar const */*new_value*/,
2542                                        bool /*is_interactive*/, gpointer data)
2544     GtkWidget *tbl = GTK_WIDGET(data);
2546     // quit if run by the _changed callbacks
2547     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2548         return;
2549     }
2551     // in turn, prevent callbacks from responding
2552     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2554     GtkAdjustment *adj = 0;
2556     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2557     bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2559     if (!strcmp(name, "inkscape:randomized")) {
2560         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2561         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2562     } else if (!strcmp(name, "inkscape:rounded")) {
2563         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2564         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2565     } else if (!strcmp(name, "inkscape:flatsided")) {
2566         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2567         char const *flatsides = repr->attribute("inkscape:flatsided");
2568         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2569         if ( flatsides && !strcmp(flatsides,"false") ) {
2570             ege_select_one_action_set_active( flat_action, 1 );
2571             gtk_action_set_sensitive( prop_action, TRUE );
2572         } else {
2573             ege_select_one_action_set_active( flat_action, 0 );
2574             gtk_action_set_sensitive( prop_action, FALSE );
2575         }
2576     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2577         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2578         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2579         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2580         if (r2 < r1) {
2581             gtk_adjustment_set_value(adj, r2/r1);
2582         } else {
2583             gtk_adjustment_set_value(adj, r1/r2);
2584         }
2585     } else if (!strcmp(name, "sodipodi:sides")) {
2586         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2587         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2588     }
2590     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2594 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2596     NULL, /* child_added */
2597     NULL, /* child_removed */
2598     star_tb_event_attr_changed,
2599     NULL, /* content_changed */
2600     NULL  /* order_changed */
2601 };
2604 /**
2605  *  \param selection Should not be NULL.
2606  */
2607 static void
2608 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2610     int n_selected = 0;
2611     Inkscape::XML::Node *repr = NULL;
2613     purge_repr_listener( tbl, tbl );
2615     for (GSList const *items = selection->itemList();
2616          items != NULL;
2617          items = items->next)
2618     {
2619         if (SP_IS_STAR((SPItem *) items->data)) {
2620             n_selected++;
2621             repr = SP_OBJECT_REPR((SPItem *) items->data);
2622         }
2623     }
2625     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2627     if (n_selected == 0) {
2628         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2629     } else if (n_selected == 1) {
2630         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2632         if (repr) {
2633             g_object_set_data( tbl, "repr", repr );
2634             Inkscape::GC::anchor(repr);
2635             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2636             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2637         }
2638     } else {
2639         // FIXME: implement averaging of all parameters for multiple selected stars
2640         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2641         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2642     }
2646 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2648     // FIXME: in this and all other _default functions, set some flag telling the value_changed
2649     // callbacks to lump all the changes for all selected objects in one undo step
2651     GtkAdjustment *adj = 0;
2653     // fixme: make settable in prefs!
2654     gint mag = 5;
2655     gdouble prop = 0.5;
2656     gboolean flat = FALSE;
2657     gdouble randomized = 0;
2658     gdouble rounded = 0;
2660     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2661     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2663     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2664     gtk_action_set_sensitive( sb2, !flat );
2666     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2667     gtk_adjustment_set_value(adj, mag);
2668     gtk_adjustment_value_changed(adj);
2670     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2671     gtk_adjustment_set_value(adj, prop);
2672     gtk_adjustment_value_changed(adj);
2674     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2675     gtk_adjustment_set_value(adj, rounded);
2676     gtk_adjustment_value_changed(adj);
2678     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2679     gtk_adjustment_set_value(adj, randomized);
2680     gtk_adjustment_value_changed(adj);
2684 void
2685 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2687     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2688     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2689     GtkWidget *l = gtk_label_new(NULL);
2690     gtk_label_set_markup(GTK_LABEL(l), title);
2691     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2692     if ( GTK_IS_TOOLBAR(tbl) ) {
2693         gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2694     } else {
2695         gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2696     }
2697     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2701 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2703     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
2705     {
2706         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2707         ege_output_action_set_use_markup( act, TRUE );
2708         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2709         g_object_set_data( holder, "mode_action", act );
2710     }
2712     {
2713         EgeAdjustmentAction* eact = 0;
2714         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2715         bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2717         /* Flatsided checkbox */
2718         {
2719             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2721             GtkTreeIter iter;
2722             gtk_list_store_append( model, &iter );
2723             gtk_list_store_set( model, &iter,
2724                                 0, _("Polygon"),
2725                                 1, _("Regular polygon (with one handle) instead of a star"),
2726                                 2, INKSCAPE_ICON_DRAW_POLYGON,
2727                                 -1 );
2729             gtk_list_store_append( model, &iter );
2730             gtk_list_store_set( model, &iter,
2731                                 0, _("Star"),
2732                                 1, _("Star instead of a regular polygon (with one handle)"),
2733                                 2, INKSCAPE_ICON_DRAW_STAR,
2734                                 -1 );
2736             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2737             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2738             g_object_set_data( holder, "flat_action", act );
2740             ege_select_one_action_set_appearance( act, "full" );
2741             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2742             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2743             ege_select_one_action_set_icon_column( act, 2 );
2744             ege_select_one_action_set_icon_size( act, secondarySize );
2745             ege_select_one_action_set_tooltip_column( act, 1  );
2747             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2748             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2749         }
2751         /* Magnitude */
2752         {
2753         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2754         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2755         eact = create_adjustment_action( "MagnitudeAction",
2756                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2757                                          "/tools/shapes/star/magnitude", 3,
2758                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2759                                          3, 1024, 1, 5,
2760                                          labels, values, G_N_ELEMENTS(labels),
2761                                          sp_stb_magnitude_value_changed,
2762                                          1.0, 0 );
2763         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2764         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2765         }
2767         /* Spoke ratio */
2768         {
2769         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2770         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2771         eact = create_adjustment_action( "SpokeAction",
2772                                          _("Spoke ratio"), _("Spoke ratio:"),
2773                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2774                                          // Base radius is the same for the closest handle.
2775                                          _("Base radius to tip radius ratio"),
2776                                          "/tools/shapes/star/proportion", 0.5,
2777                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2778                                          0.01, 1.0, 0.01, 0.1,
2779                                          labels, values, G_N_ELEMENTS(labels),
2780                                          sp_stb_proportion_value_changed );
2781         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2782         g_object_set_data( holder, "prop_action", eact );
2783         }
2785         if ( !isFlatSided ) {
2786             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2787         } else {
2788             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2789         }
2791         /* Roundedness */
2792         {
2793         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2794         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2795         eact = create_adjustment_action( "RoundednessAction",
2796                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2797                                          "/tools/shapes/star/rounded", 0.0,
2798                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2799                                          -10.0, 10.0, 0.01, 0.1,
2800                                          labels, values, G_N_ELEMENTS(labels),
2801                                          sp_stb_rounded_value_changed );
2802         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2803         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2804         }
2806         /* Randomization */
2807         {
2808         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2809         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2810         eact = create_adjustment_action( "RandomizationAction",
2811                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2812                                          "/tools/shapes/star/randomized", 0.0,
2813                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2814                                          -10.0, 10.0, 0.001, 0.01,
2815                                          labels, values, G_N_ELEMENTS(labels),
2816                                          sp_stb_randomized_value_changed, 0.1, 3 );
2817         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2818         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2819         }
2820     }
2822     {
2823         /* Reset */
2824         {
2825             GtkAction* act = gtk_action_new( "StarResetAction",
2826                                              _("Defaults"),
2827                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2828                                              GTK_STOCK_CLEAR );
2829             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2830             gtk_action_group_add_action( mainActions, act );
2831             gtk_action_set_sensitive( act, TRUE );
2832         }
2833     }
2835     sigc::connection *connection = new sigc::connection(
2836         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2837         );
2838     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2839     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2843 //########################
2844 //##       Rect         ##
2845 //########################
2847 static void sp_rtb_sensitivize( GObject *tbl )
2849     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2850     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2851     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2853     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2854         gtk_action_set_sensitive( not_rounded, FALSE );
2855     } else {
2856         gtk_action_set_sensitive( not_rounded, TRUE );
2857     }
2861 static void
2862 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2863                           void (*setter)(SPRect *, gdouble))
2865     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2867     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2868     SPUnit const *unit = tracker->getActiveUnit();
2870     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2871         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2872         prefs->setDouble(Glib::ustring("/tools/shapes/rect/") + value_name, sp_units_get_pixels(adj->value, *unit));
2873     }
2875     // quit if run by the attr_changed listener
2876     if (g_object_get_data( tbl, "freeze" )) {
2877         return;
2878     }
2880     // in turn, prevent listener from responding
2881     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2883     bool modmade = false;
2884     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2885     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2886         if (SP_IS_RECT(items->data)) {
2887             if (adj->value != 0) {
2888                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2889             } else {
2890                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2891             }
2892             modmade = true;
2893         }
2894     }
2896     sp_rtb_sensitivize( tbl );
2898     if (modmade) {
2899         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2900                                    _("Change rectangle"));
2901     }
2903     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2906 static void
2907 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2909     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2912 static void
2913 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2915     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2918 static void
2919 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2921     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2924 static void
2925 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2927     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2932 static void
2933 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2935     GtkAdjustment *adj = 0;
2937     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2938     gtk_adjustment_set_value(adj, 0.0);
2939     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2940     gtk_adjustment_value_changed(adj);
2942     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2943     gtk_adjustment_set_value(adj, 0.0);
2944     gtk_adjustment_value_changed(adj);
2946     sp_rtb_sensitivize( obj );
2949 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2950                                        gchar const */*old_value*/, gchar const */*new_value*/,
2951                                        bool /*is_interactive*/, gpointer data)
2953     GObject *tbl = G_OBJECT(data);
2955     // quit if run by the _changed callbacks
2956     if (g_object_get_data( tbl, "freeze" )) {
2957         return;
2958     }
2960     // in turn, prevent callbacks from responding
2961     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2963     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2964     SPUnit const *unit = tracker->getActiveUnit();
2966     gpointer item = g_object_get_data( tbl, "item" );
2967     if (item && SP_IS_RECT(item)) {
2968         {
2969             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2970             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2971             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2972         }
2974         {
2975             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2976             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2977             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2978         }
2980         {
2981             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2982             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2983             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2984         }
2986         {
2987             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2988             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2989             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2990         }
2991     }
2993     sp_rtb_sensitivize( tbl );
2995     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2999 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
3000     NULL, /* child_added */
3001     NULL, /* child_removed */
3002     rect_tb_event_attr_changed,
3003     NULL, /* content_changed */
3004     NULL  /* order_changed */
3005 };
3007 /**
3008  *  \param selection should not be NULL.
3009  */
3010 static void
3011 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3013     int n_selected = 0;
3014     Inkscape::XML::Node *repr = NULL;
3015     SPItem *item = NULL;
3017     if ( g_object_get_data( tbl, "repr" ) ) {
3018         g_object_set_data( tbl, "item", NULL );
3019     }
3020     purge_repr_listener( tbl, tbl );
3022     for (GSList const *items = selection->itemList();
3023          items != NULL;
3024          items = items->next) {
3025         if (SP_IS_RECT((SPItem *) items->data)) {
3026             n_selected++;
3027             item = (SPItem *) items->data;
3028             repr = SP_OBJECT_REPR(item);
3029         }
3030     }
3032     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3034     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3036     if (n_selected == 0) {
3037         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3039         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3040         gtk_action_set_sensitive(w, FALSE);
3041         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3042         gtk_action_set_sensitive(h, FALSE);
3044     } else if (n_selected == 1) {
3045         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3046         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3048         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3049         gtk_action_set_sensitive(w, TRUE);
3050         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3051         gtk_action_set_sensitive(h, TRUE);
3053         if (repr) {
3054             g_object_set_data( tbl, "repr", repr );
3055             g_object_set_data( tbl, "item", item );
3056             Inkscape::GC::anchor(repr);
3057             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
3058             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
3059         }
3060     } else {
3061         // FIXME: implement averaging of all parameters for multiple selected
3062         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3063         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3064         sp_rtb_sensitivize( tbl );
3065     }
3069 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3071     EgeAdjustmentAction* eact = 0;
3072     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3074     {
3075         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
3076         ege_output_action_set_use_markup( act, TRUE );
3077         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3078         g_object_set_data( holder, "mode_action", act );
3079     }
3081     // rx/ry units menu: create
3082     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
3083     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
3084     // fixme: add % meaning per cent of the width/height
3085     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
3086     g_object_set_data( holder, "tracker", tracker );
3088     /* W */
3089     {
3090         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3091         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3092         eact = create_adjustment_action( "RectWidthAction",
3093                                          _("Width"), _("W:"), _("Width of rectangle"),
3094                                          "/tools/shapes/rect/width", 0,
3095                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
3096                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3097                                          labels, values, G_N_ELEMENTS(labels),
3098                                          sp_rtb_width_value_changed );
3099         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3100         g_object_set_data( holder, "width_action", eact );
3101         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3102         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3103     }
3105     /* H */
3106     {
3107         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3108         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3109         eact = create_adjustment_action( "RectHeightAction",
3110                                          _("Height"), _("H:"), _("Height of rectangle"),
3111                                          "/tools/shapes/rect/height", 0,
3112                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3113                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3114                                          labels, values, G_N_ELEMENTS(labels),
3115                                          sp_rtb_height_value_changed );
3116         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3117         g_object_set_data( holder, "height_action", eact );
3118         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3119         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3120     }
3122     /* rx */
3123     {
3124         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3125         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3126         eact = create_adjustment_action( "RadiusXAction",
3127                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
3128                                          "/tools/shapes/rect/rx", 0,
3129                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3130                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3131                                          labels, values, G_N_ELEMENTS(labels),
3132                                          sp_rtb_rx_value_changed);
3133         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3134         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3135     }
3137     /* ry */
3138     {
3139         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3140         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3141         eact = create_adjustment_action( "RadiusYAction",
3142                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
3143                                          "/tools/shapes/rect/ry", 0,
3144                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3145                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3146                                          labels, values, G_N_ELEMENTS(labels),
3147                                          sp_rtb_ry_value_changed);
3148         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3149         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3150     }
3152     // add the units menu
3153     {
3154         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
3155         gtk_action_group_add_action( mainActions, act );
3156     }
3158     /* Reset */
3159     {
3160         InkAction* inky = ink_action_new( "RectResetAction",
3161                                           _("Not rounded"),
3162                                           _("Make corners sharp"),
3163                                           INKSCAPE_ICON_RECTANGLE_MAKE_CORNERS_SHARP,
3164                                           secondarySize );
3165         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
3166         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3167         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3168         g_object_set_data( holder, "not_rounded", inky );
3169     }
3171     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
3172     sp_rtb_sensitivize( holder );
3174     sigc::connection *connection = new sigc::connection(
3175         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
3176         );
3177     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3178     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3181 //########################
3182 //##       3D Box       ##
3183 //########################
3185 // normalize angle so that it lies in the interval [0,360]
3186 static double box3d_normalize_angle (double a) {
3187     double angle = a + ((int) (a/360.0))*360;
3188     if (angle < 0) {
3189         angle += 360.0;
3190     }
3191     return angle;
3194 static void
3195 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
3196                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
3197     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
3198     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
3199     //       are reset).
3200     bool is_infinite = !persp3d_VP_is_finite(persp, axis);
3202     if (is_infinite) {
3203         gtk_toggle_action_set_active(tact, TRUE);
3204         gtk_action_set_sensitive(act, TRUE);
3206         double angle = persp3d_get_infinite_angle(persp, axis);
3207         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
3208             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
3209         }
3210     } else {
3211         gtk_toggle_action_set_active(tact, FALSE);
3212         gtk_action_set_sensitive(act, FALSE);
3213     }
3216 static void
3217 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
3218     if (!persp_repr) {
3219         g_print ("No perspective given to box3d_resync_toolbar().\n");
3220         return;
3221     }
3223     GtkWidget *tbl = GTK_WIDGET(data);
3224     GtkAdjustment *adj = 0;
3225     GtkAction *act = 0;
3226     GtkToggleAction *tact = 0;
3227     Persp3D *persp = persp3d_get_from_repr(persp_repr);
3228     {
3229         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
3230         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
3231         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
3233         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
3234     }
3235     {
3236         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
3237         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
3238         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
3240         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
3241     }
3242     {
3243         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
3244         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
3245         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
3247         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
3248     }
3251 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3252                                                   gchar const */*old_value*/, gchar const */*new_value*/,
3253                                                   bool /*is_interactive*/, gpointer data)
3255     GtkWidget *tbl = GTK_WIDGET(data);
3257     // quit if run by the attr_changed or selection changed listener
3258     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3259         return;
3260     }
3262     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
3263     // sp_document_maybe_done() when the document is undo insensitive)
3264     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3266     // TODO: Only update the appropriate part of the toolbar
3267 //    if (!strcmp(name, "inkscape:vp_z")) {
3268         box3d_resync_toolbar(repr, G_OBJECT(tbl));
3269 //    }
3271     Persp3D *persp = persp3d_get_from_repr(repr);
3272     persp3d_update_box_reprs(persp);
3274     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3277 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
3279     NULL, /* child_added */
3280     NULL, /* child_removed */
3281     box3d_persp_tb_event_attr_changed,
3282     NULL, /* content_changed */
3283     NULL  /* order_changed */
3284 };
3286 /**
3287  *  \param selection Should not be NULL.
3288  */
3289 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
3290 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
3291 static void
3292 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3294     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
3295     // disable the angle entry fields for this direction (otherwise entering a value in them should only
3296     // update the perspectives with infinite VPs and leave the other ones untouched).
3298     Inkscape::XML::Node *persp_repr = NULL;
3299     purge_repr_listener(tbl, tbl);
3301     SPItem *item = selection->singleItem();
3302     if (item && SP_IS_BOX3D(item)) {
3303         // FIXME: Also deal with multiple selected boxes
3304         SPBox3D *box = SP_BOX3D(item);
3305         Persp3D *persp = box3d_get_perspective(box);
3306         persp_repr = SP_OBJECT_REPR(persp);
3307         if (persp_repr) {
3308             g_object_set_data(tbl, "repr", persp_repr);
3309             Inkscape::GC::anchor(persp_repr);
3310             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
3311             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
3312         }
3314         inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
3315         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3316         prefs->setString("/tools/shapes/3dbox/persp", persp_repr->attribute("id"));
3318         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
3319         box3d_resync_toolbar(persp_repr, tbl);
3320         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
3321     }
3324 static void
3325 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
3327     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3328     SPDocument *document = sp_desktop_document(desktop);
3330     // quit if run by the attr_changed or selection changed listener
3331     if (g_object_get_data( dataKludge, "freeze" )) {
3332         return;
3333     }
3335     // in turn, prevent listener from responding
3336     g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
3338     //Persp3D *persp = document->current_persp3d;
3339     std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
3340     if (sel_persps.empty()) {
3341         // this can happen when the document is created; we silently ignore it
3342         return;
3343     }
3344     Persp3D *persp = sel_persps.front();
3346     persp->tmat.set_infinite_direction (axis, adj->value);
3347     SP_OBJECT(persp)->updateRepr();
3349     // TODO: use the correct axis here, too
3350     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
3352     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
3356 static void
3357 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3359     box3d_angle_value_changed(adj, dataKludge, Proj::X);
3362 static void
3363 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3365     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
3368 static void
3369 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3371     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
3375 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
3377     // TODO: Take all selected perspectives into account
3378     std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
3379     if (sel_persps.empty()) {
3380         // this can happen when the document is created; we silently ignore it
3381         return;
3382     }
3383     Persp3D *persp = sel_persps.front();
3385     bool set_infinite = gtk_toggle_action_get_active(act);
3386     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
3389 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3391     box3d_vp_state_changed(act, box3d_angle, Proj::X);
3394 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3396     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
3399 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3401     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
3404 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3406     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3407     EgeAdjustmentAction* eact = 0;
3408     SPDocument *document = sp_desktop_document (desktop);
3409     Persp3D *persp = document->current_persp3d;
3411     EgeAdjustmentAction* box3d_angle_x = 0;
3412     EgeAdjustmentAction* box3d_angle_y = 0;
3413     EgeAdjustmentAction* box3d_angle_z = 0;
3415     /* Angle X */
3416     {
3417         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3418         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3419         eact = create_adjustment_action( "3DBoxAngleXAction",
3420                                          _("Angle in X direction"), _("Angle X:"),
3421                                          // Translators: PL is short for 'perspective line'
3422                                          _("Angle of PLs in X direction"),
3423                                          "/tools/shapes/3dbox/box3d_angle_x", 30,
3424                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
3425                                          -360.0, 360.0, 1.0, 10.0,
3426                                          labels, values, G_N_ELEMENTS(labels),
3427                                          box3d_angle_x_value_changed );
3428         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3429         g_object_set_data( holder, "box3d_angle_x_action", eact );
3430         box3d_angle_x = eact;
3431     }
3433     if (!persp3d_VP_is_finite(persp, Proj::X)) {
3434         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3435     } else {
3436         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3437     }
3440     /* VP X state */
3441     {
3442         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
3443                                                       // Translators: VP is short for 'vanishing point'
3444                                                       _("State of VP in X direction"),
3445                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
3446                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3447                                                       Inkscape::ICON_SIZE_DECORATION );
3448         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3449         g_object_set_data( holder, "box3d_vp_x_state_action", act );
3450         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
3451         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3452         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3453     }
3455     /* Angle Y */
3456     {
3457         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3458         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3459         eact = create_adjustment_action( "3DBoxAngleYAction",
3460                                          _("Angle in Y direction"), _("Angle Y:"),
3461                                          // Translators: PL is short for 'perspective line'
3462                                          _("Angle of PLs in Y direction"),
3463                                          "/tools/shapes/3dbox/box3d_angle_y", 30,
3464                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3465                                          -360.0, 360.0, 1.0, 10.0,
3466                                          labels, values, G_N_ELEMENTS(labels),
3467                                          box3d_angle_y_value_changed );
3468         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3469         g_object_set_data( holder, "box3d_angle_y_action", eact );
3470         box3d_angle_y = eact;
3471     }
3473     if (!persp3d_VP_is_finite(persp, Proj::Y)) {
3474         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3475     } else {
3476         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3477     }
3479     /* VP Y state */
3480     {
3481         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
3482                                                       // Translators: VP is short for 'vanishing point'
3483                                                       _("State of VP in Y direction"),
3484                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
3485                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3486                                                       Inkscape::ICON_SIZE_DECORATION );
3487         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3488         g_object_set_data( holder, "box3d_vp_y_state_action", act );
3489         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
3490         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3491         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3492     }
3494     /* Angle Z */
3495     {
3496         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3497         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3498         eact = create_adjustment_action( "3DBoxAngleZAction",
3499                                          _("Angle in Z direction"), _("Angle Z:"),
3500                                          // Translators: PL is short for 'perspective line'
3501                                          _("Angle of PLs in Z direction"),
3502                                          "/tools/shapes/3dbox/box3d_angle_z", 30,
3503                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3504                                          -360.0, 360.0, 1.0, 10.0,
3505                                          labels, values, G_N_ELEMENTS(labels),
3506                                          box3d_angle_z_value_changed );
3507         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3508         g_object_set_data( holder, "box3d_angle_z_action", eact );
3509         box3d_angle_z = eact;
3510     }
3512     if (!persp3d_VP_is_finite(persp, Proj::Z)) {
3513         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3514     } else {
3515         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3516     }
3518     /* VP Z state */
3519     {
3520         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3521                                                       // Translators: VP is short for 'vanishing point'
3522                                                       _("State of VP in Z direction"),
3523                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3524                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3525                                                       Inkscape::ICON_SIZE_DECORATION );
3526         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3527         g_object_set_data( holder, "box3d_vp_z_state_action", act );
3528         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3529         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3530         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3531     }
3533     sigc::connection *connection = new sigc::connection(
3534         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3535        );
3536     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3537     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3540 //########################
3541 //##       Spiral       ##
3542 //########################
3544 static void
3545 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, Glib::ustring const &value_name)
3547     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3549     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3550         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3551         prefs->setDouble("/tools/shapes/spiral/" + value_name, adj->value);
3552     }
3554     // quit if run by the attr_changed listener
3555     if (g_object_get_data( tbl, "freeze" )) {
3556         return;
3557     }
3559     // in turn, prevent listener from responding
3560     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3562     gchar* namespaced_name = g_strconcat("sodipodi:", value_name.data(), NULL);
3564     bool modmade = false;
3565     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3566          items != NULL;
3567          items = items->next)
3568     {
3569         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3570             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3571             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3572             SP_OBJECT((SPItem *) items->data)->updateRepr();
3573             modmade = true;
3574         }
3575     }
3577     g_free(namespaced_name);
3579     if (modmade) {
3580         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3581                                    _("Change spiral"));
3582     }
3584     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3587 static void
3588 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3590     sp_spl_tb_value_changed(adj, tbl, "revolution");
3593 static void
3594 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3596     sp_spl_tb_value_changed(adj, tbl, "expansion");
3599 static void
3600 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3602     sp_spl_tb_value_changed(adj, tbl, "t0");
3605 static void
3606 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3608     GtkWidget *tbl = GTK_WIDGET(obj);
3610     GtkAdjustment *adj;
3612     // fixme: make settable
3613     gdouble rev = 5;
3614     gdouble exp = 1.0;
3615     gdouble t0 = 0.0;
3617     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3618     gtk_adjustment_set_value(adj, rev);
3619     gtk_adjustment_value_changed(adj);
3621     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3622     gtk_adjustment_set_value(adj, exp);
3623     gtk_adjustment_value_changed(adj);
3625     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3626     gtk_adjustment_set_value(adj, t0);
3627     gtk_adjustment_value_changed(adj);
3629     spinbutton_defocus(GTK_OBJECT(tbl));
3633 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3634                                          gchar const */*old_value*/, gchar const */*new_value*/,
3635                                          bool /*is_interactive*/, gpointer data)
3637     GtkWidget *tbl = GTK_WIDGET(data);
3639     // quit if run by the _changed callbacks
3640     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3641         return;
3642     }
3644     // in turn, prevent callbacks from responding
3645     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3647     GtkAdjustment *adj;
3648     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3649     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3651     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3652     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3654     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3655     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3657     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3661 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3662     NULL, /* child_added */
3663     NULL, /* child_removed */
3664     spiral_tb_event_attr_changed,
3665     NULL, /* content_changed */
3666     NULL  /* order_changed */
3667 };
3669 static void
3670 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3672     int n_selected = 0;
3673     Inkscape::XML::Node *repr = NULL;
3675     purge_repr_listener( tbl, tbl );
3677     for (GSList const *items = selection->itemList();
3678          items != NULL;
3679          items = items->next)
3680     {
3681         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3682             n_selected++;
3683             repr = SP_OBJECT_REPR((SPItem *) items->data);
3684         }
3685     }
3687     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3689     if (n_selected == 0) {
3690         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3691     } else if (n_selected == 1) {
3692         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3694         if (repr) {
3695             g_object_set_data( tbl, "repr", repr );
3696             Inkscape::GC::anchor(repr);
3697             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3698             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3699         }
3700     } else {
3701         // FIXME: implement averaging of all parameters for multiple selected
3702         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3703         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3704     }
3708 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3710     EgeAdjustmentAction* eact = 0;
3711     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3713     {
3714         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3715         ege_output_action_set_use_markup( act, TRUE );
3716         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3717         g_object_set_data( holder, "mode_action", act );
3718     }
3720     /* Revolution */
3721     {
3722         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3723         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3724         eact = create_adjustment_action( "SpiralRevolutionAction",
3725                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3726                                          "/tools/shapes/spiral/revolution", 3.0,
3727                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3728                                          0.01, 1024.0, 0.1, 1.0,
3729                                          labels, values, G_N_ELEMENTS(labels),
3730                                          sp_spl_tb_revolution_value_changed, 1, 2);
3731         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3732     }
3734     /* Expansion */
3735     {
3736         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3737         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3738         eact = create_adjustment_action( "SpiralExpansionAction",
3739                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3740                                          "/tools/shapes/spiral/expansion", 1.0,
3741                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3742                                          0.0, 1000.0, 0.01, 1.0,
3743                                          labels, values, G_N_ELEMENTS(labels),
3744                                          sp_spl_tb_expansion_value_changed);
3745         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3746     }
3748     /* T0 */
3749     {
3750         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3751         gdouble values[] = {0, 0.5, 0.9};
3752         eact = create_adjustment_action( "SpiralT0Action",
3753                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3754                                          "/tools/shapes/spiral/t0", 0.0,
3755                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3756                                          0.0, 0.999, 0.01, 1.0,
3757                                          labels, values, G_N_ELEMENTS(labels),
3758                                          sp_spl_tb_t0_value_changed);
3759         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3760     }
3762     /* Reset */
3763     {
3764         InkAction* inky = ink_action_new( "SpiralResetAction",
3765                                           _("Defaults"),
3766                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3767                                           GTK_STOCK_CLEAR,
3768                                           secondarySize );
3769         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3770         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3771     }
3774     sigc::connection *connection = new sigc::connection(
3775         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3776         );
3777     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3778     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3781 //########################
3782 //##     Pen/Pencil     ##
3783 //########################
3785 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3786 static Glib::ustring const
3787 freehand_tool_name(GObject *dataKludge)
3789     SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3790     return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3791              ? "/tools/freehand/pen"
3792              : "/tools/freehand/pencil" );
3795 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3797     gint mode = ege_select_one_action_get_active(act);
3799     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3800     prefs->setInt(freehand_tool_name(tbl) + "/freehand-mode", mode);
3802     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3804     // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3805     // preparatory work here
3806     if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3807         SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3808         sp_pen_context_set_polyline_mode(pc);
3809     }
3812 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3814     /* Freehand mode toggle buttons */
3815     {
3816         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3817         guint freehandMode = prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/freehand-mode" : "/tools/freehand/pen/freehand-mode" ), 0);
3818         Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3820         {
3821             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3823             GtkTreeIter iter;
3824             gtk_list_store_append( model, &iter );
3825             gtk_list_store_set( model, &iter,
3826                                 0, _("Bezier"),
3827                                 1, _("Create regular Bezier path"),
3828                                 2, INKSCAPE_ICON_PATH_MODE_BEZIER,
3829                                 -1 );
3831             gtk_list_store_append( model, &iter );
3832             gtk_list_store_set( model, &iter,
3833                                 0, _("Spiro"),
3834                                 1, _("Create Spiro path"),
3835                                 2, INKSCAPE_ICON_PATH_MODE_SPIRO,
3836                                 -1 );
3838             if (!tool_is_pencil) {
3839                 gtk_list_store_append( model, &iter );
3840                 gtk_list_store_set( model, &iter,
3841                                     0, _("Zigzag"),
3842                                     1, _("Create a sequence of straight line segments"),
3843                                     2, INKSCAPE_ICON_PATH_MODE_POLYLINE,
3844                                     -1 );
3846                 gtk_list_store_append( model, &iter );
3847                 gtk_list_store_set( model, &iter,
3848                                     0, _("Paraxial"),
3849                                     1, _("Create a sequence of paraxial line segments"),
3850                                     2, INKSCAPE_ICON_PATH_MODE_POLYLINE_PARAXIAL,
3851                                     -1 );
3852             }
3854             EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3855                                                                 "FreehandModeActionPencil" :
3856                                                                 "FreehandModeActionPen",
3857                                                                 (_("Mode:")), (_("Mode of new lines drawn by this tool")), NULL, GTK_TREE_MODEL(model) );
3858             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3860             ege_select_one_action_set_appearance( act, "full" );
3861             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3862             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3863             ege_select_one_action_set_icon_column( act, 2 );
3864             ege_select_one_action_set_icon_size( act, secondarySize );
3865             ege_select_one_action_set_tooltip_column( act, 1  );
3867             ege_select_one_action_set_active( act, freehandMode);
3868             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3869         }
3870     }
3873 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3874     gint shape = ege_select_one_action_get_active( act );
3875     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3876     prefs->setInt(freehand_tool_name(dataKludge) + "/shape", shape);
3879 /**
3880  * \brief Generate the list of freehand advanced shape option entries.
3881  */
3882 GList * freehand_shape_dropdown_items_list() {
3883     GList *glist = NULL;
3885     glist = g_list_append (glist, _("None"));
3886     glist = g_list_append (glist, _("Triangle in"));
3887     glist = g_list_append (glist, _("Triangle out"));
3888     glist = g_list_append (glist, _("Ellipse"));
3889     glist = g_list_append (glist, _("From clipboard"));
3891     return glist;
3894 static void
3895 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3896     /*advanced shape options */
3897     {
3898         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3899         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3901         GList* items = 0;
3902         gint count = 0;
3903         for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3904         {
3905             GtkTreeIter iter;
3906             gtk_list_store_append( model, &iter );
3907             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3908             count++;
3909         }
3910         g_list_free( items );
3911         items = 0;
3912         EgeSelectOneAction* act1 = ege_select_one_action_new(
3913             tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3914             _("Shape:"), (_("Shape of new paths drawn by this tool")), NULL, GTK_TREE_MODEL(model));
3915         g_object_set( act1, "short_label", _("Shape:"), NULL );
3916         ege_select_one_action_set_appearance( act1, "compact" );
3917         ege_select_one_action_set_active( act1, prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/shape" : "/tools/freehand/pen/shape" ), 0) );
3918         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3919         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3920         g_object_set_data( holder, "shape_action", act1 );
3921     }
3924 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3926     sp_add_freehand_mode_toggle(mainActions, holder, false);
3927     freehand_add_advanced_shape_options(mainActions, holder, false);
3931 static void
3932 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3934     GtkWidget *tbl = GTK_WIDGET(obj);
3936     GtkAdjustment *adj;
3938     // fixme: make settable
3939     gdouble tolerance = 4;
3941     adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3942     gtk_adjustment_set_value(adj, tolerance);
3943     gtk_adjustment_value_changed(adj);
3945     spinbutton_defocus(GTK_OBJECT(tbl));
3948 static void
3949 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3951     // quit if run by the attr_changed listener
3952     if (g_object_get_data( tbl, "freeze" )) {
3953         return;
3954     }
3955     // in turn, prevent listener from responding
3956     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3957     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3958     prefs->setDouble("/tools/freehand/pencil/tolerance", adj->value);
3959     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3962 class PencilToleranceObserver : public Inkscape::Preferences::Observer {
3963 public:
3964     PencilToleranceObserver(Glib::ustring const &path, GObject *x) : Observer(path), _obj(x)
3965     {
3966         g_object_set_data(_obj, "prefobserver", this);
3967     }
3968     virtual ~PencilToleranceObserver() {
3969         if (g_object_get_data(_obj, "prefobserver") == this) {
3970                 g_object_set_data(_obj, "prefobserver", NULL);
3971         }
3972     }
3973     virtual void notify(Inkscape::Preferences::Entry const &val) {
3974         GObject* tbl = _obj;
3975         if (g_object_get_data( tbl, "freeze" )) {
3976             return;
3977         }
3978         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3980         GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl, "tolerance");
3982         double v = val.getDouble(adj->value);
3983         gtk_adjustment_set_value(adj, v);
3984         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3985     }
3986 private:
3987     GObject *_obj;
3988 };
3991 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3993     sp_add_freehand_mode_toggle(mainActions, holder, true);
3995     EgeAdjustmentAction* eact = 0;
3997     /* Tolerance */
3998     {
3999         gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
4000         gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
4001         eact = create_adjustment_action( "PencilToleranceAction",
4002                                          _("Smoothing:"), _("Smoothing: "),
4003                  _("How much smoothing (simplifying) is applied to the line"),
4004                                          "/tools/freehand/pencil/tolerance",
4005                                          3.0,
4006                                          GTK_WIDGET(desktop->canvas), NULL,
4007                                          holder, TRUE, "altx-pencil",
4008                                          1, 100.0, 0.5, 1.0,
4009                                          labels, values, G_N_ELEMENTS(labels),
4010                                          sp_pencil_tb_tolerance_value_changed,
4011                                          1, 2);
4012         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4013         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4015         PencilToleranceObserver *obs =
4016             new PencilToleranceObserver("/tools/freehand/pencil/tolerance", G_OBJECT(holder));
4017     }
4019     /* advanced shape options */
4020     freehand_add_advanced_shape_options(mainActions, holder, true);
4022     /* Reset */
4023     {
4024         InkAction* inky = ink_action_new( "PencilResetAction",
4025                                           _("Defaults"),
4026                                           _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
4027                                           GTK_STOCK_CLEAR,
4028                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4029         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
4030         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4031     }
4033     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4038 //########################
4039 //##       Tweak        ##
4040 //########################
4042 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4044     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4045     prefs->setDouble( "/tools/tweak/width", adj->value * 0.01 );
4048 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4050     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4051     prefs->setDouble( "/tools/tweak/force", adj->value * 0.01 );
4054 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4056     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4057     prefs->setBool("/tools/tweak/usepressure", gtk_toggle_action_get_active(act));
4060 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4062     int mode = ege_select_one_action_get_active( act );
4063     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4064     prefs->setInt("/tools/tweak/mode", mode);
4066     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
4067     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
4068     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
4069     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
4070     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
4071     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
4072     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
4073         if (doh) gtk_action_set_sensitive (doh, TRUE);
4074         if (dos) gtk_action_set_sensitive (dos, TRUE);
4075         if (dol) gtk_action_set_sensitive (dol, TRUE);
4076         if (doo) gtk_action_set_sensitive (doo, TRUE);
4077         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
4078         if (fid) gtk_action_set_sensitive (fid, FALSE);
4079     } else {
4080         if (doh) gtk_action_set_sensitive (doh, FALSE);
4081         if (dos) gtk_action_set_sensitive (dos, FALSE);
4082         if (dol) gtk_action_set_sensitive (dol, FALSE);
4083         if (doo) gtk_action_set_sensitive (doo, FALSE);
4084         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
4085         if (fid) gtk_action_set_sensitive (fid, TRUE);
4086     }
4089 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4091     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4092     prefs->setDouble( "/tools/tweak/fidelity", adj->value * 0.01 );
4095 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
4096     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4097     prefs->setBool("/tools/tweak/doh", gtk_toggle_action_get_active(act));
4099 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
4100     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4101     prefs->setBool("/tools/tweak/dos", gtk_toggle_action_get_active(act));
4103 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
4104     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4105     prefs->setBool("/tools/tweak/dol", gtk_toggle_action_get_active(act));
4107 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
4108     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4109     prefs->setBool("/tools/tweak/doo", gtk_toggle_action_get_active(act));
4112 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4114     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
4115     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4117     {
4118         /* Width */
4119         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
4120         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4121         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
4122                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
4123                                                               "/tools/tweak/width", 15,
4124                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
4125                                                               1, 100, 1.0, 10.0,
4126                                                               labels, values, G_N_ELEMENTS(labels),
4127                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
4128         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4129         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4130         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4131     }
4134     {
4135         /* Force */
4136         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
4137         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4138         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
4139                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
4140                                                               "/tools/tweak/force", 20,
4141                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
4142                                                               1, 100, 1.0, 10.0,
4143                                                               labels, values, G_N_ELEMENTS(labels),
4144                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
4145         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4146         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4147         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4148     }
4150     /* Mode */
4151     {
4152         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4154         GtkTreeIter iter;
4155         gtk_list_store_append( model, &iter );
4156         gtk_list_store_set( model, &iter,
4157                             0, _("Move mode"),
4158                             1, _("Move objects in any direction"),
4159                             2, INKSCAPE_ICON_OBJECT_TWEAK_PUSH,
4160                             -1 );
4162         gtk_list_store_append( model, &iter );
4163         gtk_list_store_set( model, &iter,
4164                             0, _("Move in/out mode"),
4165                             1, _("Move objects towards cursor; with Shift from cursor"),
4166                             2, INKSCAPE_ICON_OBJECT_TWEAK_ATTRACT,
4167                             -1 );
4169         gtk_list_store_append( model, &iter );
4170         gtk_list_store_set( model, &iter,
4171                             0, _("Move jitter mode"),
4172                             1, _("Move objects in random directions"),
4173                             2, INKSCAPE_ICON_OBJECT_TWEAK_RANDOMIZE,
4174                             -1 );
4176         gtk_list_store_append( model, &iter );
4177         gtk_list_store_set( model, &iter,
4178                             0, _("Scale mode"),
4179                             1, _("Shrink objects, with Shift enlarge"),
4180                             2, INKSCAPE_ICON_OBJECT_TWEAK_SHRINK,
4181                             -1 );
4183         gtk_list_store_append( model, &iter );
4184         gtk_list_store_set( model, &iter,
4185                             0, _("Rotate mode"),
4186                             1, _("Rotate objects, with Shift counterclockwise"),
4187                             2, INKSCAPE_ICON_OBJECT_TWEAK_ROTATE,
4188                             -1 );
4190         gtk_list_store_append( model, &iter );
4191         gtk_list_store_set( model, &iter,
4192                             0, _("Duplicate/delete mode"),
4193                             1, _("Duplicate objects, with Shift delete"),
4194                             2, INKSCAPE_ICON_OBJECT_TWEAK_DUPLICATE,
4195                             -1 );
4197         gtk_list_store_append( model, &iter );
4198         gtk_list_store_set( model, &iter,
4199                             0, _("Push mode"),
4200                             1, _("Push parts of paths in any direction"),
4201                             2, INKSCAPE_ICON_PATH_TWEAK_PUSH,
4202                             -1 );
4204         gtk_list_store_append( model, &iter );
4205         gtk_list_store_set( model, &iter,
4206                             0, _("Shrink/grow mode"),
4207                             1, _("Shrink (inset) parts of paths; with Shift grow (outset)"),
4208                             2, INKSCAPE_ICON_PATH_TWEAK_SHRINK,
4209                             -1 );
4211         gtk_list_store_append( model, &iter );
4212         gtk_list_store_set( model, &iter,
4213                             0, _("Attract/repel mode"),
4214                             1, _("Attract parts of paths towards cursor; with Shift from cursor"),
4215                             2, INKSCAPE_ICON_PATH_TWEAK_ATTRACT,
4216                             -1 );
4218         gtk_list_store_append( model, &iter );
4219         gtk_list_store_set( model, &iter,
4220                             0, _("Roughen mode"),
4221                             1, _("Roughen parts of paths"),
4222                             2, INKSCAPE_ICON_PATH_TWEAK_ROUGHEN,
4223                             -1 );
4225         gtk_list_store_append( model, &iter );
4226         gtk_list_store_set( model, &iter,
4227                             0, _("Color paint mode"),
4228                             1, _("Paint the tool's color upon selected objects"),
4229                             2, INKSCAPE_ICON_OBJECT_TWEAK_PAINT,
4230                             -1 );
4232         gtk_list_store_append( model, &iter );
4233         gtk_list_store_set( model, &iter,
4234                             0, _("Color jitter mode"),
4235                             1, _("Jitter the colors of selected objects"),
4236                             2, INKSCAPE_ICON_OBJECT_TWEAK_JITTER_COLOR,
4237                             -1 );
4239         gtk_list_store_append( model, &iter );
4240         gtk_list_store_set( model, &iter,
4241                             0, _("Blur mode"),
4242                             1, _("Blur selected objects more; with Shift, blur less"),
4243                             2, INKSCAPE_ICON_OBJECT_TWEAK_BLUR,
4244                             -1 );
4247         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4248         g_object_set( act, "short_label", _("Mode:"), NULL );
4249         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4250         g_object_set_data( holder, "mode_action", act );
4252         ege_select_one_action_set_appearance( act, "full" );
4253         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4254         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4255         ege_select_one_action_set_icon_column( act, 2 );
4256         ege_select_one_action_set_icon_size( act, secondarySize );
4257         ege_select_one_action_set_tooltip_column( act, 1  );
4259         gint mode = prefs->getInt("/tools/tweak/mode", 0);
4260         ege_select_one_action_set_active( act, mode );
4261         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
4263         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
4264     }
4266     guint mode = prefs->getInt("/tools/tweak/mode", 0);
4268     {
4269         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
4270         ege_output_action_set_use_markup( act, TRUE );
4271         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4272         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4273             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4274         g_object_set_data( holder, "tweak_channels_label", act);
4275     }
4277     {
4278         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
4279                                                       _("Hue"),
4280                                                       _("In color mode, act on objects' hue"),
4281                                                       NULL,
4282                                                       Inkscape::ICON_SIZE_DECORATION );
4283         //TRANSLATORS:  "H" here stands for hue
4284         g_object_set( act, "short_label", _("H"), NULL );
4285         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4286         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
4287         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doh", true) );
4288         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4289             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4290         g_object_set_data( holder, "tweak_doh", act);
4291     }
4292     {
4293         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
4294                                                       _("Saturation"),
4295                                                       _("In color mode, act on objects' saturation"),
4296                                                       NULL,
4297                                                       Inkscape::ICON_SIZE_DECORATION );
4298         //TRANSLATORS: "S" here stands for Saturation
4299         g_object_set( act, "short_label", _("S"), NULL );
4300         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4301         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
4302         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dos", true) );
4303         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4304             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4305         g_object_set_data( holder, "tweak_dos", act );
4306     }
4307     {
4308         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
4309                                                       _("Lightness"),
4310                                                       _("In color mode, act on objects' lightness"),
4311                                                       NULL,
4312                                                       Inkscape::ICON_SIZE_DECORATION );
4313         //TRANSLATORS: "L" here stands for Lightness
4314         g_object_set( act, "short_label", _("L"), NULL );
4315         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4316         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
4317         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dol", true) );
4318         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4319             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4320         g_object_set_data( holder, "tweak_dol", act );
4321     }
4322     {
4323         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
4324                                                       _("Opacity"),
4325                                                       _("In color mode, act on objects' opacity"),
4326                                                       NULL,
4327                                                       Inkscape::ICON_SIZE_DECORATION );
4328         //TRANSLATORS: "O" here stands for Opacity
4329         g_object_set( act, "short_label", _("O"), NULL );
4330         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4331         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
4332         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doo", true) );
4333         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4334             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4335         g_object_set_data( holder, "tweak_doo", act );
4336     }
4338     {   /* Fidelity */
4339         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
4340         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4341         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
4342                                                               _("Fidelity"), _("Fidelity:"),
4343                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
4344                                                               "/tools/tweak/fidelity", 50,
4345                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
4346                                                               1, 100, 1.0, 10.0,
4347                                                               labels, values, G_N_ELEMENTS(labels),
4348                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
4349         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4350         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4351         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
4352             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
4353         g_object_set_data( holder, "tweak_fidelity", eact );
4354     }
4357     /* Use Pressure button */
4358     {
4359         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
4360                                                       _("Pressure"),
4361                                                       _("Use the pressure of the input device to alter the force of tweak action"),
4362                                                       INKSCAPE_ICON_DRAW_USE_PRESSURE,
4363                                                       Inkscape::ICON_SIZE_DECORATION );
4364         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4365         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
4366         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/usepressure", true) );
4367     }
4372 //########################
4373 //##     Calligraphy    ##
4374 //########################
4375 static void update_presets_list (GObject *tbl)
4377     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4378     if (g_object_get_data(tbl, "presets_blocked"))
4379         return;
4381     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4382     if (!sel) {
4383         // WTF!? This will cause a segfault if ever reached
4384         //ege_select_one_action_set_active(sel, 0);
4385         return;
4386     }
4388     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4390     int ege_index = 1;
4391     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++ege_index) {
4392         bool match = true;
4394         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(*i);
4395         for (std::vector<Inkscape::Preferences::Entry>::iterator j = preset.begin(); j != preset.end(); ++j) {
4396             Glib::ustring entry_name = j->getEntryName();
4397             if (entry_name == "id" || entry_name == "name") continue;
4399             void *widget = g_object_get_data(tbl, entry_name.data());
4400             if (widget) {
4401                 if (GTK_IS_ADJUSTMENT(widget)) {
4402                     double v = j->getDouble();
4403                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4404                     //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
4405                     if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
4406                         match = false;
4407                         break;
4408                     }
4409                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4410                     bool v = j->getBool();
4411                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4412                     //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
4413                     if ( static_cast<bool>(gtk_toggle_action_get_active(toggle)) != v ) {
4414                         match = false;
4415                         break;
4416                     }
4417                 }
4418             }
4419         }
4421         if (match) {
4422             // newly added item is at the same index as the
4423             // save command, so we need to change twice for it to take effect
4424             ege_select_one_action_set_active(sel, 0);
4425             ege_select_one_action_set_active(sel, ege_index); // one-based index
4426             return;
4427         }
4428     }
4430     // no match found
4431     ege_select_one_action_set_active(sel, 0);
4434 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
4436     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4437     prefs->setDouble( "/tools/calligraphic/mass", adj->value * 0.01 );
4438     update_presets_list(tbl);
4441 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
4443     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4444     prefs->setDouble( "/tools/calligraphic/wiggle", adj->value * 0.01 );
4445     update_presets_list(tbl);
4448 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
4450     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4451     prefs->setDouble( "/tools/calligraphic/angle", adj->value );
4452     update_presets_list(tbl);
4455 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
4457     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4458     prefs->setDouble( "/tools/calligraphic/width", adj->value * 0.01 );
4459     update_presets_list(tbl);
4462 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
4464     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4465     prefs->setDouble("/tools/calligraphic/thinning", adj->value * 0.01 );
4466     update_presets_list(tbl);
4469 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
4471     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4472     prefs->setDouble( "/tools/calligraphic/flatness", adj->value * 0.01);
4473     update_presets_list(tbl);
4476 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
4478     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4479     prefs->setDouble( "/tools/calligraphic/tremor", adj->value * 0.01 );
4480     update_presets_list(tbl);
4483 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
4485     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4486     prefs->setDouble( "/tools/calligraphic/cap_rounding", adj->value );
4487     update_presets_list(tbl);
4490 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
4492     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4493     prefs->setBool("/tools/calligraphic/usepressure", gtk_toggle_action_get_active( act ));
4494     update_presets_list(tbl);
4497 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
4499     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4500     prefs->setBool("/tools/calligraphic/tracebackground", gtk_toggle_action_get_active( act ));
4501     update_presets_list(tbl);
4504 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
4506     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4507     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
4508     prefs->setBool("/tools/calligraphic/usetilt", gtk_toggle_action_get_active( act ));
4509     update_presets_list(tbl);
4510     if (calligraphy_angle )
4511         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
4515 static gchar const *const widget_names[] = {
4516     "width",
4517     "mass",
4518     "wiggle",
4519     "angle",
4520     "thinning",
4521     "tremor",
4522     "flatness",
4523     "cap_rounding",
4524     "usepressure",
4525     "tracebackground",
4526     "usetilt"
4527 };
4530 static void sp_dcc_build_presets_list(GObject *tbl)
4532     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4534     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4535     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4536     gtk_list_store_clear (model);
4538     {
4539         GtkTreeIter iter;
4540         gtk_list_store_append( model, &iter );
4541         gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4542     }
4544     // iterate over all presets to populate the list
4545     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4546     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4547     int ii=1;
4549     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i) {
4550         GtkTreeIter iter;
4551         Glib::ustring preset_name = prefs->getString(*i + "/name");
4552         gtk_list_store_append( model, &iter );
4553         gtk_list_store_set( model, &iter, 0, preset_name.data(), 1, ii++, -1 );
4554     }
4556     {
4557         GtkTreeIter iter;
4558         gtk_list_store_append( model, &iter );
4559         gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4560         g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4561     }
4563     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4565     update_presets_list (tbl);
4568 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4570     using Inkscape::UI::Dialog::CalligraphicProfileRename;
4571     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4572     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4573     if (! desktop) return;
4575     if (g_object_get_data(tbl, "presets_blocked"))
4576         return;
4578     CalligraphicProfileRename::show(desktop);
4579     if ( !CalligraphicProfileRename::applied()) {
4580         // dialog cancelled
4581         update_presets_list (tbl);
4582         return;
4583     }
4584     Glib::ustring profile_name = CalligraphicProfileRename::getProfileName();
4586     if (profile_name.empty()) {
4587         // empty name entered
4588         update_presets_list (tbl);
4589         return;
4590     }
4592     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4594     // If there's a preset with the given name, find it and set save_path appropriately
4595     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4596     int total_presets = presets.size();
4597     int new_index = -1;
4598     Glib::ustring save_path; // profile pref path without a trailing slash
4600     int temp_index = 0;
4601     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++temp_index) {
4602         Glib::ustring name = prefs->getString(*i + "/name");
4603         if (!name.empty() && profile_name == name) {
4604             new_index = temp_index;
4605             save_path = *i;
4606             break;
4607         }
4608     }
4610     if (new_index == -1) {
4611         // no preset with this name, create
4612         new_index = total_presets + 1;
4613         gchar *profile_id = g_strdup_printf("/dcc%d", new_index);
4614         save_path = Glib::ustring("/tools/calligraphic/preset") + profile_id;
4615         g_free(profile_id);
4616     }
4618     for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4619         gchar const *const widget_name = widget_names[i];
4620         void *widget = g_object_get_data(tbl, widget_name);
4621         if (widget) {
4622             if (GTK_IS_ADJUSTMENT(widget)) {
4623                 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4624                 prefs->setDouble(save_path + "/" + widget_name, gtk_adjustment_get_value(adj));
4625                 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4626             } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4627                 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4628                 prefs->setBool(save_path + "/" + widget_name, gtk_toggle_action_get_active(toggle));
4629                 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4630             } else {
4631                 g_warning("Unknown widget type for preset: %s\n", widget_name);
4632             }
4633         } else {
4634             g_warning("Bad key when writing preset: %s\n", widget_name);
4635         }
4636     }
4637     prefs->setString(save_path + "/name", profile_name);
4639     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4640     sp_dcc_build_presets_list (tbl);
4644 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4646     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4648     gint preset_index = ege_select_one_action_get_active( act );
4649     // This is necessary because EgeSelectOneAction spams us with GObject "changed" signal calls
4650     // even when the preset is not changed. It would be good to replace it with something more
4651     // modern. Index 0 means "No preset", so we don't do anything.
4652     if (preset_index == 0) return;
4654     gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4656     if (preset_index == save_presets_index) {
4657         // this is the Save command
4658         sp_dcc_save_profile(NULL, tbl);
4659         return;
4660     }
4662     if (g_object_get_data(tbl, "presets_blocked"))
4663         return;
4665     // preset_index is one-based so we subtract 1
4666     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4667     Glib::ustring preset_path = presets.at(preset_index - 1);
4669     if (!preset_path.empty()) {
4670         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
4672         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(preset_path);
4674         // Shouldn't this be std::map?
4675         for (std::vector<Inkscape::Preferences::Entry>::iterator i = preset.begin(); i != preset.end(); ++i) {
4676             Glib::ustring entry_name = i->getEntryName();
4677             if (entry_name == "id" || entry_name == "name") continue;
4678             void *widget = g_object_get_data(tbl, entry_name.data());
4679             if (widget) {
4680                 if (GTK_IS_ADJUSTMENT(widget)) {
4681                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4682                     gtk_adjustment_set_value(adj, i->getDouble());
4683                     //std::cout << "set adj " << attr_name << " to " << v << "\n";
4684                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4685                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4686                     gtk_toggle_action_set_active(toggle, i->getBool());
4687                     //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4688                 } else {
4689                     g_warning("Unknown widget type for preset: %s\n", entry_name.data());
4690                 }
4691             } else {
4692                 g_warning("Bad key found in a preset record: %s\n", entry_name.data());
4693             }
4694         }
4695         g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4696     }
4700 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4702     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4703     {
4704         g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4706         EgeAdjustmentAction* calligraphy_angle = 0;
4708         {
4709         /* Width */
4710         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4711         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4712         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4713                                                               _("Pen Width"), _("Width:"),
4714                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
4715                                                               "/tools/calligraphic/width", 15,
4716                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4717                                                               1, 100, 1.0, 10.0,
4718                                                               labels, values, G_N_ELEMENTS(labels),
4719                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
4720         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4721         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4722         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4723         }
4725         {
4726         /* Thinning */
4727             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4728             gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4729         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4730                                                               _("Stroke Thinning"), _("Thinning:"),
4731                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4732                                                               "/tools/calligraphic/thinning", 10,
4733                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4734                                                               -100, 100, 1, 10.0,
4735                                                               labels, values, G_N_ELEMENTS(labels),
4736                                                               sp_ddc_velthin_value_changed, 0.01, 0, 100);
4737         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4738         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4739         }
4741         {
4742         /* Angle */
4743         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4744         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4745         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4746                                                               _("Pen Angle"), _("Angle:"),
4747                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4748                                                               "/tools/calligraphic/angle", 30,
4749                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4750                                                               -90.0, 90.0, 1.0, 10.0,
4751                                                               labels, values, G_N_ELEMENTS(labels),
4752                                                               sp_ddc_angle_value_changed, 1, 0 );
4753         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4754         g_object_set_data( holder, "angle_action", eact );
4755         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4756         calligraphy_angle = eact;
4757         }
4759         {
4760         /* Fixation */
4761             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4762         gdouble values[] = {0, 20, 40, 60, 90, 100};
4763         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4764                                                               _("Fixation"), _("Fixation:"),
4765                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"),
4766                                                               "/tools/calligraphic/flatness", 90,
4767                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4768                                                               0.0, 100, 1.0, 10.0,
4769                                                               labels, values, G_N_ELEMENTS(labels),
4770                                                               sp_ddc_flatness_value_changed, 0.01, 0, 100 );
4771         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4772         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4773         }
4775         {
4776         /* Cap Rounding */
4777             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4778         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4779         // TRANSLATORS: "cap" means "end" (both start and finish) here
4780         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4781                                                               _("Cap rounding"), _("Caps:"),
4782                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4783                                                               "/tools/calligraphic/cap_rounding", 0.0,
4784                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4785                                                               0.0, 5.0, 0.01, 0.1,
4786                                                               labels, values, G_N_ELEMENTS(labels),
4787                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4788         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4789         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4790         }
4792         {
4793         /* Tremor */
4794             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4795         gdouble values[] = {0, 10, 20, 40, 60, 100};
4796         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4797                                                               _("Stroke Tremor"), _("Tremor:"),
4798                                                               _("Increase to make strokes rugged and trembling"),
4799                                                               "/tools/calligraphic/tremor", 0.0,
4800                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4801                                                               0.0, 100, 1, 10.0,
4802                                                               labels, values, G_N_ELEMENTS(labels),
4803                                                               sp_ddc_tremor_value_changed, 0.01, 0, 100 );
4805         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4806         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4807         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4808         }
4810         {
4811         /* Wiggle */
4812         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4813         gdouble values[] = {0, 20, 40, 60, 100};
4814         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4815                                                               _("Pen Wiggle"), _("Wiggle:"),
4816                                                               _("Increase to make the pen waver and wiggle"),
4817                                                               "/tools/calligraphic/wiggle", 0.0,
4818                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4819                                                               0.0, 100, 1, 10.0,
4820                                                               labels, values, G_N_ELEMENTS(labels),
4821                                                               sp_ddc_wiggle_value_changed, 0.01, 0, 100 );
4822         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4823         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4824         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4825         }
4827         {
4828         /* Mass */
4829             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4830         gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4831         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4832                                                               _("Pen Mass"), _("Mass:"),
4833                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
4834                                                               "/tools/calligraphic/mass", 2.0,
4835                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4836                                                               0.0, 100, 1, 10.0,
4837                                                               labels, values, G_N_ELEMENTS(labels),
4838                                                               sp_ddc_mass_value_changed, 0.01, 0, 100 );
4839         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4840         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4841         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4842         }
4845         /* Trace Background button */
4846         {
4847             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4848                                                           _("Trace Background"),
4849                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4850                                                           INKSCAPE_ICON_DRAW_TRACE_BACKGROUND,
4851                                                           Inkscape::ICON_SIZE_DECORATION );
4852             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4853             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4854             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/tracebackground", false) );
4855             g_object_set_data( holder, "tracebackground", act );
4856         }
4858         /* Use Pressure button */
4859         {
4860             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4861                                                           _("Pressure"),
4862                                                           _("Use the pressure of the input device to alter the width of the pen"),
4863                                                           INKSCAPE_ICON_DRAW_USE_PRESSURE,
4864                                                           Inkscape::ICON_SIZE_DECORATION );
4865             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4866             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4867             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usepressure", true) );
4868             g_object_set_data( holder, "usepressure", act );
4869         }
4871         /* Use Tilt button */
4872         {
4873             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4874                                                           _("Tilt"),
4875                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
4876                                                           INKSCAPE_ICON_DRAW_USE_TILT,
4877                                                           Inkscape::ICON_SIZE_DECORATION );
4878             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4879             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4880             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs->getBool("/tools/calligraphic/usetilt", true) );
4881             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usetilt", true) );
4882             g_object_set_data( holder, "usetilt", act );
4883         }
4885         /*calligraphic profile */
4886         {
4887             GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
4888             EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
4889             ege_select_one_action_set_appearance (act1, "compact");
4890             g_object_set_data (holder, "profile_selector", act1 );
4892             g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
4894             sp_dcc_build_presets_list (holder);
4896             g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
4897             gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
4898         }
4899     }
4903 //########################
4904 //##    Circle / Arc    ##
4905 //########################
4907 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4909     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4910     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4912     if (v1 == 0 && v2 == 0) {
4913         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4914             gtk_action_set_sensitive( ocb, FALSE );
4915             gtk_action_set_sensitive( make_whole, FALSE );
4916         }
4917     } else {
4918         gtk_action_set_sensitive( ocb, TRUE );
4919         gtk_action_set_sensitive( make_whole, TRUE );
4920     }
4923 static void
4924 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4926     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4928     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4929         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4930         prefs->setDouble(Glib::ustring("/tools/shapes/arc") + value_name, (adj->value * M_PI)/ 180);
4931     }
4933     // quit if run by the attr_changed listener
4934     if (g_object_get_data( tbl, "freeze" )) {
4935         return;
4936     }
4938     // in turn, prevent listener from responding
4939     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4941     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4943     bool modmade = false;
4944     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4945          items != NULL;
4946          items = items->next)
4947     {
4948         SPItem *item = SP_ITEM(items->data);
4950         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4952             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4953             SPArc *arc = SP_ARC(item);
4955             if (!strcmp(value_name, "start"))
4956                 ge->start = (adj->value * M_PI)/ 180;
4957             else
4958                 ge->end = (adj->value * M_PI)/ 180;
4960             sp_genericellipse_normalize(ge);
4961             ((SPObject *)arc)->updateRepr();
4962             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4964             modmade = true;
4965         }
4966     }
4968     g_free(namespaced_name);
4970     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4972     sp_arctb_sensitivize( tbl, adj->value, other->value );
4974     if (modmade) {
4975         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4976                                    _("Arc: Change start/end"));
4977     }
4979     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4983 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
4985     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
4988 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4990     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
4994 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4996     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4997     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4998         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4999         prefs->setBool("/tools/shapes/arc/open", ege_select_one_action_get_active(act) != 0);
5000     }
5002     // quit if run by the attr_changed listener
5003     if (g_object_get_data( tbl, "freeze" )) {
5004         return;
5005     }
5007     // in turn, prevent listener from responding
5008     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5010     bool modmade = false;
5012     if ( ege_select_one_action_get_active(act) != 0 ) {
5013         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5014              items != NULL;
5015              items = items->next)
5016         {
5017             if (SP_IS_ARC((SPItem *) items->data)) {
5018                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5019                 repr->setAttribute("sodipodi:open", "true");
5020                 SP_OBJECT((SPItem *) items->data)->updateRepr();
5021                 modmade = true;
5022             }
5023         }
5024     } else {
5025         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5026              items != NULL;
5027              items = items->next)
5028         {
5029             if (SP_IS_ARC((SPItem *) items->data))    {
5030                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5031                 repr->setAttribute("sodipodi:open", NULL);
5032                 SP_OBJECT((SPItem *) items->data)->updateRepr();
5033                 modmade = true;
5034             }
5035         }
5036     }
5038     if (modmade) {
5039         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
5040                                    _("Arc: Change open/closed"));
5041     }
5043     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5046 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
5048     GtkAdjustment *adj;
5049     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
5050     gtk_adjustment_set_value(adj, 0.0);
5051     gtk_adjustment_value_changed(adj);
5053     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
5054     gtk_adjustment_set_value(adj, 0.0);
5055     gtk_adjustment_value_changed(adj);
5057     spinbutton_defocus( GTK_OBJECT(obj) );
5060 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
5061                                       gchar const */*old_value*/, gchar const */*new_value*/,
5062                                       bool /*is_interactive*/, gpointer data)
5064     GObject *tbl = G_OBJECT(data);
5066     // quit if run by the _changed callbacks
5067     if (g_object_get_data( tbl, "freeze" )) {
5068         return;
5069     }
5071     // in turn, prevent callbacks from responding
5072     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5074     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
5075     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
5077     GtkAdjustment *adj1,*adj2;
5078     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
5079     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
5080     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
5081     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
5083     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
5085     char const *openstr = NULL;
5086     openstr = repr->attribute("sodipodi:open");
5087     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
5089     if (openstr) {
5090         ege_select_one_action_set_active( ocb, 1 );
5091     } else {
5092         ege_select_one_action_set_active( ocb, 0 );
5093     }
5095     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5098 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
5099     NULL, /* child_added */
5100     NULL, /* child_removed */
5101     arc_tb_event_attr_changed,
5102     NULL, /* content_changed */
5103     NULL  /* order_changed */
5104 };
5107 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
5109     int n_selected = 0;
5110     Inkscape::XML::Node *repr = NULL;
5112     purge_repr_listener( tbl, tbl );
5114     for (GSList const *items = selection->itemList();
5115          items != NULL;
5116          items = items->next)
5117     {
5118         if (SP_IS_ARC((SPItem *) items->data)) {
5119             n_selected++;
5120             repr = SP_OBJECT_REPR((SPItem *) items->data);
5121         }
5122     }
5124     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
5126     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
5127     if (n_selected == 0) {
5128         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
5129     } else if (n_selected == 1) {
5130         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
5131         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5133         if (repr) {
5134             g_object_set_data( tbl, "repr", repr );
5135             Inkscape::GC::anchor(repr);
5136             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
5137             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
5138         }
5139     } else {
5140         // FIXME: implement averaging of all parameters for multiple selected
5141         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
5142         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5143         sp_arctb_sensitivize( tbl, 1, 0 );
5144     }
5148 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5150     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5152     EgeAdjustmentAction* eact = 0;
5153     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
5156     {
5157         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
5158         ege_output_action_set_use_markup( act, TRUE );
5159         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5160         g_object_set_data( holder, "mode_action", act );
5161     }
5163     /* Start */
5164     {
5165         eact = create_adjustment_action( "ArcStartAction",
5166                                          _("Start"), _("Start:"),
5167                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
5168                                          "/tools/shapes/arc/start", 0.0,
5169                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
5170                                          -360.0, 360.0, 1.0, 10.0,
5171                                          0, 0, 0,
5172                                          sp_arctb_start_value_changed);
5173         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5174     }
5176     /* End */
5177     {
5178         eact = create_adjustment_action( "ArcEndAction",
5179                                          _("End"), _("End:"),
5180                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
5181                                          "/tools/shapes/arc/end", 0.0,
5182                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
5183                                          -360.0, 360.0, 1.0, 10.0,
5184                                          0, 0, 0,
5185                                          sp_arctb_end_value_changed);
5186         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5187     }
5189     /* Segments / Pie checkbox */
5190     {
5191         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5193         GtkTreeIter iter;
5194         gtk_list_store_append( model, &iter );
5195         gtk_list_store_set( model, &iter,
5196                             0, _("Closed arc"),
5197                             1, _("Switch to segment (closed shape with two radii)"),
5198                             2, INKSCAPE_ICON_DRAW_ELLIPSE_SEGMENT,
5199                             -1 );
5201         gtk_list_store_append( model, &iter );
5202         gtk_list_store_set( model, &iter,
5203                             0, _("Open Arc"),
5204                             1, _("Switch to arc (unclosed shape)"),
5205                             2, INKSCAPE_ICON_DRAW_ELLIPSE_ARC,
5206                             -1 );
5208         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5209         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5210         g_object_set_data( holder, "open_action", act );
5212         ege_select_one_action_set_appearance( act, "full" );
5213         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5214         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5215         ege_select_one_action_set_icon_column( act, 2 );
5216         ege_select_one_action_set_icon_size( act, secondarySize );
5217         ege_select_one_action_set_tooltip_column( act, 1  );
5219         bool isClosed = !prefs->getBool("/tools/shapes/arc/open", false);
5220         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
5221         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
5222     }
5224     /* Make Whole */
5225     {
5226         InkAction* inky = ink_action_new( "ArcResetAction",
5227                                           _("Make whole"),
5228                                           _("Make the shape a whole ellipse, not arc or segment"),
5229                                           INKSCAPE_ICON_DRAW_ELLIPSE_WHOLE,
5230                                           secondarySize );
5231         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
5232         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5233         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
5234         g_object_set_data( holder, "make_whole", inky );
5235     }
5237     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
5238     // sensitivize make whole and open checkbox
5239     {
5240         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
5241         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
5242         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
5243     }
5246     sigc::connection *connection = new sigc::connection(
5247         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
5248         );
5249     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
5250     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
5256 // toggle button callbacks and updaters
5258 //########################
5259 //##      Dropper       ##
5260 //########################
5262 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
5263     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5264     prefs->setInt( "/tools/dropper/pick", gtk_toggle_action_get_active( act ) );
5265     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
5266     if ( set_action ) {
5267         if ( gtk_toggle_action_get_active( act ) ) {
5268             gtk_action_set_sensitive( set_action, TRUE );
5269         } else {
5270             gtk_action_set_sensitive( set_action, FALSE );
5271         }
5272     }
5274     spinbutton_defocus(GTK_OBJECT(tbl));
5277 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
5278     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5279     prefs->setBool( "/tools/dropper/setalpha", gtk_toggle_action_get_active( act ) );
5280     spinbutton_defocus(GTK_OBJECT(tbl));
5284 /**
5285  * Dropper auxiliary toolbar construction and setup.
5286  *
5287  * TODO: Would like to add swatch of current color.
5288  * TODO: Add queue of last 5 or so colors selected with new swatches so that
5289  *       can drag and drop places. Will provide a nice mixing palette.
5290  */
5291 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
5293     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5294     gint pickAlpha = prefs->getInt( "/tools/dropper/pick", 1 );
5296     {
5297         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
5298         ege_output_action_set_use_markup( act, TRUE );
5299         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5300     }
5302     {
5303         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
5304                                                       _("Pick opacity"),
5305                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
5306                                                       NULL,
5307                                                       Inkscape::ICON_SIZE_DECORATION );
5308         g_object_set( act, "short_label", _("Pick"), NULL );
5309         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5310         g_object_set_data( holder, "pick_action", act );
5311         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
5312         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
5313     }
5315     {
5316         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
5317                                                       _("Assign opacity"),
5318                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
5319                                                       NULL,
5320                                                       Inkscape::ICON_SIZE_DECORATION );
5321         g_object_set( act, "short_label", _("Assign"), NULL );
5322         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5323         g_object_set_data( holder, "set_action", act );
5324         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/dropper/setalpha", true) );
5325         // make sure it's disabled if we're not picking alpha
5326         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
5327         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
5328     }
5332 //########################
5333 //##      LPETool       ##
5334 //########################
5336 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
5338 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
5339 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
5341     using namespace Inkscape::LivePathEffect;
5343     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
5344     SPEventContext *ec = desktop->event_context;
5345     if (!SP_IS_LPETOOL_CONTEXT(ec)) {
5346         return;
5347     }
5349     // only take action if run by the attr_changed listener
5350     if (!g_object_get_data(tbl, "freeze")) {
5351         // in turn, prevent listener from responding
5352         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5354         gint mode = ege_select_one_action_get_active(act);
5355         EffectType type = lpesubtools[mode].type;
5357         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5358         bool success = lpetool_try_construction(lc, type);
5359         if (success) {
5360             // since the construction was already performed, we set the state back to inactive
5361             ege_select_one_action_set_active(act, 0);
5362             mode = 0;
5363         } else {
5364             // switch to the chosen subtool
5365             SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
5366         }
5368         if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5369             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5370             prefs->setInt( "/tools/lpetool/mode", mode );
5371         }
5373         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
5374     }
5377 void sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject */*tbl*/)
5379     SPEventContext *ec = selection->desktop()->event_context;
5380     if (!SP_IS_LPETOOL_CONTEXT(ec))
5381         return;
5383     lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
5386 void
5387 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
5389     using namespace Inkscape::LivePathEffect;
5390     SPEventContext *ec = selection->desktop()->event_context;
5391     if (!SP_IS_LPETOOL_CONTEXT(ec))
5392         return;
5393     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
5395     lpetool_delete_measuring_items(lc);
5396     lpetool_create_measuring_items(lc, selection);
5398     // activate line segment combo box if a single item with LPELineSegment is selected
5399     GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
5400     SPItem *item = selection->singleItem();
5401     if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
5402         SPLPEItem *lpeitem = SP_LPE_ITEM(item);
5403         Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
5404         if (lpe && lpe->effectType() == LINE_SEGMENT) {
5405             LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
5406             g_object_set_data(tbl, "currentlpe", lpe);
5407             g_object_set_data(tbl, "currentlpeitem", lpeitem);
5408             gtk_action_set_sensitive(w, TRUE);
5409             ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
5410         } else {
5411             g_object_set_data(tbl, "currentlpe", NULL);
5412             g_object_set_data(tbl, "currentlpeitem", NULL);
5413             gtk_action_set_sensitive(w, FALSE);
5414         }
5415     } else {
5416         g_object_set_data(tbl, "currentlpe", NULL);
5417         g_object_set_data(tbl, "currentlpeitem", NULL);
5418         gtk_action_set_sensitive(w, FALSE);
5419     }
5422 static void
5423 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
5424     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5425     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5427     bool show = gtk_toggle_action_get_active( act );
5428     prefs->setBool("/tools/lpetool/show_bbox",  show);
5430     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5431         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5432         lpetool_context_reset_limiting_bbox(lc);
5433     }
5436 static void
5437 lpetool_toggle_show_measuring_info (GtkToggleAction *act, GObject *tbl) {
5438     SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
5439     if (!tools_isactive(desktop, TOOLS_LPETOOL))
5440         return;
5442     GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
5443     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5444     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5445         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5446         bool show = gtk_toggle_action_get_active( act );
5447         prefs->setBool("/tools/lpetool/show_measuring_info",  show);
5448         lpetool_show_measuring_info(lc, show);
5449         gtk_action_set_sensitive(GTK_ACTION(unitact), show);
5450     }
5453 static void lpetool_unit_changed(GtkAction* /*act*/, GObject* tbl) {
5454     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
5455     SPUnit const *unit = tracker->getActiveUnit();
5456     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5457     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5459     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5460     if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
5461         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5462         lpetool_delete_measuring_items(lc);
5463         lpetool_create_measuring_items(lc);
5464     }
5467 static void
5468 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
5469     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5470     Inkscape::Selection *selection = desktop->selection;
5472     Geom::OptRect bbox = selection->bounds();
5474     if (bbox) {
5475         Geom::Point A(bbox->min());
5476         Geom::Point B(bbox->max());
5478         A *= desktop->doc2dt();
5479         B *= desktop->doc2dt();
5481         // TODO: should we provide a way to store points in prefs?
5482         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5483         prefs->setDouble("/tools/lpetool/bbox_upperleftx", A[Geom::X]);
5484         prefs->setDouble("/tools/lpetool/bbox_upperlefty", A[Geom::Y]);
5485         prefs->setDouble("/tools/lpetool/bbox_lowerrightx", B[Geom::X]);
5486         prefs->setDouble("/tools/lpetool/bbox_lowerrighty", B[Geom::Y]);
5488         lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
5489     }
5491     gtk_toggle_action_set_active(act, false);
5494 static void
5495 sp_line_segment_build_list(GObject *tbl)
5497     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
5499     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
5500     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
5501     gtk_list_store_clear (model);
5503     // TODO: we add the entries of rht combo box manually; later this should be done automatically
5504     {
5505         GtkTreeIter iter;
5506         gtk_list_store_append( model, &iter );
5507         gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
5508         gtk_list_store_append( model, &iter );
5509         gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
5510         gtk_list_store_append( model, &iter );
5511         gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
5512         gtk_list_store_append( model, &iter );
5513         gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
5514     }
5516     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5519 static void
5520 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
5521     using namespace Inkscape::LivePathEffect;
5523     // quit if run by the attr_changed listener
5524     if (g_object_get_data(tbl, "freeze")) {
5525         return;
5526     }
5528     // in turn, prevent listener from responding
5529     g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5531     LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
5532     SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5533     if (lpeitem) {
5534         SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5535         lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
5536         sp_lpe_item_update_patheffect(lpeitem, true, true);
5537     }
5539     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5542 static void
5543 lpetool_open_lpe_dialog (GtkToggleAction *act, gpointer data) {
5544     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5546     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5547         sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5548     }
5549     gtk_toggle_action_set_active(act, false);
5552 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5554     UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5555     tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5556     g_object_set_data(holder, "tracker", tracker);
5557     SPUnit const *unit = tracker->getActiveUnit();
5559     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5560     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5562     /** Automatically create a list of LPEs that get added to the toolbar **/
5563     {
5564         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5566         GtkTreeIter iter;
5568         // the first toggle button represents the state that no subtool is active (remove this when
5569         // this can be modeled by EgeSelectOneAction or some other action)
5570         gtk_list_store_append( model, &iter );
5571         gtk_list_store_set( model, &iter,
5572                             0, _("All inactive"),
5573                             1, _("No geometric tool is active"),
5574                             2, _("draw-geometry-inactive"),
5575                             -1 );
5577         Inkscape::LivePathEffect::EffectType type;
5578         for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5579             type =  lpesubtools[i].type;
5580             gtk_list_store_append( model, &iter );
5581             gtk_list_store_set( model, &iter,
5582                                 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5583                                 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5584                                 2, lpesubtools[i].icon_name,
5585                                 -1 );
5586         }
5588         EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5589         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5590         g_object_set_data( holder, "lpetool_mode_action", act );
5592         ege_select_one_action_set_appearance( act, "full" );
5593         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5594         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5595         ege_select_one_action_set_icon_column( act, 2 );
5596         ege_select_one_action_set_tooltip_column( act, 1  );
5598         gint lpeToolMode = prefs->getInt("/tools/lpetool/mode", 0);
5599         ege_select_one_action_set_active( act, lpeToolMode );
5600         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5601     }
5603     /* Show limiting bounding box */
5604     {
5605         InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5606                                                       _("Show limiting bounding box"),
5607                                                       _("Show bounding box (used to cut infinite lines)"),
5608                                                       "show-bounding-box",
5609                                                       Inkscape::ICON_SIZE_DECORATION );
5610         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5611         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5612         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_bbox", true ) );
5613     }
5615     /* Set limiting bounding box to bbox of current selection */
5616     {
5617         InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5618                                                       _("Get limiting bounding box from selection"),
5619                                                       _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5620                                                       "draw-geometry-set-bounding-box",
5621                                                       Inkscape::ICON_SIZE_DECORATION );
5622         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5623         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5624         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5625     }
5628     /* Combo box to choose line segment type */
5629     {
5630         GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5631         EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5632         ege_select_one_action_set_appearance (act, "compact");
5633         g_object_set_data (holder, "lpetool_line_segment_action", act );
5635         g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5637         sp_line_segment_build_list (holder);
5639         g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5640         gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5641         gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5642     }
5644     /* Display measuring info for selected items */
5645     {
5646         InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5647                                                       _("Display measuring info"),
5648                                                       _("Display measuring info for selected items"),
5649                                                       "draw-geometry-show-measuring-info",
5650                                                       Inkscape::ICON_SIZE_DECORATION );
5651         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5652         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
5653         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_measuring_info", true ) );
5654     }
5656     // add the units menu
5657     {
5658         GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5659         gtk_action_group_add_action( mainActions, act );
5660         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5661         g_object_set_data(holder, "lpetool_units_action", act);
5662         gtk_action_set_sensitive(act, prefs->getBool("/tools/lpetool/show_measuring_info", true));
5663     }
5665     /* Open LPE dialog (to adapt parameters numerically) */
5666     {
5667         InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
5668                                                       _("Open LPE dialog"),
5669                                                       _("Open LPE dialog (to adapt parameters numerically)"),
5670                                                       "dialog-geometry",
5671                                                       Inkscape::ICON_SIZE_DECORATION );
5672         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5673         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
5674         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5675     }
5677     //watch selection
5678     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5680     sigc::connection *c_selection_modified =
5681         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5682                               (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
5683     pool->add_connection ("selection-modified", c_selection_modified);
5685     sigc::connection *c_selection_changed =
5686         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5687                               (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5688     pool->add_connection ("selection-changed", c_selection_changed);
5691 //########################
5692 //##       Eraser       ##
5693 //########################
5695 static void sp_erc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
5697     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5698     prefs->setDouble( "/tools/eraser/width", adj->value * 0.01 );
5699     update_presets_list(tbl);
5702 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5704     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5705     bool eraserMode = ege_select_one_action_get_active( act ) != 0;
5706     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5707         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5708         prefs->setBool( "/tools/eraser/mode", eraserMode );
5709     }
5711     // only take action if run by the attr_changed listener
5712     if (!g_object_get_data( tbl, "freeze" )) {
5713         // in turn, prevent listener from responding
5714         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5716         if ( eraserMode != 0 ) {
5717         } else {
5718         }
5719         // TODO finish implementation
5721         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5722     }
5725 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5727     {
5728         /* Width */
5729         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5730         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5731         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5732                                                               _("Pen Width"), _("Width:"),
5733                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
5734                                                               "/tools/eraser/width", 15,
5735                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5736                                                               1, 100, 1.0, 10.0,
5737                                                               labels, values, G_N_ELEMENTS(labels),
5738                                                               sp_erc_width_value_changed,  0.01, 0, 100 );
5739         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5740         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5741         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5742     }
5744     {
5745         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5747         GtkTreeIter iter;
5748         gtk_list_store_append( model, &iter );
5749         gtk_list_store_set( model, &iter,
5750                             0, _("Delete"),
5751                             1, _("Delete objects touched by the eraser"),
5752                             2, INKSCAPE_ICON_DRAW_ERASER_DELETE_OBJECTS,
5753                             -1 );
5755         gtk_list_store_append( model, &iter );
5756         gtk_list_store_set( model, &iter,
5757                             0, _("Cut"),
5758                             1, _("Cut out from objects"),
5759                             2, INKSCAPE_ICON_PATH_DIFFERENCE,
5760                             -1 );
5762         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5763         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5764         g_object_set_data( holder, "eraser_mode_action", act );
5766         ege_select_one_action_set_appearance( act, "full" );
5767         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5768         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5769         ege_select_one_action_set_icon_column( act, 2 );
5770         ege_select_one_action_set_tooltip_column( act, 1  );
5772         /// @todo Convert to boolean?
5773         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5774         gint eraserMode = prefs->getBool("/tools/eraser/mode") ? 1 : 0;
5775         ege_select_one_action_set_active( act, eraserMode );
5776         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
5777     }
5781 //########################
5782 //##    Text Toolbox    ##
5783 //########################
5784 /*
5785 static void
5786 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
5788     //Call back for letter sizing spinbutton
5791 static void
5792 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
5794     //Call back for line height spinbutton
5797 static void
5798 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5800     //Call back for horizontal kerning spinbutton
5803 static void
5804 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5806     //Call back for vertical kerning spinbutton
5809 static void
5810 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
5812     //Call back for letter rotation spinbutton
5813 }*/
5815 namespace {
5817 void
5818 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
5820     // quit if run by the _changed callbacks
5821     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5822         return;
5823     }
5825     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5827     SPStyle *query =
5828         sp_style_new (SP_ACTIVE_DOCUMENT);
5830     int result_family =
5831         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5833     int result_style =
5834         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5836     int result_numbers =
5837         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5839     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5841     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5842     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
5843         // there are no texts in selection, read from prefs
5845             sp_style_read_from_prefs(query, "/tools/text");
5847             if (g_object_get_data(tbl, "text_style_from_prefs")) {
5848             // do not reset the toolbar style from prefs if we already did it last time
5849             sp_style_unref(query);
5850             g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5851             return;
5852         }
5853         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
5854     } else {
5855         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
5856     }
5858     if (query->text)
5859     {
5860         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
5861             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5862             gtk_entry_set_text (GTK_ENTRY (entry), "");
5864         } else if (query->text->font_specification.value || query->text->font_family.value) {
5866             Gtk::ComboBoxEntry *combo = (Gtk::ComboBoxEntry *) (g_object_get_data (G_OBJECT (tbl), "family-entry-combo"));
5867             GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5869             // Get the font that corresponds
5870             Glib::ustring familyName;
5872             font_instance * font = font_factory::Default()->FaceFromStyle(query);
5873             if (font) {
5874                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
5875                 font->Unref();
5876                 font = NULL;
5877             }
5879             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
5881             Gtk::TreeIter iter;
5882             try {
5883                 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
5884                 Glib::RefPtr<Gtk::TreeModel> model = combo->get_model();
5885                 iter = model->get_iter(path);
5886             } catch (...) {
5887                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
5888                 sp_style_unref(query);
5889                 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5890                 return;
5891             }
5893             combo->set_active (iter);
5894         }
5896         //Size
5897         {
5898             GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
5899             gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
5900             gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
5901             g_free(str);
5902         }
5904         //Anchor
5905         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
5906         {
5907             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
5908             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5909             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5910             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5911         }
5912         else
5913         {
5914             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
5915             {
5916                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
5917                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5918                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5919                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5920             }
5921             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
5922             {
5923                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
5924                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5925                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5926                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5927             }
5928             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
5929             {
5930                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
5931                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5932                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5933                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5934             }
5935         }
5937         //Style
5938         {
5939             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
5941             gboolean active = gtk_toggle_button_get_active (button);
5942             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
5944             if (active != check)
5945             {
5946                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5947                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5948                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5949             }
5950         }
5952         {
5953             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
5955             gboolean active = gtk_toggle_button_get_active (button);
5956             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
5958             if (active != check)
5959             {
5960                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5961                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5962                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5963             }
5964         }
5966         //Orientation
5967         //locking both buttons, changing one affect all group (both)
5968         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
5969         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5971         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
5972         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
5974         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
5975         {
5976             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5977         }
5978         else
5979         {
5980             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
5981         }
5982         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5983         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
5984     }
5986     sp_style_unref(query);
5988     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5991 void
5992 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
5994     sp_text_toolbox_selection_changed (selection, tbl);
5997 void
5998 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
6000     sp_text_toolbox_selection_changed (NULL, tbl);
6003 void
6004 sp_text_toolbox_family_changed (GtkComboBoxEntry    *,
6005                                 GObject             *tbl)
6007     // quit if run by the _changed callbacks
6008     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6009         return;
6010     }
6012     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6014     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
6015     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6016     const gchar* family = gtk_entry_get_text (GTK_ENTRY (entry));
6018     //g_print ("family changed to: %s\n", family);
6020     SPStyle *query =
6021         sp_style_new (SP_ACTIVE_DOCUMENT);
6023     int result_fontspec =
6024         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6026     SPCSSAttr *css = sp_repr_css_attr_new ();
6028     // First try to get the font spec from the stored value
6029     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
6031     if (fontSpec.empty()) {
6032         // Construct a new font specification if it does not yet exist
6033         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6034         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6035         fontFromStyle->Unref();
6036     }
6038     if (!fontSpec.empty()) {
6039         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
6040         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
6041             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
6042             if (font) {
6043                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6045                 // Set all the these just in case they were altered when finding the best
6046                 // match for the new family and old style...
6048                 gchar c[256];
6050                 font->Family(c, 256);
6051                 sp_repr_css_set_property (css, "font-family", c);
6053                 font->Attribute( "weight", c, 256);
6054                 sp_repr_css_set_property (css, "font-weight", c);
6056                 font->Attribute("style", c, 256);
6057                 sp_repr_css_set_property (css, "font-style", c);
6059                 font->Attribute("stretch", c, 256);
6060                 sp_repr_css_set_property (css, "font-stretch", c);
6062                 font->Attribute("variant", c, 256);
6063                 sp_repr_css_set_property (css, "font-variant", c);
6065                 font->Unref();
6066             }
6067         }
6068     }
6070     // If querying returned nothing, set the default style of the tool (for new texts)
6071     if (result_fontspec == QUERY_STYLE_NOTHING)
6072     {
6073         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6074         prefs->setStyle("/tools/text/style", css);
6075         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
6076     }
6077     else
6078     {
6079         sp_desktop_set_style (desktop, css, true, true);
6080     }
6082     sp_style_unref(query);
6084     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6085                                    _("Text: Change font family"));
6086     sp_repr_css_attr_unref (css);
6088     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6090     // unfreeze
6091     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6093     // focus to canvas
6094     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6098 void
6099 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
6100                                    gpointer          data)
6102     if (g_object_get_data (G_OBJECT (button), "block")) return;
6103     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
6104     int prop = GPOINTER_TO_INT(data);
6106     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6107     SPCSSAttr *css = sp_repr_css_attr_new ();
6109     switch (prop)
6110     {
6111         case 0:
6112         {
6113             sp_repr_css_set_property (css, "text-anchor", "start");
6114             sp_repr_css_set_property (css, "text-align", "start");
6115             break;
6116         }
6117         case 1:
6118         {
6119             sp_repr_css_set_property (css, "text-anchor", "middle");
6120             sp_repr_css_set_property (css, "text-align", "center");
6121             break;
6122         }
6124         case 2:
6125         {
6126             sp_repr_css_set_property (css, "text-anchor", "end");
6127             sp_repr_css_set_property (css, "text-align", "end");
6128             break;
6129         }
6131         case 3:
6132         {
6133             sp_repr_css_set_property (css, "text-anchor", "start");
6134             sp_repr_css_set_property (css, "text-align", "justify");
6135             break;
6136         }
6137     }
6139     SPStyle *query =
6140         sp_style_new (SP_ACTIVE_DOCUMENT);
6141     int result_numbers =
6142         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6144     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6145     if (result_numbers == QUERY_STYLE_NOTHING)
6146     {
6147         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6148         prefs->setStyle("/tools/text/style", css);
6149     }
6151     sp_style_unref(query);
6153     sp_desktop_set_style (desktop, css, true, true);
6154     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6155                                    _("Text: Change alignment"));
6156     sp_repr_css_attr_unref (css);
6158     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6161 void
6162 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
6163                                gpointer          data)
6165     if (g_object_get_data (G_OBJECT (button), "block")) return;
6167     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
6168     SPCSSAttr   *css        = sp_repr_css_attr_new ();
6169     int          prop       = GPOINTER_TO_INT(data);
6170     bool         active     = gtk_toggle_button_get_active (button);
6172     SPStyle *query =
6173         sp_style_new (SP_ACTIVE_DOCUMENT);
6175     int result_fontspec =
6176         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6178     //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6179     //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6180     //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6182     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
6183     Glib::ustring newFontSpec = "";
6185     if (fontSpec.empty()) {
6186         // Construct a new font specification if it does not yet exist
6187         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6188         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6189         fontFromStyle->Unref();
6190     }
6192     switch (prop)
6193     {
6194         case 0:
6195         {
6196             if (!fontSpec.empty()) {
6197                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
6198             }
6199             if (fontSpec != newFontSpec) {
6200                 // Don't even set the bold if the font didn't exist on the system
6201                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
6202             }
6203             break;
6204         }
6206         case 1:
6207         {
6208             if (!fontSpec.empty()) {
6209                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
6210             }
6211             if (fontSpec != newFontSpec) {
6212                 // Don't even set the italic if the font didn't exist on the system
6213                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
6214             }
6215             break;
6216         }
6217     }
6219     if (!newFontSpec.empty()) {
6220         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6221     }
6223     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6224     if (result_fontspec == QUERY_STYLE_NOTHING)
6225     {
6226         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6227         prefs->setStyle("/tools/text/style", css);
6228     }
6230     sp_style_unref(query);
6232     sp_desktop_set_style (desktop, css, true, true);
6233     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6234                                    _("Text: Change font style"));
6235     sp_repr_css_attr_unref (css);
6237     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6240 void
6241 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
6242                                      gpointer         data)
6244     if (g_object_get_data (G_OBJECT (button), "block")) {
6245         return;
6246     }
6248     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
6249     SPCSSAttr   *css        = sp_repr_css_attr_new ();
6250     int          prop       = GPOINTER_TO_INT(data);
6252     switch (prop)
6253     {
6254         case 0:
6255         {
6256             sp_repr_css_set_property (css, "writing-mode", "lr");
6257             break;
6258         }
6260         case 1:
6261         {
6262             sp_repr_css_set_property (css, "writing-mode", "tb");
6263             break;
6264         }
6265     }
6267     SPStyle *query =
6268         sp_style_new (SP_ACTIVE_DOCUMENT);
6269     int result_numbers =
6270         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6272     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6273     if (result_numbers == QUERY_STYLE_NOTHING)
6274     {
6275         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6276         prefs->setStyle("/tools/text/style", css);
6277     }
6279     sp_desktop_set_style (desktop, css, true, true);
6280     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6281                                    _("Text: Change orientation"));
6282     sp_repr_css_attr_unref (css);
6284     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6287 gboolean
6288 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6290     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6291     if (!desktop) return FALSE;
6293     switch (get_group0_keyval (event)) {
6294         case GDK_KP_Enter: // chosen
6295         case GDK_Return:
6296             // unfreeze and update, which will defocus
6297             g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6298             sp_text_toolbox_family_changed (NULL, tbl); 
6299             return TRUE; // I consumed the event
6300             break;
6301         case GDK_Escape: 
6302             // defocus
6303             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6304             return TRUE; // I consumed the event
6305             break;
6306     }
6307     return FALSE;
6310 gboolean
6311 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
6313     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6314     if (!desktop) return FALSE;
6316     switch (get_group0_keyval (event)) {
6317         case GDK_KP_Enter:
6318         case GDK_Return:
6319         case GDK_Escape: // defocus
6320             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6321             return TRUE; // I consumed the event
6322             break;
6323         case GDK_w:
6324         case GDK_W:
6325             if (event->state & GDK_CONTROL_MASK) {
6326                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6327                 return TRUE; // I consumed the event
6328             }
6329             break;
6330     }
6331     return FALSE;
6335 void
6336 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
6337                                GObject     *tbl)
6339      // quit if run by the _changed callbacks
6340     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6341         return;
6342     }
6344     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6346    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6348     // If this is not from selecting a size in the list (in which case get_active will give the
6349     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
6350     // process this event. This fixes GTK's stupid insistence on sending an activate change every
6351     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
6352    if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed")) {
6353         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6354         return;
6355    }
6357     gdouble value = -1;
6358     {
6359         gchar *endptr;
6360         gchar *const text = gtk_combo_box_get_active_text(cbox);
6361         if (text) {
6362             value = g_strtod(text, &endptr);
6363             if (endptr == text) {  // Conversion failed, non-numeric input.
6364                 value = -1;
6365             }
6366             g_free(text);
6367         }
6368     }
6369     if (value <= 0) {
6370         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6371         return; // could not parse value
6372     }
6374     SPCSSAttr *css = sp_repr_css_attr_new ();
6375     Inkscape::CSSOStringStream osfs;
6376     osfs << value;
6377     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
6379     SPStyle *query =
6380         sp_style_new (SP_ACTIVE_DOCUMENT);
6381     int result_numbers =
6382         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6384     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6385     if (result_numbers == QUERY_STYLE_NOTHING)
6386     {
6387         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6388         prefs->setStyle("/tools/text/style", css);
6389     }
6391     sp_style_unref(query);
6393     sp_desktop_set_style (desktop, css, true, true);
6394     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
6395                                    _("Text: Change font size"));
6396     sp_repr_css_attr_unref (css);
6398     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6400     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6403 gboolean
6404 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
6406     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6407     if (!desktop) return FALSE;
6409     if (!g_object_get_data (tbl, "esc-pressed")) {
6410         g_object_set_data (tbl, "enter-pressed", gpointer(1));
6411         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6412         sp_text_toolbox_size_changed (cbox, tbl);
6413         g_object_set_data (tbl, "enter-pressed", gpointer(0));
6414     }
6415     return FALSE; // I consumed the event
6419 gboolean
6420 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6422     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6423     if (!desktop) return FALSE;
6425     switch (get_group0_keyval (event)) {
6426         case GDK_Escape: // defocus
6427             g_object_set_data (tbl, "esc-pressed", gpointer(1));
6428             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6429             g_object_set_data (tbl, "esc-pressed", gpointer(0));
6430             return TRUE; // I consumed the event
6431             break;
6432         case GDK_Return: // defocus
6433         case GDK_KP_Enter:
6434             g_object_set_data (tbl, "enter-pressed", gpointer(1));
6435             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6436             sp_text_toolbox_size_changed (cbox, tbl);
6437             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6438             g_object_set_data (tbl, "enter-pressed", gpointer(0));
6439             return TRUE; // I consumed the event
6440             break;
6441     }
6442     return FALSE;
6445 // While editing font name in the entry, disable family_changed by freezing, otherwise completion
6446 // does not work!
6447 gboolean
6448 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
6449                                  GdkEventFocus    */*event*/,
6450                                  GObject          *tbl)
6452     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6453     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1); // select all
6454     return FALSE;
6457 gboolean
6458 sp_text_toolbox_entry_focus_out  (GtkWidget        *entry,
6459                                  GdkEventFocus    */*event*/,
6460                                  GObject          *tbl)
6462     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6463     gtk_entry_select_region (GTK_ENTRY (entry), 0, 0); // deselect
6464     return FALSE;
6467 void
6468 cell_data_func  (GtkCellLayout */*cell_layout*/,
6469                  GtkCellRenderer   *cell,
6470                  GtkTreeModel      *tree_model,
6471                  GtkTreeIter       *iter,
6472                  gpointer           /*data*/)
6474     gchar *family;
6475     gtk_tree_model_get(tree_model, iter, 0, &family, -1);
6476     gchar *const family_escaped = g_markup_escape_text(family, -1);
6478     static char const *const sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
6479     gchar *const sample_escaped = g_markup_escape_text(sample, -1);
6481     std::stringstream markup;
6482     markup << family_escaped << "  <span foreground='darkgray' font_family='"
6483            << family_escaped << "'>" << sample_escaped << "</span>";
6484     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6486     g_free(family);
6487     g_free(family_escaped);
6488     g_free(sample_escaped);
6491 gboolean            text_toolbox_completion_match_selected    (GtkEntryCompletion *widget,
6492                                                         GtkTreeModel       *model,
6493                                                         GtkTreeIter        *iter,
6494                                                         GObject *tbl) 
6496     // We intercept this signal so as to fire family_changed at once (without it, you'd have to
6497     // press Enter again after choosing a completion)
6498     gchar *family;
6499     gtk_tree_model_get(model, iter, 0, &family, -1);
6501     GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6502     gtk_entry_set_text (GTK_ENTRY (entry), family);
6504     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6505     sp_text_toolbox_family_changed (NULL, tbl);
6506     return TRUE;
6510 static void
6511 cbe_add_completion (GtkComboBoxEntry *cbe, GObject *tbl){
6512     GtkEntry *entry;
6513     GtkEntryCompletion *completion;
6514     GtkTreeModel *model;
6516     entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(cbe)));
6517     completion = gtk_entry_completion_new();
6518     model = gtk_combo_box_get_model(GTK_COMBO_BOX(cbe));
6519     gtk_entry_completion_set_model(completion, model);
6520     gtk_entry_completion_set_text_column(completion, 0);
6521     gtk_entry_completion_set_inline_completion(completion, FALSE);
6522     gtk_entry_completion_set_inline_selection(completion, FALSE);
6523     gtk_entry_completion_set_popup_completion(completion, TRUE);
6524     gtk_entry_set_completion(entry, completion);
6526     g_signal_connect (G_OBJECT (completion),  "match-selected", G_CALLBACK (text_toolbox_completion_match_selected), tbl);
6528     g_object_unref(completion);
6531 void        sp_text_toolbox_family_popnotify          (GtkComboBox *widget,
6532                                                        void *property,
6533                                                         GObject *tbl)
6535   // while the drop-down is open, we disable font family changing, reenabling it only when it closes
6537   gboolean shown;
6538   g_object_get (G_OBJECT(widget), "popup-shown", &shown, NULL);
6539   if (shown) {
6540          g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6541          //g_print("POP: notify: SHOWN\n");
6542   } else {
6543          g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6545          // stupid GTK doesn't let us attach to events in the drop-down window, so we peek here to
6546          // find out if the drop down was closed by Enter and if so, manually update (only
6547          // necessary on Windows, on Linux it updates itself - what a mess, but we'll manage)
6548          GdkEvent *ev = gtk_get_current_event();
6549          if (ev) {
6550              //g_print ("ev type: %d\n", ev->type);
6551              if (ev->type == GDK_KEY_PRESS) {
6552                  switch (get_group0_keyval ((GdkEventKey *) ev)) {
6553                      case GDK_KP_Enter: // chosen
6554                      case GDK_Return:
6555                      {
6556                          // make sure the chosen one is inserted into the entry
6557                          GtkComboBox  *combo = GTK_COMBO_BOX (((Gtk::ComboBox *) (g_object_get_data (tbl, "family-entry-combo")))->gobj());
6558                          GtkTreeModel *model = gtk_combo_box_get_model(combo);
6559                          GtkTreeIter iter;
6560                          gboolean has_active = gtk_combo_box_get_active_iter (combo, &iter);
6561                          if (has_active) {
6562                              gchar *family;
6563                              gtk_tree_model_get(model, &iter, 0, &family, -1);
6564                              GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6565                              gtk_entry_set_text (GTK_ENTRY (entry), family);
6566                          }
6568                          // update
6569                          sp_text_toolbox_family_changed (NULL, tbl); 
6570                          break;
6571                      }
6572                  } 
6573              }
6574          }
6576          // regardless of whether we updated, defocus the widget
6577          SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6578          if (desktop)
6579              gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6580          //g_print("POP: notify: HIDDEN\n");
6581   }
6584 GtkWidget*
6585 sp_text_toolbox_new (SPDesktop *desktop)
6587     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
6588     GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("/toolbox/secondary", 1));
6590     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
6591     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
6593     GtkTooltips *tt = gtk_tooltips_new();
6595     ////////////Family
6596     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
6597     Gtk::ComboBoxEntry *font_sel = Gtk::manage(new Gtk::ComboBoxEntry(store));
6598     g_signal_connect (G_OBJECT (font_sel->gobj()), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
6600     cbe_add_completion(font_sel->gobj(), G_OBJECT(tbl));
6601     
6602     gtk_toolbar_append_widget( tbl, (GtkWidget*) font_sel->gobj(), "", "");
6603     g_object_set_data (G_OBJECT (tbl), "family-entry-combo", font_sel);
6605     // expand the field a bit so as to view more of the previews in the drop-down
6606     GtkRequisition req;
6607     gtk_widget_size_request (GTK_WIDGET (font_sel->gobj()), &req);
6608     gtk_widget_set_size_request  (GTK_WIDGET (font_sel->gobj()), req.width + 40, -1);
6610     GtkWidget* entry = (GtkWidget*) font_sel->get_entry()->gobj();
6611     g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6613     g_signal_connect (G_OBJECT (font_sel->gobj()), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6614     g_signal_connect (G_OBJECT (font_sel->gobj()), "notify::popup-shown", 
6615              G_CALLBACK (sp_text_toolbox_family_popnotify), tbl);
6616     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
6617     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
6618     g_signal_connect (G_OBJECT (entry),  "focus-out-event", G_CALLBACK (sp_text_toolbox_entry_focus_out), tbl);
6620     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
6621     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
6623     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
6624     gtk_cell_layout_clear( GTK_CELL_LAYOUT(font_sel->gobj()) );
6625     gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(font_sel->gobj()) , cell , TRUE );
6626     gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT(font_sel->gobj()), cell, GtkCellLayoutDataFunc (cell_data_func), NULL, NULL);
6628     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
6629     GtkWidget *box = gtk_event_box_new ();
6630     gtk_container_add (GTK_CONTAINER (box), image);
6631     gtk_toolbar_append_widget( tbl, box, "", "");
6632     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
6633     GtkTooltips *tooltips = gtk_tooltips_new ();
6634     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
6635     gtk_widget_hide (GTK_WIDGET (box));
6636     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
6638     ////////////Size
6639     gchar const *const sizes[] = {
6640         "4", "6", "8", "9", "10", "11", "12", "13", "14",
6641         "16", "18", "20", "22", "24", "28",
6642         "32", "36", "40", "48", "56", "64", "72", "144"
6643     };
6645     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
6646     for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
6647         gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
6648     }
6649     gtk_widget_set_size_request (cbox, 80, -1);
6650     gtk_toolbar_append_widget( tbl, cbox, "", "");
6651     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
6652     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
6653     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
6654     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
6656     ////////////Text anchor
6657     GtkWidget *group   = gtk_radio_button_new (NULL);
6658     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
6659     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
6661     // left
6662     GtkWidget *rbutton = group;
6663     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6664     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
6665     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6667     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6668     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
6669     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
6670     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
6672     // center
6673     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6674     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6675     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
6676     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6678     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6679     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
6680     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
6681     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
6683     // right
6684     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6685     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6686     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
6687     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6689     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6690     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
6691     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
6692     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
6694     // fill
6695     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6696     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6697     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
6698     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6700     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6701     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
6702     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
6703     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
6705     gtk_toolbar_append_widget( tbl, row, "", "");
6707     //spacer
6708     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6710     ////////////Text style
6711     row = gtk_hbox_new (FALSE, 4);
6713     // bold
6714     rbutton = gtk_toggle_button_new ();
6715     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6716     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
6717     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6718     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
6720     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6721     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
6722     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
6724     // italic
6725     rbutton = gtk_toggle_button_new ();
6726     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6727     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
6728     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6729     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
6731     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6732     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
6733     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
6735     gtk_toolbar_append_widget( tbl, row, "", "");
6737     //spacer
6738     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6740     // Text orientation
6741     group   = gtk_radio_button_new (NULL);
6742     row     = gtk_hbox_new (FALSE, 4);
6743     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
6745     // horizontal
6746     rbutton = group;
6747     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6748     gtk_container_add           (GTK_CONTAINER (rbutton),
6749                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_HORIZONTAL));
6750     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6751     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
6753     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6754     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
6755     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
6757     // vertical
6758     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (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_VERTICAL));
6762     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6763     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
6765     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6766     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
6767     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
6768     gtk_toolbar_append_widget( tbl, row, "", "" );
6771     //watch selection
6772     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
6774     sigc::connection *c_selection_changed =
6775         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
6776                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
6777     pool->add_connection ("selection-changed", c_selection_changed);
6779     sigc::connection *c_selection_modified =
6780         new sigc::connection (sp_desktop_selection (desktop)->connectModified
6781                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
6782     pool->add_connection ("selection-modified", c_selection_modified);
6784     sigc::connection *c_subselection_changed =
6785         new sigc::connection (desktop->connectToolSubselectionChanged
6786                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
6787     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
6789     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
6792     gtk_widget_show_all( GTK_WIDGET(tbl) );
6794     return GTK_WIDGET(tbl);
6795 } // end of sp_text_toolbox_new()
6797 }//<unnamed> namespace
6800 //#########################
6801 //##      Connector      ##
6802 //#########################
6804 static void sp_connector_path_set_avoid(void)
6806     cc_selection_set_avoid(true);
6810 static void sp_connector_path_set_ignore(void)
6812     cc_selection_set_avoid(false);
6817 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
6819     // quit if run by the _changed callbacks
6820     if (g_object_get_data( tbl, "freeze" )) {
6821         return;
6822     }
6824     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
6825     SPDocument *doc = sp_desktop_document(desktop);
6827     if (!sp_document_get_undo_sensitive(doc))
6828     {
6829         return;
6830     }
6832     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6834     if ( repr->attribute("inkscape:connector-spacing") ) {
6835         gdouble priorValue = gtk_adjustment_get_value(adj);
6836         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
6837         if ( priorValue == gtk_adjustment_get_value(adj) ) {
6838             return;
6839         }
6840     } else if ( adj->value == defaultConnSpacing ) {
6841         return;
6842     }
6844     // in turn, prevent callbacks from responding
6845     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6847     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
6848     SP_OBJECT(desktop->namedview)->updateRepr();
6850     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
6851     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
6852         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
6853         Geom::Matrix m = Geom::identity();
6854         avoid_item_move(&m, item);
6855     }
6857     if (items) {
6858         g_slist_free(items);
6859     }
6861     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
6862             _("Change connector spacing"));
6864     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6866     spinbutton_defocus(GTK_OBJECT(tbl));
6869 static void sp_connector_graph_layout(void)
6871     if (!SP_ACTIVE_DESKTOP) return;
6872     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6874     // hack for clones, see comment in align-and-distribute.cpp
6875     int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
6876     prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
6878     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
6880     prefs->setInt("/options/clonecompensation/value", saved_compensation);
6882     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
6885 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6887     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6888     prefs->setBool("/tools/connector/directedlayout",
6889                 gtk_toggle_action_get_active( act ));
6892 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6894     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6895     prefs->setBool("/tools/connector/avoidoverlaplayout",
6896                 gtk_toggle_action_get_active( act ));
6900 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
6902     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6903     prefs->setDouble("/tools/connector/length", adj->value);
6904     spinbutton_defocus(GTK_OBJECT(tbl));
6907 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
6908                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
6909                                             bool /*is_interactive*/, gpointer data)
6911     GtkWidget *tbl = GTK_WIDGET(data);
6913     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6914         return;
6915     }
6916     if (strcmp(name, "inkscape:connector-spacing") != 0) {
6917         return;
6918     }
6920     GtkAdjustment *adj = (GtkAdjustment*)
6921             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
6922     gdouble spacing = defaultConnSpacing;
6923     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
6925     gtk_adjustment_set_value(adj, spacing);
6929 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
6930     NULL, /* child_added */
6931     NULL, /* child_removed */
6932     connector_tb_event_attr_changed,
6933     NULL, /* content_changed */
6934     NULL  /* order_changed */
6935 };
6938 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
6940     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6941     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
6943     {
6944         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
6945                                           _("Avoid"),
6946                                           _("Make connectors avoid selected objects"),
6947                                           INKSCAPE_ICON_CONNECTOR_AVOID,
6948                                           secondarySize );
6949         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
6950         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6951     }
6953     {
6954         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
6955                                           _("Ignore"),
6956                                           _("Make connectors ignore selected objects"),
6957                                           INKSCAPE_ICON_CONNECTOR_IGNORE,
6958                                           secondarySize );
6959         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
6960         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6961     }
6963     EgeAdjustmentAction* eact = 0;
6965     // Spacing spinbox
6966     eact = create_adjustment_action( "ConnectorSpacingAction",
6967                                      _("Connector Spacing"), _("Spacing:"),
6968                                      _("The amount of space left around objects by auto-routing connectors"),
6969                                      "/tools/connector/spacing", defaultConnSpacing,
6970                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
6971                                      0, 100, 1.0, 10.0,
6972                                      0, 0, 0,
6973                                      connector_spacing_changed, 1, 0 );
6974     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6976     // Graph (connector network) layout
6977     {
6978         InkAction* inky = ink_action_new( "ConnectorGraphAction",
6979                                           _("Graph"),
6980                                           _("Nicely arrange selected connector network"),
6981                                           INKSCAPE_ICON_DISTRIBUTE_GRAPH,
6982                                           secondarySize );
6983         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
6984         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6985     }
6987     // Default connector length spinbox
6988     eact = create_adjustment_action( "ConnectorLengthAction",
6989                                      _("Connector Length"), _("Length:"),
6990                                      _("Ideal length for connectors when layout is applied"),
6991                                      "/tools/connector/length", 100,
6992                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
6993                                      10, 1000, 10.0, 100.0,
6994                                      0, 0, 0,
6995                                      connector_length_changed, 1, 0 );
6996     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6999     // Directed edges toggle button
7000     {
7001         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
7002                                                       _("Downwards"),
7003                                                       _("Make connectors with end-markers (arrows) point downwards"),
7004                                                       INKSCAPE_ICON_DISTRIBUTE_GRAPH_DIRECTED,
7005                                                       Inkscape::ICON_SIZE_DECORATION );
7006         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7008         bool tbuttonstate = prefs->getBool("/tools/connector/directedlayout");
7009         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7011         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
7012     }
7014     // Avoid overlaps toggle button
7015     {
7016         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
7017                                                       _("Remove overlaps"),
7018                                                       _("Do not allow overlapping shapes"),
7019                                                       INKSCAPE_ICON_DISTRIBUTE_REMOVE_OVERLAPS,
7020                                                       Inkscape::ICON_SIZE_DECORATION );
7021         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7023         bool tbuttonstate = prefs->getBool("/tools/connector/avoidoverlaplayout");
7024         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), (tbuttonstate ? TRUE : FALSE ));
7026         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
7027     }
7029     // Code to watch for changes to the connector-spacing attribute in
7030     // the XML.
7031     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7032     g_assert(repr != NULL);
7034     purge_repr_listener( holder, holder );
7036     if (repr) {
7037         g_object_set_data( holder, "repr", repr );
7038         Inkscape::GC::anchor(repr);
7039         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
7040         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
7041     }
7042 } // end of sp_connector_toolbox_prep()
7045 //#########################
7046 //##     Paintbucket     ##
7047 //#########################
7049 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
7051     gint channels = ege_select_one_action_get_active( act );
7052     flood_channels_set_channels( channels );
7055 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
7057     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7058     prefs->setInt("/tools/paintbucket/threshold", (gint)adj->value);
7061 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
7063     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7064     prefs->setBool("/tools/paintbucket/autogap", ege_select_one_action_get_active( act ));
7067 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
7069     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
7070     SPUnit const *unit = tracker->getActiveUnit();
7071     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7073     prefs->setDouble("/tools/paintbucket/offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
7074     prefs->setString("/tools/paintbucket/offsetunits", sp_unit_get_abbreviation(unit));
7077 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
7079     // FIXME: make defaults settable via Inkscape Options
7080     struct KeyValue {
7081         char const *key;
7082         double value;
7083     } const key_values[] = {
7084         {"threshold", 15},
7085         {"offset", 0.0}
7086     };
7088     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
7089         KeyValue const &kv = key_values[i];
7090         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
7091         if ( adj ) {
7092             gtk_adjustment_set_value(adj, kv.value);
7093         }
7094     }
7096     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
7097     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
7098     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
7099     ege_select_one_action_set_active( autogap_action, 0 );
7102 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
7104     EgeAdjustmentAction* eact = 0;
7105     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7107     {
7108         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7110         GList* items = 0;
7111         gint count = 0;
7112         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
7113         {
7114             GtkTreeIter iter;
7115             gtk_list_store_append( model, &iter );
7116             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7117             count++;
7118         }
7119         g_list_free( items );
7120         items = 0;
7121         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
7122         g_object_set( act1, "short_label", _("Fill by:"), NULL );
7123         ege_select_one_action_set_appearance( act1, "compact" );
7124         ege_select_one_action_set_active( act1, prefs->getInt("/tools/paintbucket/channels", 0) );
7125         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
7126         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
7127         g_object_set_data( holder, "channels_action", act1 );
7128     }
7130     // Spacing spinbox
7131     {
7132         eact = create_adjustment_action(
7133             "ThresholdAction",
7134             _("Fill Threshold"), _("Threshold:"),
7135             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
7136             "/tools/paintbucket/threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
7137             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
7138             0, 0, 0,
7139             paintbucket_threshold_changed, 1, 0 );
7141         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
7142         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7143     }
7145     // Create the units menu.
7146     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
7147     Glib::ustring stored_unit = prefs->getString("/tools/paintbucket/offsetunits");
7148     if (!stored_unit.empty())
7149         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit.data()));
7150     g_object_set_data( holder, "tracker", tracker );
7151     {
7152         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
7153         gtk_action_group_add_action( mainActions, act );
7154     }
7156     // Offset spinbox
7157     {
7158         eact = create_adjustment_action(
7159             "OffsetAction",
7160             _("Grow/shrink by"), _("Grow/shrink by:"),
7161             _("The amount to grow (positive) or shrink (negative) the created fill path"),
7162             "/tools/paintbucket/offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
7163             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
7164             0, 0, 0,
7165             paintbucket_offset_changed, 1, 2);
7166         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
7168         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7169     }
7171     /* Auto Gap */
7172     {
7173         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7175         GList* items = 0;
7176         gint count = 0;
7177         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
7178         {
7179             GtkTreeIter iter;
7180             gtk_list_store_append( model, &iter );
7181             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7182             count++;
7183         }
7184         g_list_free( items );
7185         items = 0;
7186         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
7187         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
7188         ege_select_one_action_set_appearance( act2, "compact" );
7189         ege_select_one_action_set_active( act2, prefs->getBool("/tools/paintbucket/autogap") );
7190         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
7191         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
7192         g_object_set_data( holder, "autogap_action", act2 );
7193     }
7195     /* Reset */
7196     {
7197         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
7198                                           _("Defaults"),
7199                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
7200                                           GTK_STOCK_CLEAR );
7201         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
7202         gtk_action_group_add_action( mainActions, act );
7203         gtk_action_set_sensitive( act, TRUE );
7204     }
7208 /*
7209   Local Variables:
7210   mode:c++
7211   c-file-style:"stroustrup"
7212   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
7213   indent-tabs-mode:nil
7214   fill-column:99
7215   End:
7216 */
7217 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :