Code

Switch to widget state for painting
[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);
1890         if (dt == NULL || nv == NULL) {
1891                 g_warning("No desktop or namedview specified (in toggle_snap_callback)!");
1892                 return;
1893         }
1895         Inkscape::XML::Node *repr = SP_OBJECT_REPR(nv);
1897         if (repr == NULL) {
1898                 g_warning("This namedview doesn't have a xml representation attached!");
1899                 return;
1900         }
1902         bool v = false;
1903         SPAttributeEnum attr = (SPAttributeEnum) GPOINTER_TO_INT(g_object_get_data(G_OBJECT(act), "SP_ATTR_INKSCAPE"));
1905         switch (attr) {
1906         case SP_ATTR_INKSCAPE_SNAP_GLOBAL:
1907                 dt->toggleSnapGlobal();
1908                 break;
1909         case SP_ATTR_INKSCAPE_SNAP_BBOX:
1910                 v = nv->snap_manager.snapprefs.getSnapModeBBox();
1911                 sp_repr_set_boolean(repr, "inkscape:snap-bbox", !v);
1912                 break;
1913         case SP_ATTR_INKSCAPE_BBOX_PATHS:
1914                 v = nv->snap_manager.snapprefs.getSnapToBBoxPath();
1915                 sp_repr_set_boolean(repr, "inkscape:bbox-paths", !v);
1916                 break;
1917         case SP_ATTR_INKSCAPE_BBOX_NODES:
1918                 v = nv->snap_manager.snapprefs.getSnapToBBoxNode();
1919                 sp_repr_set_boolean(repr, "inkscape:bbox-nodes", !v);
1920                 break;
1921         case SP_ATTR_INKSCAPE_SNAP_NODES:
1922                 v = nv->snap_manager.snapprefs.getSnapModeNode();
1923                 sp_repr_set_boolean(repr, "inkscape:snap-nodes", !v);
1924                 break;
1925         case SP_ATTR_INKSCAPE_OBJECT_PATHS:
1926                 v = nv->snap_manager.snapprefs.getSnapToItemPath();
1927                 sp_repr_set_boolean(repr, "inkscape:object-paths", !v);
1928                 break;
1929         case SP_ATTR_INKSCAPE_OBJECT_NODES:
1930                 v = nv->snap_manager.snapprefs.getSnapToItemNode();
1931                 sp_repr_set_boolean(repr, "inkscape:object-nodes", !v);
1932                 break;
1933         case SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES:
1934                 v = nv->snap_manager.snapprefs.getSnapSmoothNodes();
1935                 sp_repr_set_boolean(repr, "inkscape:snap-smooth-nodes", !v);
1936                 break;
1937         case SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS:
1938                 v = nv->snap_manager.snapprefs.getSnapIntersectionCS();
1939                 sp_repr_set_boolean(repr, "inkscape:snap-intersection-paths", !v);
1940                 break;
1941         case SP_ATTR_INKSCAPE_SNAP_CENTER:
1942                 v = nv->snap_manager.snapprefs.getIncludeItemCenter();
1943                 sp_repr_set_boolean(repr, "inkscape:snap-center", !v);
1944                 break;
1945         case SP_ATTR_INKSCAPE_SNAP_GRIDS:
1946                 v = nv->snap_manager.snapprefs.getSnapToGrids();
1947                 sp_repr_set_boolean(repr, "inkscape:snap-grids", !v);
1948                 break;
1949         case SP_ATTR_INKSCAPE_SNAP_TO_GUIDES:
1950                 v = nv->snap_manager.snapprefs.getSnapToGuides();
1951                 sp_repr_set_boolean(repr, "inkscape:snap-to-guides", !v);
1952                 break;
1953         case SP_ATTR_INKSCAPE_SNAP_PAGE:
1954                 v = nv->snap_manager.snapprefs.getSnapToPageBorder();
1955                 sp_repr_set_boolean(repr, "inkscape:snap-page", !v);
1956                 break;
1957         /*case SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE:
1958                 v = nv->snap_manager.snapprefs.getSnapIntersectionGG();
1959                 sp_repr_set_boolean(repr, "inkscape:snap-intersection-grid-guide", !v);
1960                 break;*/
1961         case SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS:
1962                 v = nv->snap_manager.snapprefs.getSnapLineMidpoints();
1963                 sp_repr_set_boolean(repr, "inkscape:snap-midpoints", !v);
1964                 break;
1965         case SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS:
1966                 v = nv->snap_manager.snapprefs.getSnapObjectMidpoints();
1967                 sp_repr_set_boolean(repr, "inkscape:snap-object-midpoints", !v);
1968                 break;
1969         case SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS:
1970                 v = nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints();
1971                 sp_repr_set_boolean(repr, "inkscape:snap-bbox-edge-midpoints", !v);
1972                 break;
1973         case SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS:
1974                 v = nv->snap_manager.snapprefs.getSnapBBoxMidpoints();
1975                 sp_repr_set_boolean(repr, "inkscape:snap-bbox-midpoints", !v);
1976                 break;
1977         default:
1978                 g_warning("toggle_snap_callback has been called with an ID for which no action has been defined");
1979                 break;
1980         }
1982         // The snapping preferences are stored in the document, and therefore toggling makes the document dirty
1983         SPDocument *doc = SP_OBJECT_DOCUMENT(nv);
1984         doc->setModifiedSinceSave();
1987 void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1989         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1990         Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
1992         gchar const * descr =
1993                 "<ui>"
1994                 "  <toolbar name='SnapToolbar'>"
1995                 "    <toolitem action='ToggleSnapGlobal' />"
1996                         "    <separator />"
1997                         "    <toolitem action='ToggleSnapFromBBoxCorner' />"
1998                         "    <toolitem action='ToggleSnapToBBoxPath' />"
1999                         "    <toolitem action='ToggleSnapToBBoxNode' />"
2000                         "    <toolitem action='ToggleSnapToFromBBoxEdgeMidpoints' />"
2001                         "    <toolitem action='ToggleSnapToFromBBoxCenters' />"
2002                         "    <separator />"
2003                         "    <toolitem action='ToggleSnapFromNode' />"
2004                         "    <toolitem action='ToggleSnapToItemPath' />"
2005                         "    <toolitem action='ToggleSnapToPathIntersections' />"
2006                         "    <toolitem action='ToggleSnapToItemNode' />"
2007                         "    <toolitem action='ToggleSnapToSmoothNodes' />"
2008                         "    <toolitem action='ToggleSnapToFromLineMidpoints' />"
2009                         "    <toolitem action='ToggleSnapToFromObjectCenters' />"
2010                         "    <toolitem action='ToggleSnapToFromRotationCenter' />"
2011                         "    <separator />"
2012                         "    <toolitem action='ToggleSnapToPageBorder' />"
2013                         "    <toolitem action='ToggleSnapToGrids' />"
2014                         "    <toolitem action='ToggleSnapToGuides' />"
2015                   //"    <toolitem action='ToggleSnapToGridGuideIntersections' />"
2016                         "  </toolbar>"
2017                 "</ui>";
2019         Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
2021         {
2022                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapGlobal",
2023                     _("Snap"), _("Enable snapping"), INKSCAPE_ICON_SNAP, secondarySize,
2024                     SP_ATTR_INKSCAPE_SNAP_GLOBAL);
2026                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2027                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2028         }
2030         {
2031                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromBBoxCorner",
2032                     _("Bounding box"), _("Snap bounding box corners"), INKSCAPE_ICON_SNAP_BOUNDING_BOX,
2033                     secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX);
2035                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2036                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2037         }
2039         {
2040                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxPath",
2041                     _("Bounding box edges"), _("Snap to edges of a bounding box"),
2042                     INKSCAPE_ICON_SNAP_BOUNDING_BOX_EDGES, secondarySize, SP_ATTR_INKSCAPE_BBOX_PATHS);
2044                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2045                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2046         }
2048         {
2049                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxNode",
2050                     _("Bounding box corners"), _("Snap to bounding box corners"),
2051                     INKSCAPE_ICON_SNAP_BOUNDING_BOX_CORNERS, secondarySize, SP_ATTR_INKSCAPE_BBOX_NODES);
2053                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2054                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2055         }
2057         {
2058                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxEdgeMidpoints",
2059                     _("BBox Edge Midpoints"), _("Snap from and to midpoints of bounding box edges"),
2060                     INKSCAPE_ICON_SNAP_BOUNDING_BOX_MIDPOINTS, secondarySize,
2061                     SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS);
2063                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2064                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2065         }
2067         {
2068                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxCenters",
2069                     _("BBox Centers"), _("Snapping from and to centers of bounding boxes"),
2070                     INKSCAPE_ICON_SNAP_BOUNDING_BOX_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS);
2072                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2073                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2074         }
2076         {
2077                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromNode",
2078                     _("Nodes"), _("Snap nodes or handles"), INKSCAPE_ICON_SNAP_NODES, secondarySize, SP_ATTR_INKSCAPE_SNAP_NODES);
2080                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2081                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2082         }
2084         {
2085                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemPath",
2086                     _("Paths"), _("Snap to paths"), INKSCAPE_ICON_SNAP_NODES_PATH, secondarySize,
2087                     SP_ATTR_INKSCAPE_OBJECT_PATHS);
2089                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2090                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2091         }
2093         {
2094                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPathIntersections",
2095                     _("Path intersections"), _("Snap to path intersections"),
2096                     INKSCAPE_ICON_SNAP_NODES_INTERSECTION, secondarySize, SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS);
2098                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2099                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2100         }
2102         {
2103                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemNode",
2104                     _("To nodes"), _("Snap to cusp nodes"), INKSCAPE_ICON_SNAP_NODES_CUSP, secondarySize,
2105                     SP_ATTR_INKSCAPE_OBJECT_NODES);
2107                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2108                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2109         }
2111         {
2112                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToSmoothNodes",
2113                     _("Smooth nodes"), _("Snap to smooth nodes"), INKSCAPE_ICON_SNAP_NODES_SMOOTH,
2114                     secondarySize, SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES);
2116                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2117                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2118         }
2120         {
2121                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromLineMidpoints",
2122                     _("Line Midpoints"), _("Snap from and to midpoints of line segments"),
2123                     INKSCAPE_ICON_SNAP_NODES_MIDPOINT, secondarySize, SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS);
2125                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2126                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2127         }
2129         {
2130                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromObjectCenters",
2131                     _("Object Centers"), _("Snap from and to centers of objects"),
2132                     INKSCAPE_ICON_SNAP_NODES_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS);
2134                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2135                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2136         }
2138         {
2139                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromRotationCenter",
2140                     _("Rotation Centers"), _("Snap from and to an item's rotation center"),
2141                     INKSCAPE_ICON_SNAP_NODES_ROTATION_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_CENTER);
2143                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2144                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2145         }
2147         {
2148                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPageBorder",
2149                     _("Page border"), _("Snap to the page border"), INKSCAPE_ICON_SNAP_PAGE,
2150                     secondarySize, SP_ATTR_INKSCAPE_SNAP_PAGE);
2152                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2153                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2154         }
2156         {
2157                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGrids",
2158                     _("Grids"), _("Snap to grids"), INKSCAPE_ICON_GRID_RECTANGULAR, secondarySize,
2159                     SP_ATTR_INKSCAPE_SNAP_GRIDS);
2161                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2162                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2163         }
2165         {
2166                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGuides",
2167                     _("Guides"), _("Snap to guides"), INKSCAPE_ICON_GUIDES, secondarySize,
2168                     SP_ATTR_INKSCAPE_SNAP_TO_GUIDES);
2170                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2171                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2172         }
2174         /*{
2175                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGridGuideIntersections",
2176                     _("Grid/guide intersections"), _("Snap to intersections of a grid with a guide"),
2177                     INKSCAPE_ICON_SNAP_GRID_GUIDE_INTERSECTIONS, secondarySize,
2178                     SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE);
2180                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2181                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2182         }*/
2184     GtkUIManager* mgr = gtk_ui_manager_new();
2185     GError* errVal = 0;
2187     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
2188     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
2190     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/SnapToolbar" );
2191     if ( prefs->getBool("/toolbox/icononly", true) ) {
2192         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
2193     }
2195     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/secondary");
2196     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
2198     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
2199     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
2201     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
2203     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
2204     if ( child ) {
2205         gtk_container_remove( GTK_CONTAINER(toolbox), child );
2206     }
2208     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
2212 void update_snap_toolbox(SPDesktop *desktop, SPEventContext */*eventcontext*/, GtkWidget *toolbox)
2214         g_assert(desktop != NULL);
2215         g_assert(toolbox != NULL);
2217         SPNamedView *nv = sp_desktop_namedview(desktop);
2218         if (nv == NULL) {
2219                 g_warning("Namedview cannot be retrieved (in update_snap_toolbox)!");
2220                 return;
2221         }
2223         Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2225         Glib::RefPtr<Gtk::Action> act1 = mainActions->get_action("ToggleSnapGlobal");
2226         Glib::RefPtr<Gtk::Action> act2 = mainActions->get_action("ToggleSnapFromBBoxCorner");
2227         Glib::RefPtr<Gtk::Action> act3 = mainActions->get_action("ToggleSnapToBBoxPath");
2228         Glib::RefPtr<Gtk::Action> act4 = mainActions->get_action("ToggleSnapToBBoxNode");
2229         Glib::RefPtr<Gtk::Action> act4b = mainActions->get_action("ToggleSnapToFromBBoxEdgeMidpoints");
2230         Glib::RefPtr<Gtk::Action> act4c = mainActions->get_action("ToggleSnapToFromBBoxCenters");
2231         Glib::RefPtr<Gtk::Action> act5 = mainActions->get_action("ToggleSnapFromNode");
2232         Glib::RefPtr<Gtk::Action> act6 = mainActions->get_action("ToggleSnapToItemPath");
2233         Glib::RefPtr<Gtk::Action> act6b = mainActions->get_action("ToggleSnapToPathIntersections");
2234         Glib::RefPtr<Gtk::Action> act7 = mainActions->get_action("ToggleSnapToItemNode");
2235         Glib::RefPtr<Gtk::Action> act8 = mainActions->get_action("ToggleSnapToSmoothNodes");
2236         Glib::RefPtr<Gtk::Action> act9 = mainActions->get_action("ToggleSnapToFromLineMidpoints");
2237         Glib::RefPtr<Gtk::Action> act10 = mainActions->get_action("ToggleSnapToFromObjectCenters");
2238         Glib::RefPtr<Gtk::Action> act11 = mainActions->get_action("ToggleSnapToFromRotationCenter");
2239         Glib::RefPtr<Gtk::Action> act12 = mainActions->get_action("ToggleSnapToPageBorder");
2240         //Glib::RefPtr<Gtk::Action> act13 = mainActions->get_action("ToggleSnapToGridGuideIntersections");
2241         Glib::RefPtr<Gtk::Action> act14 = mainActions->get_action("ToggleSnapToGrids");
2242         Glib::RefPtr<Gtk::Action> act15 = mainActions->get_action("ToggleSnapToGuides");
2245         if (!act1) {
2246                 return; // The snap actions haven't been defined yet (might be the case during startup)
2247         }
2249         // The ..._set_active calls below will toggle the buttons, but this shouldn't lead to
2250         // changes in our document because we're only updating the UI;
2251         // Setting the "freeze" parameter to true will block the code in toggle_snap_callback()
2252         g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(TRUE));
2254         bool const c1 = nv->snap_manager.snapprefs.getSnapEnabledGlobally();
2255         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act1->gobj()), c1);
2257         bool const c2 = nv->snap_manager.snapprefs.getSnapModeBBox();
2258         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act2->gobj()), c2);
2259         gtk_action_set_sensitive(GTK_ACTION(act2->gobj()), c1);
2261         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act3->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxPath());
2262         gtk_action_set_sensitive(GTK_ACTION(act3->gobj()), c1 && c2);
2263         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxNode());
2264         gtk_action_set_sensitive(GTK_ACTION(act4->gobj()), c1 && c2);
2265         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4b->gobj()), nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints());
2266         gtk_action_set_sensitive(GTK_ACTION(act4b->gobj()), c1 && c2);
2267         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4c->gobj()), nv->snap_manager.snapprefs.getSnapBBoxMidpoints());
2268         gtk_action_set_sensitive(GTK_ACTION(act4c->gobj()), c1 && c2);
2270         bool const c3 = nv->snap_manager.snapprefs.getSnapModeNode();
2271         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act5->gobj()), c3);
2272         gtk_action_set_sensitive(GTK_ACTION(act5->gobj()), c1);
2274         bool const c4 = nv->snap_manager.snapprefs.getSnapToItemPath();
2275         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6->gobj()), c4);
2276         gtk_action_set_sensitive(GTK_ACTION(act6->gobj()), c1 && c3);
2277         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6b->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionCS());
2278         gtk_action_set_sensitive(GTK_ACTION(act6b->gobj()), c1 && c3 && c4);
2279         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act7->gobj()), nv->snap_manager.snapprefs.getSnapToItemNode());
2280         gtk_action_set_sensitive(GTK_ACTION(act7->gobj()), c1 && c3);
2281         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act8->gobj()), nv->snap_manager.snapprefs.getSnapSmoothNodes());
2282         gtk_action_set_sensitive(GTK_ACTION(act8->gobj()), c1 && c3);
2283         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act9->gobj()), nv->snap_manager.snapprefs.getSnapLineMidpoints());
2284         gtk_action_set_sensitive(GTK_ACTION(act9->gobj()), c1 && c3);
2285         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act10->gobj()), nv->snap_manager.snapprefs.getSnapObjectMidpoints());
2286         gtk_action_set_sensitive(GTK_ACTION(act10->gobj()), c1 && c3);
2287         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act11->gobj()), nv->snap_manager.snapprefs.getIncludeItemCenter());
2288         gtk_action_set_sensitive(GTK_ACTION(act11->gobj()), c1 && c3);
2290         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act12->gobj()), nv->snap_manager.snapprefs.getSnapToPageBorder());
2291         gtk_action_set_sensitive(GTK_ACTION(act12->gobj()), c1);
2292         //gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act13->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionGG());
2293         //gtk_action_set_sensitive(GTK_ACTION(act13->gobj()), c1);
2295         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act14->gobj()), nv->snap_manager.snapprefs.getSnapToGrids());
2296         gtk_action_set_sensitive(GTK_ACTION(act14->gobj()), c1);
2297         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act15->gobj()), nv->snap_manager.snapprefs.getSnapToGuides());
2298         gtk_action_set_sensitive(GTK_ACTION(act15->gobj()), c1);
2301         g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(FALSE)); // unfreeze (see above)
2304 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
2306     gtk_widget_show(toolbox_toplevel);
2307     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
2309     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
2310     if (!shown_toolbox) {
2311         return;
2312     }
2313     gtk_widget_show(toolbox);
2315     gtk_widget_show_all(shown_toolbox);
2318 static GtkWidget *
2319 sp_empty_toolbox_new(SPDesktop *desktop)
2321     GtkWidget *tbl = gtk_toolbar_new();
2322     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
2323     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
2325     gtk_widget_show_all(tbl);
2326     sp_set_font_size_smaller (tbl);
2328     return tbl;
2331 #define MODE_LABEL_WIDTH 70
2333 //########################
2334 //##       Star         ##
2335 //########################
2337 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2339     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2341     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2342         // do not remember prefs if this call is initiated by an undo change, because undoing object
2343         // creation sets bogus values to its attributes before it is deleted
2344         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2345         prefs->setInt("/tools/shapes/star/magnitude", (gint)adj->value);
2346     }
2348     // quit if run by the attr_changed listener
2349     if (g_object_get_data( dataKludge, "freeze" )) {
2350         return;
2351     }
2353     // in turn, prevent listener from responding
2354     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2356     bool modmade = false;
2358     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2359     GSList const *items = selection->itemList();
2360     for (; items != NULL; items = items->next) {
2361         if (SP_IS_STAR((SPItem *) items->data)) {
2362             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2363             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
2364             sp_repr_set_svg_double(repr, "sodipodi:arg2",
2365                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
2366                                     + M_PI / (gint)adj->value));
2367             SP_OBJECT((SPItem *) items->data)->updateRepr();
2368             modmade = true;
2369         }
2370     }
2371     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2372                                    _("Star: Change number of corners"));
2374     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2377 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2379     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2381     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2382         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2383         prefs->setDouble("/tools/shapes/star/proportion", adj->value);
2384     }
2386     // quit if run by the attr_changed listener
2387     if (g_object_get_data( dataKludge, "freeze" )) {
2388         return;
2389     }
2391     // in turn, prevent listener from responding
2392     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2394     bool modmade = false;
2395     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2396     GSList const *items = selection->itemList();
2397     for (; items != NULL; items = items->next) {
2398         if (SP_IS_STAR((SPItem *) items->data)) {
2399             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2401             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2402             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2403             if (r2 < r1) {
2404                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
2405             } else {
2406                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
2407             }
2409             SP_OBJECT((SPItem *) items->data)->updateRepr();
2410             modmade = true;
2411         }
2412     }
2414     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2415                                    _("Star: Change spoke ratio"));
2417     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2420 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
2422     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2423     bool flat = ege_select_one_action_get_active( act ) == 0;
2425     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2426         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2427         prefs->setBool( "/tools/shapes/star/isflatsided", flat);
2428     }
2430     // quit if run by the attr_changed listener
2431     if (g_object_get_data( dataKludge, "freeze" )) {
2432         return;
2433     }
2435     // in turn, prevent listener from responding
2436     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2438     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2439     GSList const *items = selection->itemList();
2440     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2441     bool modmade = false;
2443     if ( prop_action ) {
2444         gtk_action_set_sensitive( prop_action, !flat );
2445     }
2447     for (; items != NULL; items = items->next) {
2448         if (SP_IS_STAR((SPItem *) items->data)) {
2449             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2450             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
2451             SP_OBJECT((SPItem *) items->data)->updateRepr();
2452             modmade = true;
2453         }
2454     }
2456     if (modmade) {
2457         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2458                          flat ? _("Make polygon") : _("Make star"));
2459     }
2461     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2464 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2466     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2468     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2469         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2470         prefs->setDouble("/tools/shapes/star/rounded", (gdouble) adj->value);
2471     }
2473     // quit if run by the attr_changed listener
2474     if (g_object_get_data( dataKludge, "freeze" )) {
2475         return;
2476     }
2478     // in turn, prevent listener from responding
2479     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2481     bool modmade = false;
2483     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2484     GSList const *items = selection->itemList();
2485     for (; items != NULL; items = items->next) {
2486         if (SP_IS_STAR((SPItem *) items->data)) {
2487             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2488             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2489             SP_OBJECT(items->data)->updateRepr();
2490             modmade = true;
2491         }
2492     }
2493     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2494                                    _("Star: Change rounding"));
2496     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2499 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2501     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2503     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2504         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2505         prefs->setDouble("/tools/shapes/star/randomized", (gdouble) adj->value);
2506     }
2508     // quit if run by the attr_changed listener
2509     if (g_object_get_data( dataKludge, "freeze" )) {
2510         return;
2511     }
2513     // in turn, prevent listener from responding
2514     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2516     bool modmade = false;
2518     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2519     GSList const *items = selection->itemList();
2520     for (; items != NULL; items = items->next) {
2521         if (SP_IS_STAR((SPItem *) items->data)) {
2522             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2523             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2524             SP_OBJECT(items->data)->updateRepr();
2525             modmade = true;
2526         }
2527     }
2528     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2529                                    _("Star: Change randomization"));
2531     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2535 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2536                                        gchar const */*old_value*/, gchar const */*new_value*/,
2537                                        bool /*is_interactive*/, gpointer data)
2539     GtkWidget *tbl = GTK_WIDGET(data);
2541     // quit if run by the _changed callbacks
2542     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2543         return;
2544     }
2546     // in turn, prevent callbacks from responding
2547     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2549     GtkAdjustment *adj = 0;
2551     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2552     bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2554     if (!strcmp(name, "inkscape:randomized")) {
2555         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2556         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2557     } else if (!strcmp(name, "inkscape:rounded")) {
2558         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2559         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2560     } else if (!strcmp(name, "inkscape:flatsided")) {
2561         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2562         char const *flatsides = repr->attribute("inkscape:flatsided");
2563         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2564         if ( flatsides && !strcmp(flatsides,"false") ) {
2565             ege_select_one_action_set_active( flat_action, 1 );
2566             gtk_action_set_sensitive( prop_action, TRUE );
2567         } else {
2568             ege_select_one_action_set_active( flat_action, 0 );
2569             gtk_action_set_sensitive( prop_action, FALSE );
2570         }
2571     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2572         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2573         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2574         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2575         if (r2 < r1) {
2576             gtk_adjustment_set_value(adj, r2/r1);
2577         } else {
2578             gtk_adjustment_set_value(adj, r1/r2);
2579         }
2580     } else if (!strcmp(name, "sodipodi:sides")) {
2581         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2582         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2583     }
2585     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2589 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2591     NULL, /* child_added */
2592     NULL, /* child_removed */
2593     star_tb_event_attr_changed,
2594     NULL, /* content_changed */
2595     NULL  /* order_changed */
2596 };
2599 /**
2600  *  \param selection Should not be NULL.
2601  */
2602 static void
2603 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2605     int n_selected = 0;
2606     Inkscape::XML::Node *repr = NULL;
2608     purge_repr_listener( tbl, tbl );
2610     for (GSList const *items = selection->itemList();
2611          items != NULL;
2612          items = items->next)
2613     {
2614         if (SP_IS_STAR((SPItem *) items->data)) {
2615             n_selected++;
2616             repr = SP_OBJECT_REPR((SPItem *) items->data);
2617         }
2618     }
2620     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2622     if (n_selected == 0) {
2623         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2624     } else if (n_selected == 1) {
2625         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2627         if (repr) {
2628             g_object_set_data( tbl, "repr", repr );
2629             Inkscape::GC::anchor(repr);
2630             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2631             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2632         }
2633     } else {
2634         // FIXME: implement averaging of all parameters for multiple selected stars
2635         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2636         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2637     }
2641 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2643     // FIXME: in this and all other _default functions, set some flag telling the value_changed
2644     // callbacks to lump all the changes for all selected objects in one undo step
2646     GtkAdjustment *adj = 0;
2648     // fixme: make settable in prefs!
2649     gint mag = 5;
2650     gdouble prop = 0.5;
2651     gboolean flat = FALSE;
2652     gdouble randomized = 0;
2653     gdouble rounded = 0;
2655     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2656     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2658     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2659     gtk_action_set_sensitive( sb2, !flat );
2661     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2662     gtk_adjustment_set_value(adj, mag);
2663     gtk_adjustment_value_changed(adj);
2665     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2666     gtk_adjustment_set_value(adj, prop);
2667     gtk_adjustment_value_changed(adj);
2669     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2670     gtk_adjustment_set_value(adj, rounded);
2671     gtk_adjustment_value_changed(adj);
2673     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2674     gtk_adjustment_set_value(adj, randomized);
2675     gtk_adjustment_value_changed(adj);
2679 void
2680 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2682     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2683     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2684     GtkWidget *l = gtk_label_new(NULL);
2685     gtk_label_set_markup(GTK_LABEL(l), title);
2686     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2687     if ( GTK_IS_TOOLBAR(tbl) ) {
2688         gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2689     } else {
2690         gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2691     }
2692     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2696 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2698     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
2700     {
2701         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2702         ege_output_action_set_use_markup( act, TRUE );
2703         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2704         g_object_set_data( holder, "mode_action", act );
2705     }
2707     {
2708         EgeAdjustmentAction* eact = 0;
2709         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2710         bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2712         /* Flatsided checkbox */
2713         {
2714             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2716             GtkTreeIter iter;
2717             gtk_list_store_append( model, &iter );
2718             gtk_list_store_set( model, &iter,
2719                                 0, _("Polygon"),
2720                                 1, _("Regular polygon (with one handle) instead of a star"),
2721                                 2, INKSCAPE_ICON_DRAW_POLYGON,
2722                                 -1 );
2724             gtk_list_store_append( model, &iter );
2725             gtk_list_store_set( model, &iter,
2726                                 0, _("Star"),
2727                                 1, _("Star instead of a regular polygon (with one handle)"),
2728                                 2, INKSCAPE_ICON_DRAW_STAR,
2729                                 -1 );
2731             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2732             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2733             g_object_set_data( holder, "flat_action", act );
2735             ege_select_one_action_set_appearance( act, "full" );
2736             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2737             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2738             ege_select_one_action_set_icon_column( act, 2 );
2739             ege_select_one_action_set_icon_size( act, secondarySize );
2740             ege_select_one_action_set_tooltip_column( act, 1  );
2742             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2743             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2744         }
2746         /* Magnitude */
2747         {
2748         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2749         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2750         eact = create_adjustment_action( "MagnitudeAction",
2751                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2752                                          "/tools/shapes/star/magnitude", 3,
2753                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2754                                          3, 1024, 1, 5,
2755                                          labels, values, G_N_ELEMENTS(labels),
2756                                          sp_stb_magnitude_value_changed,
2757                                          1.0, 0 );
2758         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2759         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2760         }
2762         /* Spoke ratio */
2763         {
2764         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2765         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2766         eact = create_adjustment_action( "SpokeAction",
2767                                          _("Spoke ratio"), _("Spoke ratio:"),
2768                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2769                                          // Base radius is the same for the closest handle.
2770                                          _("Base radius to tip radius ratio"),
2771                                          "/tools/shapes/star/proportion", 0.5,
2772                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2773                                          0.01, 1.0, 0.01, 0.1,
2774                                          labels, values, G_N_ELEMENTS(labels),
2775                                          sp_stb_proportion_value_changed );
2776         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2777         g_object_set_data( holder, "prop_action", eact );
2778         }
2780         if ( !isFlatSided ) {
2781             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2782         } else {
2783             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2784         }
2786         /* Roundedness */
2787         {
2788         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2789         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2790         eact = create_adjustment_action( "RoundednessAction",
2791                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2792                                          "/tools/shapes/star/rounded", 0.0,
2793                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2794                                          -10.0, 10.0, 0.01, 0.1,
2795                                          labels, values, G_N_ELEMENTS(labels),
2796                                          sp_stb_rounded_value_changed );
2797         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2798         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2799         }
2801         /* Randomization */
2802         {
2803         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2804         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2805         eact = create_adjustment_action( "RandomizationAction",
2806                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2807                                          "/tools/shapes/star/randomized", 0.0,
2808                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2809                                          -10.0, 10.0, 0.001, 0.01,
2810                                          labels, values, G_N_ELEMENTS(labels),
2811                                          sp_stb_randomized_value_changed, 0.1, 3 );
2812         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2813         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2814         }
2815     }
2817     {
2818         /* Reset */
2819         {
2820             GtkAction* act = gtk_action_new( "StarResetAction",
2821                                              _("Defaults"),
2822                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2823                                              GTK_STOCK_CLEAR );
2824             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2825             gtk_action_group_add_action( mainActions, act );
2826             gtk_action_set_sensitive( act, TRUE );
2827         }
2828     }
2830     sigc::connection *connection = new sigc::connection(
2831         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2832         );
2833     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2834     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2838 //########################
2839 //##       Rect         ##
2840 //########################
2842 static void sp_rtb_sensitivize( GObject *tbl )
2844     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2845     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2846     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2848     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2849         gtk_action_set_sensitive( not_rounded, FALSE );
2850     } else {
2851         gtk_action_set_sensitive( not_rounded, TRUE );
2852     }
2856 static void
2857 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2858                           void (*setter)(SPRect *, gdouble))
2860     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2862     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2863     SPUnit const *unit = tracker->getActiveUnit();
2865     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2866         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2867         prefs->setDouble(Glib::ustring("/tools/shapes/rect/") + value_name, sp_units_get_pixels(adj->value, *unit));
2868     }
2870     // quit if run by the attr_changed listener
2871     if (g_object_get_data( tbl, "freeze" )) {
2872         return;
2873     }
2875     // in turn, prevent listener from responding
2876     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2878     bool modmade = false;
2879     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2880     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2881         if (SP_IS_RECT(items->data)) {
2882             if (adj->value != 0) {
2883                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2884             } else {
2885                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2886             }
2887             modmade = true;
2888         }
2889     }
2891     sp_rtb_sensitivize( tbl );
2893     if (modmade) {
2894         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2895                                    _("Change rectangle"));
2896     }
2898     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2901 static void
2902 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2904     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2907 static void
2908 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2910     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2913 static void
2914 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2916     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2919 static void
2920 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2922     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2927 static void
2928 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2930     GtkAdjustment *adj = 0;
2932     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2933     gtk_adjustment_set_value(adj, 0.0);
2934     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2935     gtk_adjustment_value_changed(adj);
2937     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2938     gtk_adjustment_set_value(adj, 0.0);
2939     gtk_adjustment_value_changed(adj);
2941     sp_rtb_sensitivize( obj );
2944 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2945                                        gchar const */*old_value*/, gchar const */*new_value*/,
2946                                        bool /*is_interactive*/, gpointer data)
2948     GObject *tbl = G_OBJECT(data);
2950     // quit if run by the _changed callbacks
2951     if (g_object_get_data( tbl, "freeze" )) {
2952         return;
2953     }
2955     // in turn, prevent callbacks from responding
2956     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2958     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2959     SPUnit const *unit = tracker->getActiveUnit();
2961     gpointer item = g_object_get_data( tbl, "item" );
2962     if (item && SP_IS_RECT(item)) {
2963         {
2964             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2965             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2966             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2967         }
2969         {
2970             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2971             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2972             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2973         }
2975         {
2976             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2977             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2978             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2979         }
2981         {
2982             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2983             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2984             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2985         }
2986     }
2988     sp_rtb_sensitivize( tbl );
2990     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2994 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2995     NULL, /* child_added */
2996     NULL, /* child_removed */
2997     rect_tb_event_attr_changed,
2998     NULL, /* content_changed */
2999     NULL  /* order_changed */
3000 };
3002 /**
3003  *  \param selection should not be NULL.
3004  */
3005 static void
3006 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3008     int n_selected = 0;
3009     Inkscape::XML::Node *repr = NULL;
3010     SPItem *item = NULL;
3012     if ( g_object_get_data( tbl, "repr" ) ) {
3013         g_object_set_data( tbl, "item", NULL );
3014     }
3015     purge_repr_listener( tbl, tbl );
3017     for (GSList const *items = selection->itemList();
3018          items != NULL;
3019          items = items->next) {
3020         if (SP_IS_RECT((SPItem *) items->data)) {
3021             n_selected++;
3022             item = (SPItem *) items->data;
3023             repr = SP_OBJECT_REPR(item);
3024         }
3025     }
3027     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3029     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3031     if (n_selected == 0) {
3032         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3034         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3035         gtk_action_set_sensitive(w, FALSE);
3036         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3037         gtk_action_set_sensitive(h, FALSE);
3039     } else if (n_selected == 1) {
3040         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3041         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3043         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3044         gtk_action_set_sensitive(w, TRUE);
3045         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3046         gtk_action_set_sensitive(h, TRUE);
3048         if (repr) {
3049             g_object_set_data( tbl, "repr", repr );
3050             g_object_set_data( tbl, "item", item );
3051             Inkscape::GC::anchor(repr);
3052             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
3053             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
3054         }
3055     } else {
3056         // FIXME: implement averaging of all parameters for multiple selected
3057         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3058         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3059         sp_rtb_sensitivize( tbl );
3060     }
3064 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3066     EgeAdjustmentAction* eact = 0;
3067     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3069     {
3070         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
3071         ege_output_action_set_use_markup( act, TRUE );
3072         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3073         g_object_set_data( holder, "mode_action", act );
3074     }
3076     // rx/ry units menu: create
3077     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
3078     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
3079     // fixme: add % meaning per cent of the width/height
3080     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
3081     g_object_set_data( holder, "tracker", tracker );
3083     /* W */
3084     {
3085         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3086         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3087         eact = create_adjustment_action( "RectWidthAction",
3088                                          _("Width"), _("W:"), _("Width of rectangle"),
3089                                          "/tools/shapes/rect/width", 0,
3090                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
3091                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3092                                          labels, values, G_N_ELEMENTS(labels),
3093                                          sp_rtb_width_value_changed );
3094         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3095         g_object_set_data( holder, "width_action", eact );
3096         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3097         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3098     }
3100     /* H */
3101     {
3102         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3103         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3104         eact = create_adjustment_action( "RectHeightAction",
3105                                          _("Height"), _("H:"), _("Height of rectangle"),
3106                                          "/tools/shapes/rect/height", 0,
3107                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3108                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3109                                          labels, values, G_N_ELEMENTS(labels),
3110                                          sp_rtb_height_value_changed );
3111         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3112         g_object_set_data( holder, "height_action", eact );
3113         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3114         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3115     }
3117     /* rx */
3118     {
3119         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3120         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3121         eact = create_adjustment_action( "RadiusXAction",
3122                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
3123                                          "/tools/shapes/rect/rx", 0,
3124                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3125                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3126                                          labels, values, G_N_ELEMENTS(labels),
3127                                          sp_rtb_rx_value_changed);
3128         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3129         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3130     }
3132     /* ry */
3133     {
3134         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3135         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3136         eact = create_adjustment_action( "RadiusYAction",
3137                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
3138                                          "/tools/shapes/rect/ry", 0,
3139                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3140                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3141                                          labels, values, G_N_ELEMENTS(labels),
3142                                          sp_rtb_ry_value_changed);
3143         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3144         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3145     }
3147     // add the units menu
3148     {
3149         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
3150         gtk_action_group_add_action( mainActions, act );
3151     }
3153     /* Reset */
3154     {
3155         InkAction* inky = ink_action_new( "RectResetAction",
3156                                           _("Not rounded"),
3157                                           _("Make corners sharp"),
3158                                           INKSCAPE_ICON_RECTANGLE_MAKE_CORNERS_SHARP,
3159                                           secondarySize );
3160         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
3161         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3162         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3163         g_object_set_data( holder, "not_rounded", inky );
3164     }
3166     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
3167     sp_rtb_sensitivize( holder );
3169     sigc::connection *connection = new sigc::connection(
3170         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
3171         );
3172     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3173     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3176 //########################
3177 //##       3D Box       ##
3178 //########################
3180 // normalize angle so that it lies in the interval [0,360]
3181 static double box3d_normalize_angle (double a) {
3182     double angle = a + ((int) (a/360.0))*360;
3183     if (angle < 0) {
3184         angle += 360.0;
3185     }
3186     return angle;
3189 static void
3190 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
3191                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
3192     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
3193     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
3194     //       are reset).
3195     bool is_infinite = !persp3d_VP_is_finite(persp, axis);
3197     if (is_infinite) {
3198         gtk_toggle_action_set_active(tact, TRUE);
3199         gtk_action_set_sensitive(act, TRUE);
3201         double angle = persp3d_get_infinite_angle(persp, axis);
3202         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
3203             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
3204         }
3205     } else {
3206         gtk_toggle_action_set_active(tact, FALSE);
3207         gtk_action_set_sensitive(act, FALSE);
3208     }
3211 static void
3212 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
3213     if (!persp_repr) {
3214         g_print ("No perspective given to box3d_resync_toolbar().\n");
3215         return;
3216     }
3218     GtkWidget *tbl = GTK_WIDGET(data);
3219     GtkAdjustment *adj = 0;
3220     GtkAction *act = 0;
3221     GtkToggleAction *tact = 0;
3222     Persp3D *persp = persp3d_get_from_repr(persp_repr);
3223     {
3224         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
3225         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
3226         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
3228         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
3229     }
3230     {
3231         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
3232         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
3233         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
3235         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
3236     }
3237     {
3238         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
3239         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
3240         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
3242         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
3243     }
3246 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3247                                                   gchar const */*old_value*/, gchar const */*new_value*/,
3248                                                   bool /*is_interactive*/, gpointer data)
3250     GtkWidget *tbl = GTK_WIDGET(data);
3252     // quit if run by the attr_changed or selection changed listener
3253     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3254         return;
3255     }
3257     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
3258     // sp_document_maybe_done() when the document is undo insensitive)
3259     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3261     // TODO: Only update the appropriate part of the toolbar
3262 //    if (!strcmp(name, "inkscape:vp_z")) {
3263         box3d_resync_toolbar(repr, G_OBJECT(tbl));
3264 //    }
3266     Persp3D *persp = persp3d_get_from_repr(repr);
3267     persp3d_update_box_reprs(persp);
3269     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3272 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
3274     NULL, /* child_added */
3275     NULL, /* child_removed */
3276     box3d_persp_tb_event_attr_changed,
3277     NULL, /* content_changed */
3278     NULL  /* order_changed */
3279 };
3281 /**
3282  *  \param selection Should not be NULL.
3283  */
3284 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
3285 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
3286 static void
3287 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3289     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
3290     // disable the angle entry fields for this direction (otherwise entering a value in them should only
3291     // update the perspectives with infinite VPs and leave the other ones untouched).
3293     Inkscape::XML::Node *persp_repr = NULL;
3294     purge_repr_listener(tbl, tbl);
3296     SPItem *item = selection->singleItem();
3297     if (item && SP_IS_BOX3D(item)) {
3298         // FIXME: Also deal with multiple selected boxes
3299         SPBox3D *box = SP_BOX3D(item);
3300         Persp3D *persp = box3d_get_perspective(box);
3301         persp_repr = SP_OBJECT_REPR(persp);
3302         if (persp_repr) {
3303             g_object_set_data(tbl, "repr", persp_repr);
3304             Inkscape::GC::anchor(persp_repr);
3305             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
3306             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
3307         }
3309         inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
3310         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3311         prefs->setString("/tools/shapes/3dbox/persp", persp_repr->attribute("id"));
3313         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
3314         box3d_resync_toolbar(persp_repr, tbl);
3315         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
3316     }
3319 static void
3320 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
3322     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3323     SPDocument *document = sp_desktop_document(desktop);
3325     // quit if run by the attr_changed or selection changed listener
3326     if (g_object_get_data( dataKludge, "freeze" )) {
3327         return;
3328     }
3330     // in turn, prevent listener from responding
3331     g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
3333     //Persp3D *persp = document->current_persp3d;
3334     std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
3335     if (sel_persps.empty()) {
3336         // this can happen when the document is created; we silently ignore it
3337         return;
3338     }
3339     Persp3D *persp = sel_persps.front();
3341     persp->tmat.set_infinite_direction (axis, adj->value);
3342     SP_OBJECT(persp)->updateRepr();
3344     // TODO: use the correct axis here, too
3345     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
3347     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
3351 static void
3352 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3354     box3d_angle_value_changed(adj, dataKludge, Proj::X);
3357 static void
3358 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3360     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
3363 static void
3364 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3366     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
3370 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
3372     // TODO: Take all selected perspectives into account
3373     std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
3374     if (sel_persps.empty()) {
3375         // this can happen when the document is created; we silently ignore it
3376         return;
3377     }
3378     Persp3D *persp = sel_persps.front();
3380     bool set_infinite = gtk_toggle_action_get_active(act);
3381     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
3384 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3386     box3d_vp_state_changed(act, box3d_angle, Proj::X);
3389 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3391     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
3394 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3396     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
3399 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3401     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3402     EgeAdjustmentAction* eact = 0;
3403     SPDocument *document = sp_desktop_document (desktop);
3404     Persp3D *persp = document->current_persp3d;
3406     EgeAdjustmentAction* box3d_angle_x = 0;
3407     EgeAdjustmentAction* box3d_angle_y = 0;
3408     EgeAdjustmentAction* box3d_angle_z = 0;
3410     /* Angle X */
3411     {
3412         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3413         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3414         eact = create_adjustment_action( "3DBoxAngleXAction",
3415                                          _("Angle in X direction"), _("Angle X:"),
3416                                          // Translators: PL is short for 'perspective line'
3417                                          _("Angle of PLs in X direction"),
3418                                          "/tools/shapes/3dbox/box3d_angle_x", 30,
3419                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
3420                                          -360.0, 360.0, 1.0, 10.0,
3421                                          labels, values, G_N_ELEMENTS(labels),
3422                                          box3d_angle_x_value_changed );
3423         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3424         g_object_set_data( holder, "box3d_angle_x_action", eact );
3425         box3d_angle_x = eact;
3426     }
3428     if (!persp3d_VP_is_finite(persp, Proj::X)) {
3429         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3430     } else {
3431         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3432     }
3435     /* VP X state */
3436     {
3437         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
3438                                                       // Translators: VP is short for 'vanishing point'
3439                                                       _("State of VP in X direction"),
3440                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
3441                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3442                                                       Inkscape::ICON_SIZE_DECORATION );
3443         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3444         g_object_set_data( holder, "box3d_vp_x_state_action", act );
3445         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
3446         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3447         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3448     }
3450     /* Angle Y */
3451     {
3452         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3453         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3454         eact = create_adjustment_action( "3DBoxAngleYAction",
3455                                          _("Angle in Y direction"), _("Angle Y:"),
3456                                          // Translators: PL is short for 'perspective line'
3457                                          _("Angle of PLs in Y direction"),
3458                                          "/tools/shapes/3dbox/box3d_angle_y", 30,
3459                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3460                                          -360.0, 360.0, 1.0, 10.0,
3461                                          labels, values, G_N_ELEMENTS(labels),
3462                                          box3d_angle_y_value_changed );
3463         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3464         g_object_set_data( holder, "box3d_angle_y_action", eact );
3465         box3d_angle_y = eact;
3466     }
3468     if (!persp3d_VP_is_finite(persp, Proj::Y)) {
3469         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3470     } else {
3471         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3472     }
3474     /* VP Y state */
3475     {
3476         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
3477                                                       // Translators: VP is short for 'vanishing point'
3478                                                       _("State of VP in Y direction"),
3479                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
3480                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3481                                                       Inkscape::ICON_SIZE_DECORATION );
3482         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3483         g_object_set_data( holder, "box3d_vp_y_state_action", act );
3484         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
3485         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3486         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3487     }
3489     /* Angle Z */
3490     {
3491         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3492         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3493         eact = create_adjustment_action( "3DBoxAngleZAction",
3494                                          _("Angle in Z direction"), _("Angle Z:"),
3495                                          // Translators: PL is short for 'perspective line'
3496                                          _("Angle of PLs in Z direction"),
3497                                          "/tools/shapes/3dbox/box3d_angle_z", 30,
3498                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3499                                          -360.0, 360.0, 1.0, 10.0,
3500                                          labels, values, G_N_ELEMENTS(labels),
3501                                          box3d_angle_z_value_changed );
3502         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3503         g_object_set_data( holder, "box3d_angle_z_action", eact );
3504         box3d_angle_z = eact;
3505     }
3507     if (!persp3d_VP_is_finite(persp, Proj::Z)) {
3508         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3509     } else {
3510         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3511     }
3513     /* VP Z state */
3514     {
3515         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3516                                                       // Translators: VP is short for 'vanishing point'
3517                                                       _("State of VP in Z direction"),
3518                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3519                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3520                                                       Inkscape::ICON_SIZE_DECORATION );
3521         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3522         g_object_set_data( holder, "box3d_vp_z_state_action", act );
3523         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3524         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3525         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3526     }
3528     sigc::connection *connection = new sigc::connection(
3529         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3530        );
3531     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3532     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3535 //########################
3536 //##       Spiral       ##
3537 //########################
3539 static void
3540 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, Glib::ustring const &value_name)
3542     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3544     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3545         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3546         prefs->setDouble("/tools/shapes/spiral/" + value_name, adj->value);
3547     }
3549     // quit if run by the attr_changed listener
3550     if (g_object_get_data( tbl, "freeze" )) {
3551         return;
3552     }
3554     // in turn, prevent listener from responding
3555     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3557     gchar* namespaced_name = g_strconcat("sodipodi:", value_name.data(), NULL);
3559     bool modmade = false;
3560     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3561          items != NULL;
3562          items = items->next)
3563     {
3564         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3565             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3566             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3567             SP_OBJECT((SPItem *) items->data)->updateRepr();
3568             modmade = true;
3569         }
3570     }
3572     g_free(namespaced_name);
3574     if (modmade) {
3575         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3576                                    _("Change spiral"));
3577     }
3579     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3582 static void
3583 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3585     sp_spl_tb_value_changed(adj, tbl, "revolution");
3588 static void
3589 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3591     sp_spl_tb_value_changed(adj, tbl, "expansion");
3594 static void
3595 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3597     sp_spl_tb_value_changed(adj, tbl, "t0");
3600 static void
3601 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3603     GtkWidget *tbl = GTK_WIDGET(obj);
3605     GtkAdjustment *adj;
3607     // fixme: make settable
3608     gdouble rev = 5;
3609     gdouble exp = 1.0;
3610     gdouble t0 = 0.0;
3612     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3613     gtk_adjustment_set_value(adj, rev);
3614     gtk_adjustment_value_changed(adj);
3616     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3617     gtk_adjustment_set_value(adj, exp);
3618     gtk_adjustment_value_changed(adj);
3620     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3621     gtk_adjustment_set_value(adj, t0);
3622     gtk_adjustment_value_changed(adj);
3624     spinbutton_defocus(GTK_OBJECT(tbl));
3628 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3629                                          gchar const */*old_value*/, gchar const */*new_value*/,
3630                                          bool /*is_interactive*/, gpointer data)
3632     GtkWidget *tbl = GTK_WIDGET(data);
3634     // quit if run by the _changed callbacks
3635     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3636         return;
3637     }
3639     // in turn, prevent callbacks from responding
3640     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3642     GtkAdjustment *adj;
3643     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3644     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3646     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3647     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3649     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3650     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3652     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3656 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3657     NULL, /* child_added */
3658     NULL, /* child_removed */
3659     spiral_tb_event_attr_changed,
3660     NULL, /* content_changed */
3661     NULL  /* order_changed */
3662 };
3664 static void
3665 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3667     int n_selected = 0;
3668     Inkscape::XML::Node *repr = NULL;
3670     purge_repr_listener( tbl, tbl );
3672     for (GSList const *items = selection->itemList();
3673          items != NULL;
3674          items = items->next)
3675     {
3676         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3677             n_selected++;
3678             repr = SP_OBJECT_REPR((SPItem *) items->data);
3679         }
3680     }
3682     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3684     if (n_selected == 0) {
3685         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3686     } else if (n_selected == 1) {
3687         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3689         if (repr) {
3690             g_object_set_data( tbl, "repr", repr );
3691             Inkscape::GC::anchor(repr);
3692             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3693             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3694         }
3695     } else {
3696         // FIXME: implement averaging of all parameters for multiple selected
3697         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3698         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3699     }
3703 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3705     EgeAdjustmentAction* eact = 0;
3706     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3708     {
3709         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3710         ege_output_action_set_use_markup( act, TRUE );
3711         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3712         g_object_set_data( holder, "mode_action", act );
3713     }
3715     /* Revolution */
3716     {
3717         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3718         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3719         eact = create_adjustment_action( "SpiralRevolutionAction",
3720                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3721                                          "/tools/shapes/spiral/revolution", 3.0,
3722                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3723                                          0.01, 1024.0, 0.1, 1.0,
3724                                          labels, values, G_N_ELEMENTS(labels),
3725                                          sp_spl_tb_revolution_value_changed, 1, 2);
3726         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3727     }
3729     /* Expansion */
3730     {
3731         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3732         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3733         eact = create_adjustment_action( "SpiralExpansionAction",
3734                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3735                                          "/tools/shapes/spiral/expansion", 1.0,
3736                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3737                                          0.0, 1000.0, 0.01, 1.0,
3738                                          labels, values, G_N_ELEMENTS(labels),
3739                                          sp_spl_tb_expansion_value_changed);
3740         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3741     }
3743     /* T0 */
3744     {
3745         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3746         gdouble values[] = {0, 0.5, 0.9};
3747         eact = create_adjustment_action( "SpiralT0Action",
3748                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3749                                          "/tools/shapes/spiral/t0", 0.0,
3750                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3751                                          0.0, 0.999, 0.01, 1.0,
3752                                          labels, values, G_N_ELEMENTS(labels),
3753                                          sp_spl_tb_t0_value_changed);
3754         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3755     }
3757     /* Reset */
3758     {
3759         InkAction* inky = ink_action_new( "SpiralResetAction",
3760                                           _("Defaults"),
3761                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3762                                           GTK_STOCK_CLEAR,
3763                                           secondarySize );
3764         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3765         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3766     }
3769     sigc::connection *connection = new sigc::connection(
3770         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3771         );
3772     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3773     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3776 //########################
3777 //##     Pen/Pencil     ##
3778 //########################
3780 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3781 static Glib::ustring const
3782 freehand_tool_name(GObject *dataKludge)
3784     SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3785     return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3786              ? "/tools/freehand/pen"
3787              : "/tools/freehand/pencil" );
3790 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3792     gint mode = ege_select_one_action_get_active(act);
3794     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3795     prefs->setInt(freehand_tool_name(tbl) + "/freehand-mode", mode);
3797     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3799     // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3800     // preparatory work here
3801     if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3802         SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3803         sp_pen_context_set_polyline_mode(pc);
3804     }
3807 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3809     /* Freehand mode toggle buttons */
3810     {
3811         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3812         guint freehandMode = prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/freehand-mode" : "/tools/freehand/pen/freehand-mode" ), 0);
3813         Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3815         {
3816             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3818             GtkTreeIter iter;
3819             gtk_list_store_append( model, &iter );
3820             gtk_list_store_set( model, &iter,
3821                                 0, _("Bezier"),
3822                                 1, _("Create regular Bezier path"),
3823                                 2, INKSCAPE_ICON_PATH_MODE_BEZIER,
3824                                 -1 );
3826             gtk_list_store_append( model, &iter );
3827             gtk_list_store_set( model, &iter,
3828                                 0, _("Spiro"),
3829                                 1, _("Create Spiro path"),
3830                                 2, INKSCAPE_ICON_PATH_MODE_SPIRO,
3831                                 -1 );
3833             if (!tool_is_pencil) {
3834                 gtk_list_store_append( model, &iter );
3835                 gtk_list_store_set( model, &iter,
3836                                     0, _("Zigzag"),
3837                                     1, _("Create a sequence of straight line segments"),
3838                                     2, INKSCAPE_ICON_PATH_MODE_POLYLINE,
3839                                     -1 );
3841                 gtk_list_store_append( model, &iter );
3842                 gtk_list_store_set( model, &iter,
3843                                     0, _("Paraxial"),
3844                                     1, _("Create a sequence of paraxial line segments"),
3845                                     2, INKSCAPE_ICON_PATH_MODE_POLYLINE_PARAXIAL,
3846                                     -1 );
3847             }
3849             EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3850                                                                 "FreehandModeActionPencil" :
3851                                                                 "FreehandModeActionPen",
3852                                                                 (_("Mode:")), ("Mode"), NULL, GTK_TREE_MODEL(model) );
3853             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3855             ege_select_one_action_set_appearance( act, "full" );
3856             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3857             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3858             ege_select_one_action_set_icon_column( act, 2 );
3859             ege_select_one_action_set_icon_size( act, secondarySize );
3860             ege_select_one_action_set_tooltip_column( act, 1  );
3862             ege_select_one_action_set_active( act, freehandMode);
3863             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3864         }
3865     }
3868 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3869     gint shape = ege_select_one_action_get_active( act );
3870     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3871     prefs->setInt(freehand_tool_name(dataKludge) + "/shape", shape);
3874 /**
3875  * \brief Generate the list of freehand advanced shape option entries.
3876  */
3877 GList * freehand_shape_dropdown_items_list() {
3878     GList *glist = NULL;
3880     glist = g_list_append (glist, _("None"));
3881     glist = g_list_append (glist, _("Triangle in"));
3882     glist = g_list_append (glist, _("Triangle out"));
3883     glist = g_list_append (glist, _("Ellipse"));
3884     glist = g_list_append (glist, _("From clipboard"));
3886     return glist;
3889 static void
3890 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3891     /*advanced shape options */
3892     {
3893         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3894         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3896         GList* items = 0;
3897         gint count = 0;
3898         for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3899         {
3900             GtkTreeIter iter;
3901             gtk_list_store_append( model, &iter );
3902             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3903             count++;
3904         }
3905         g_list_free( items );
3906         items = 0;
3907         EgeSelectOneAction* act1 = ege_select_one_action_new(
3908             tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3909             _("Shape:"), ("Shape"), NULL, GTK_TREE_MODEL(model));
3910         g_object_set( act1, "short_label", _("Shape:"), NULL );
3911         ege_select_one_action_set_appearance( act1, "compact" );
3912         ege_select_one_action_set_active( act1, prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/shape" : "/tools/freehand/pen/shape" ), 0) );
3913         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3914         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3915         g_object_set_data( holder, "shape_action", act1 );
3916     }
3919 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3921     sp_add_freehand_mode_toggle(mainActions, holder, false);
3922     freehand_add_advanced_shape_options(mainActions, holder, false);
3926 static void
3927 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3929     GtkWidget *tbl = GTK_WIDGET(obj);
3931     GtkAdjustment *adj;
3933     // fixme: make settable
3934     gdouble tolerance = 4;
3936     adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3937     gtk_adjustment_set_value(adj, tolerance);
3938     gtk_adjustment_value_changed(adj);
3940     spinbutton_defocus(GTK_OBJECT(tbl));
3943 static void
3944 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3946     // quit if run by the attr_changed listener
3947     if (g_object_get_data( tbl, "freeze" )) {
3948         return;
3949     }
3950     // in turn, prevent listener from responding
3951     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3952     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3953     prefs->setDouble("/tools/freehand/pencil/tolerance", adj->value);
3954     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3957 class PencilToleranceObserver : public Inkscape::Preferences::Observer {
3958 public:
3959     PencilToleranceObserver(Glib::ustring const &path, GObject *x) : Observer(path), _obj(x)
3960     {
3961         g_object_set_data(_obj, "prefobserver", this);
3962     }
3963     virtual ~PencilToleranceObserver() {
3964         if (g_object_get_data(_obj, "prefobserver") == this) {
3965                 g_object_set_data(_obj, "prefobserver", NULL);
3966         }
3967     }
3968     virtual void notify(Inkscape::Preferences::Entry const &val) {
3969         GObject* tbl = _obj;
3970         if (g_object_get_data( tbl, "freeze" )) {
3971             return;
3972         }
3973         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3975         GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl, "tolerance");
3977         double v = val.getDouble(adj->value);
3978         gtk_adjustment_set_value(adj, v);
3979         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3980     }
3981 private:
3982     GObject *_obj;
3983 };
3986 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3988     sp_add_freehand_mode_toggle(mainActions, holder, true);
3990     EgeAdjustmentAction* eact = 0;
3992     /* Tolerance */
3993     {
3994         gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
3995         gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
3996         eact = create_adjustment_action( "PencilToleranceAction",
3997                                          _("Smoothing:"), _("Smoothing: "),
3998                  _("How much smoothing (simplifying) is applied to the line"),
3999                                          "/tools/freehand/pencil/tolerance",
4000                                          3.0,
4001                                          GTK_WIDGET(desktop->canvas), NULL,
4002                                          holder, TRUE, "altx-pencil",
4003                                          1, 100.0, 0.5, 1.0,
4004                                          labels, values, G_N_ELEMENTS(labels),
4005                                          sp_pencil_tb_tolerance_value_changed,
4006                                          1, 2);
4007         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4008         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4010         PencilToleranceObserver *obs =
4011             new PencilToleranceObserver("/tools/freehand/pencil/tolerance", G_OBJECT(holder));
4012     }
4014     /* advanced shape options */
4015     freehand_add_advanced_shape_options(mainActions, holder, true);
4017     /* Reset */
4018     {
4019         InkAction* inky = ink_action_new( "PencilResetAction",
4020                                           _("Defaults"),
4021                                           _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
4022                                           GTK_STOCK_CLEAR,
4023                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4024         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
4025         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4026     }
4028     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4033 //########################
4034 //##       Tweak        ##
4035 //########################
4037 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4039     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4040     prefs->setDouble( "/tools/tweak/width", adj->value * 0.01 );
4043 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4045     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4046     prefs->setDouble( "/tools/tweak/force", adj->value * 0.01 );
4049 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4051     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4052     prefs->setBool("/tools/tweak/usepressure", gtk_toggle_action_get_active(act));
4055 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4057     int mode = ege_select_one_action_get_active( act );
4058     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4059     prefs->setInt("/tools/tweak/mode", mode);
4061     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
4062     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
4063     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
4064     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
4065     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
4066     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
4067     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
4068         if (doh) gtk_action_set_sensitive (doh, TRUE);
4069         if (dos) gtk_action_set_sensitive (dos, TRUE);
4070         if (dol) gtk_action_set_sensitive (dol, TRUE);
4071         if (doo) gtk_action_set_sensitive (doo, TRUE);
4072         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
4073         if (fid) gtk_action_set_sensitive (fid, FALSE);
4074     } else {
4075         if (doh) gtk_action_set_sensitive (doh, FALSE);
4076         if (dos) gtk_action_set_sensitive (dos, FALSE);
4077         if (dol) gtk_action_set_sensitive (dol, FALSE);
4078         if (doo) gtk_action_set_sensitive (doo, FALSE);
4079         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
4080         if (fid) gtk_action_set_sensitive (fid, TRUE);
4081     }
4084 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4086     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4087     prefs->setDouble( "/tools/tweak/fidelity", adj->value * 0.01 );
4090 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
4091     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4092     prefs->setBool("/tools/tweak/doh", gtk_toggle_action_get_active(act));
4094 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
4095     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4096     prefs->setBool("/tools/tweak/dos", gtk_toggle_action_get_active(act));
4098 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
4099     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4100     prefs->setBool("/tools/tweak/dol", gtk_toggle_action_get_active(act));
4102 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
4103     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4104     prefs->setBool("/tools/tweak/doo", gtk_toggle_action_get_active(act));
4107 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4109     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
4110     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4112     {
4113         /* Width */
4114         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
4115         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4116         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
4117                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
4118                                                               "/tools/tweak/width", 15,
4119                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
4120                                                               1, 100, 1.0, 10.0,
4121                                                               labels, values, G_N_ELEMENTS(labels),
4122                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
4123         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4124         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4125         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4126     }
4129     {
4130         /* Force */
4131         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
4132         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4133         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
4134                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
4135                                                               "/tools/tweak/force", 20,
4136                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
4137                                                               1, 100, 1.0, 10.0,
4138                                                               labels, values, G_N_ELEMENTS(labels),
4139                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
4140         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4141         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4142         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4143     }
4145     /* Mode */
4146     {
4147         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4149         GtkTreeIter iter;
4150         gtk_list_store_append( model, &iter );
4151         gtk_list_store_set( model, &iter,
4152                             0, _("Move mode"),
4153                             1, _("Move objects in any direction"),
4154                             2, INKSCAPE_ICON_OBJECT_TWEAK_PUSH,
4155                             -1 );
4157         gtk_list_store_append( model, &iter );
4158         gtk_list_store_set( model, &iter,
4159                             0, _("Move in/out mode"),
4160                             1, _("Move objects towards cursor; with Shift from cursor"),
4161                             2, INKSCAPE_ICON_OBJECT_TWEAK_ATTRACT,
4162                             -1 );
4164         gtk_list_store_append( model, &iter );
4165         gtk_list_store_set( model, &iter,
4166                             0, _("Move jitter mode"),
4167                             1, _("Move objects in random directions"),
4168                             2, INKSCAPE_ICON_OBJECT_TWEAK_RANDOMIZE,
4169                             -1 );
4171         gtk_list_store_append( model, &iter );
4172         gtk_list_store_set( model, &iter,
4173                             0, _("Scale mode"),
4174                             1, _("Shrink objects, with Shift enlarge"),
4175                             2, INKSCAPE_ICON_OBJECT_TWEAK_SHRINK,
4176                             -1 );
4178         gtk_list_store_append( model, &iter );
4179         gtk_list_store_set( model, &iter,
4180                             0, _("Rotate mode"),
4181                             1, _("Rotate objects, with Shift counterclockwise"),
4182                             2, INKSCAPE_ICON_OBJECT_TWEAK_ROTATE,
4183                             -1 );
4185         gtk_list_store_append( model, &iter );
4186         gtk_list_store_set( model, &iter,
4187                             0, _("Duplicate/delete mode"),
4188                             1, _("Duplicate objects, with Shift delete"),
4189                             2, INKSCAPE_ICON_OBJECT_TWEAK_DUPLICATE,
4190                             -1 );
4192         gtk_list_store_append( model, &iter );
4193         gtk_list_store_set( model, &iter,
4194                             0, _("Push mode"),
4195                             1, _("Push parts of paths in any direction"),
4196                             2, INKSCAPE_ICON_PATH_TWEAK_PUSH,
4197                             -1 );
4199         gtk_list_store_append( model, &iter );
4200         gtk_list_store_set( model, &iter,
4201                             0, _("Shrink/grow mode"),
4202                             1, _("Shrink (inset) parts of paths; with Shift grow (outset)"),
4203                             2, INKSCAPE_ICON_PATH_TWEAK_SHRINK,
4204                             -1 );
4206         gtk_list_store_append( model, &iter );
4207         gtk_list_store_set( model, &iter,
4208                             0, _("Attract/repel mode"),
4209                             1, _("Attract parts of paths towards cursor; with Shift from cursor"),
4210                             2, INKSCAPE_ICON_PATH_TWEAK_ATTRACT,
4211                             -1 );
4213         gtk_list_store_append( model, &iter );
4214         gtk_list_store_set( model, &iter,
4215                             0, _("Roughen mode"),
4216                             1, _("Roughen parts of paths"),
4217                             2, INKSCAPE_ICON_PATH_TWEAK_ROUGHEN,
4218                             -1 );
4220         gtk_list_store_append( model, &iter );
4221         gtk_list_store_set( model, &iter,
4222                             0, _("Color paint mode"),
4223                             1, _("Paint the tool's color upon selected objects"),
4224                             2, INKSCAPE_ICON_OBJECT_TWEAK_PAINT,
4225                             -1 );
4227         gtk_list_store_append( model, &iter );
4228         gtk_list_store_set( model, &iter,
4229                             0, _("Color jitter mode"),
4230                             1, _("Jitter the colors of selected objects"),
4231                             2, INKSCAPE_ICON_OBJECT_TWEAK_JITTER_COLOR,
4232                             -1 );
4234         gtk_list_store_append( model, &iter );
4235         gtk_list_store_set( model, &iter,
4236                             0, _("Blur mode"),
4237                             1, _("Blur selected objects more; with Shift, blur less"),
4238                             2, INKSCAPE_ICON_OBJECT_TWEAK_BLUR,
4239                             -1 );
4242         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4243         g_object_set( act, "short_label", _("Mode:"), NULL );
4244         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4245         g_object_set_data( holder, "mode_action", act );
4247         ege_select_one_action_set_appearance( act, "full" );
4248         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4249         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4250         ege_select_one_action_set_icon_column( act, 2 );
4251         ege_select_one_action_set_icon_size( act, secondarySize );
4252         ege_select_one_action_set_tooltip_column( act, 1  );
4254         gint mode = prefs->getInt("/tools/tweak/mode", 0);
4255         ege_select_one_action_set_active( act, mode );
4256         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
4258         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
4259     }
4261     guint mode = prefs->getInt("/tools/tweak/mode", 0);
4263     {
4264         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
4265         ege_output_action_set_use_markup( act, TRUE );
4266         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4267         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4268             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4269         g_object_set_data( holder, "tweak_channels_label", act);
4270     }
4272     {
4273         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
4274                                                       _("Hue"),
4275                                                       _("In color mode, act on objects' hue"),
4276                                                       NULL,
4277                                                       Inkscape::ICON_SIZE_DECORATION );
4278         //TRANSLATORS:  "H" here stands for hue
4279         g_object_set( act, "short_label", _("H"), NULL );
4280         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4281         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
4282         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doh", true) );
4283         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4284             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4285         g_object_set_data( holder, "tweak_doh", act);
4286     }
4287     {
4288         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
4289                                                       _("Saturation"),
4290                                                       _("In color mode, act on objects' saturation"),
4291                                                       NULL,
4292                                                       Inkscape::ICON_SIZE_DECORATION );
4293         //TRANSLATORS: "S" here stands for Saturation
4294         g_object_set( act, "short_label", _("S"), NULL );
4295         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4296         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
4297         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dos", true) );
4298         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4299             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4300         g_object_set_data( holder, "tweak_dos", act );
4301     }
4302     {
4303         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
4304                                                       _("Lightness"),
4305                                                       _("In color mode, act on objects' lightness"),
4306                                                       NULL,
4307                                                       Inkscape::ICON_SIZE_DECORATION );
4308         //TRANSLATORS: "L" here stands for Lightness
4309         g_object_set( act, "short_label", _("L"), NULL );
4310         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4311         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
4312         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dol", true) );
4313         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4314             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4315         g_object_set_data( holder, "tweak_dol", act );
4316     }
4317     {
4318         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
4319                                                       _("Opacity"),
4320                                                       _("In color mode, act on objects' opacity"),
4321                                                       NULL,
4322                                                       Inkscape::ICON_SIZE_DECORATION );
4323         //TRANSLATORS: "O" here stands for Opacity
4324         g_object_set( act, "short_label", _("O"), NULL );
4325         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4326         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
4327         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doo", true) );
4328         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4329             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4330         g_object_set_data( holder, "tweak_doo", act );
4331     }
4333     {   /* Fidelity */
4334         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
4335         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4336         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
4337                                                               _("Fidelity"), _("Fidelity:"),
4338                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
4339                                                               "/tools/tweak/fidelity", 50,
4340                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
4341                                                               1, 100, 1.0, 10.0,
4342                                                               labels, values, G_N_ELEMENTS(labels),
4343                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
4344         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4345         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4346         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
4347             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
4348         g_object_set_data( holder, "tweak_fidelity", eact );
4349     }
4352     /* Use Pressure button */
4353     {
4354         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
4355                                                       _("Pressure"),
4356                                                       _("Use the pressure of the input device to alter the force of tweak action"),
4357                                                       INKSCAPE_ICON_DRAW_USE_PRESSURE,
4358                                                       Inkscape::ICON_SIZE_DECORATION );
4359         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4360         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
4361         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/usepressure", true) );
4362     }
4367 //########################
4368 //##     Calligraphy    ##
4369 //########################
4370 static void update_presets_list (GObject *tbl)
4372     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4373     if (g_object_get_data(tbl, "presets_blocked"))
4374         return;
4376     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4377     if (!sel) {
4378         // WTF!? This will cause a segfault if ever reached
4379         //ege_select_one_action_set_active(sel, 0);
4380         return;
4381     }
4383     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4385     int ege_index = 1;
4386     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++ege_index) {
4387         bool match = true;
4389         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(*i);
4390         for (std::vector<Inkscape::Preferences::Entry>::iterator j = preset.begin(); j != preset.end(); ++j) {
4391             Glib::ustring entry_name = j->getEntryName();
4392             if (entry_name == "id" || entry_name == "name") continue;
4394             void *widget = g_object_get_data(tbl, entry_name.data());
4395             if (widget) {
4396                 if (GTK_IS_ADJUSTMENT(widget)) {
4397                     double v = j->getDouble();
4398                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4399                     //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
4400                     if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
4401                         match = false;
4402                         break;
4403                     }
4404                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4405                     bool v = j->getBool();
4406                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4407                     //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
4408                     if ( static_cast<bool>(gtk_toggle_action_get_active(toggle)) != v ) {
4409                         match = false;
4410                         break;
4411                     }
4412                 }
4413             }
4414         }
4416         if (match) {
4417             // newly added item is at the same index as the
4418             // save command, so we need to change twice for it to take effect
4419             ege_select_one_action_set_active(sel, 0);
4420             ege_select_one_action_set_active(sel, ege_index); // one-based index
4421             return;
4422         }
4423     }
4425     // no match found
4426     ege_select_one_action_set_active(sel, 0);
4429 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
4431     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4432     prefs->setDouble( "/tools/calligraphic/mass", adj->value * 0.01 );
4433     update_presets_list(tbl);
4436 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
4438     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4439     prefs->setDouble( "/tools/calligraphic/wiggle", adj->value * 0.01 );
4440     update_presets_list(tbl);
4443 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
4445     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4446     prefs->setDouble( "/tools/calligraphic/angle", adj->value );
4447     update_presets_list(tbl);
4450 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
4452     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4453     prefs->setDouble( "/tools/calligraphic/width", adj->value * 0.01 );
4454     update_presets_list(tbl);
4457 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
4459     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4460     prefs->setDouble("/tools/calligraphic/thinning", adj->value * 0.01 );
4461     update_presets_list(tbl);
4464 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
4466     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4467     prefs->setDouble( "/tools/calligraphic/flatness", adj->value * 0.01);
4468     update_presets_list(tbl);
4471 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
4473     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4474     prefs->setDouble( "/tools/calligraphic/tremor", adj->value * 0.01 );
4475     update_presets_list(tbl);
4478 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
4480     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4481     prefs->setDouble( "/tools/calligraphic/cap_rounding", adj->value );
4482     update_presets_list(tbl);
4485 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
4487     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4488     prefs->setBool("/tools/calligraphic/usepressure", gtk_toggle_action_get_active( act ));
4489     update_presets_list(tbl);
4492 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
4494     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4495     prefs->setBool("/tools/calligraphic/tracebackground", gtk_toggle_action_get_active( act ));
4496     update_presets_list(tbl);
4499 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
4501     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4502     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
4503     prefs->setBool("/tools/calligraphic/usetilt", gtk_toggle_action_get_active( act ));
4504     update_presets_list(tbl);
4505     if (calligraphy_angle )
4506         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
4510 static gchar const *const widget_names[] = {
4511     "width",
4512     "mass",
4513     "wiggle",
4514     "angle",
4515     "thinning",
4516     "tremor",
4517     "flatness",
4518     "cap_rounding",
4519     "usepressure",
4520     "tracebackground",
4521     "usetilt"
4522 };
4525 static void sp_dcc_build_presets_list(GObject *tbl)
4527     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4529     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4530     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4531     gtk_list_store_clear (model);
4533     {
4534         GtkTreeIter iter;
4535         gtk_list_store_append( model, &iter );
4536         gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4537     }
4539     // iterate over all presets to populate the list
4540     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4541     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4542     int ii=1;
4544     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i) {
4545         GtkTreeIter iter;
4546         Glib::ustring preset_name = prefs->getString(*i + "/name");
4547         gtk_list_store_append( model, &iter );
4548         gtk_list_store_set( model, &iter, 0, preset_name.data(), 1, ii++, -1 );
4549     }
4551     {
4552         GtkTreeIter iter;
4553         gtk_list_store_append( model, &iter );
4554         gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4555         g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4556     }
4558     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4560     update_presets_list (tbl);
4563 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4565     using Inkscape::UI::Dialog::CalligraphicProfileRename;
4566     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4567     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4568     if (! desktop) return;
4570     if (g_object_get_data(tbl, "presets_blocked"))
4571         return;
4573     CalligraphicProfileRename::show(desktop);
4574     if ( !CalligraphicProfileRename::applied()) {
4575         // dialog cancelled
4576         update_presets_list (tbl);
4577         return;
4578     }
4579     Glib::ustring profile_name = CalligraphicProfileRename::getProfileName();
4581     if (profile_name.empty()) {
4582         // empty name entered
4583         update_presets_list (tbl);
4584         return;
4585     }
4587     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4589     // If there's a preset with the given name, find it and set save_path appropriately
4590     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4591     int total_presets = presets.size();
4592     int new_index = -1;
4593     Glib::ustring save_path; // profile pref path without a trailing slash
4595     int temp_index = 0;
4596     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++temp_index) {
4597         Glib::ustring name = prefs->getString(*i + "/name");
4598         if (!name.empty() && profile_name == name) {
4599             new_index = temp_index;
4600             save_path = *i;
4601             break;
4602         }
4603     }
4605     if (new_index == -1) {
4606         // no preset with this name, create
4607         new_index = total_presets + 1;
4608         gchar *profile_id = g_strdup_printf("/dcc%d", new_index);
4609         save_path = Glib::ustring("/tools/calligraphic/preset") + profile_id;
4610         g_free(profile_id);
4611     }
4613     for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4614         gchar const *const widget_name = widget_names[i];
4615         void *widget = g_object_get_data(tbl, widget_name);
4616         if (widget) {
4617             if (GTK_IS_ADJUSTMENT(widget)) {
4618                 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4619                 prefs->setDouble(save_path + "/" + widget_name, gtk_adjustment_get_value(adj));
4620                 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4621             } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4622                 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4623                 prefs->setBool(save_path + "/" + widget_name, gtk_toggle_action_get_active(toggle));
4624                 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4625             } else {
4626                 g_warning("Unknown widget type for preset: %s\n", widget_name);
4627             }
4628         } else {
4629             g_warning("Bad key when writing preset: %s\n", widget_name);
4630         }
4631     }
4632     prefs->setString(save_path + "/name", profile_name);
4634     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4635     sp_dcc_build_presets_list (tbl);
4639 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4641     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4643     gint preset_index = ege_select_one_action_get_active( act );
4644     // This is necessary because EgeSelectOneAction spams us with GObject "changed" signal calls
4645     // even when the preset is not changed. It would be good to replace it with something more
4646     // modern. Index 0 means "No preset", so we don't do anything.
4647     if (preset_index == 0) return;
4649     gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4651     if (preset_index == save_presets_index) {
4652         // this is the Save command
4653         sp_dcc_save_profile(NULL, tbl);
4654         return;
4655     }
4657     if (g_object_get_data(tbl, "presets_blocked"))
4658         return;
4660     // preset_index is one-based so we subtract 1
4661     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4662     Glib::ustring preset_path = presets.at(preset_index - 1);
4664     if (!preset_path.empty()) {
4665         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
4667         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(preset_path);
4669         // Shouldn't this be std::map?
4670         for (std::vector<Inkscape::Preferences::Entry>::iterator i = preset.begin(); i != preset.end(); ++i) {
4671             Glib::ustring entry_name = i->getEntryName();
4672             if (entry_name == "id" || entry_name == "name") continue;
4673             void *widget = g_object_get_data(tbl, entry_name.data());
4674             if (widget) {
4675                 if (GTK_IS_ADJUSTMENT(widget)) {
4676                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4677                     gtk_adjustment_set_value(adj, i->getDouble());
4678                     //std::cout << "set adj " << attr_name << " to " << v << "\n";
4679                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4680                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4681                     gtk_toggle_action_set_active(toggle, i->getBool());
4682                     //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4683                 } else {
4684                     g_warning("Unknown widget type for preset: %s\n", entry_name.data());
4685                 }
4686             } else {
4687                 g_warning("Bad key found in a preset record: %s\n", entry_name.data());
4688             }
4689         }
4690         g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4691     }
4695 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4697     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4698     {
4699         g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4701         EgeAdjustmentAction* calligraphy_angle = 0;
4703         {
4704         /* Width */
4705         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4706         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4707         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4708                                                               _("Pen Width"), _("Width:"),
4709                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
4710                                                               "/tools/calligraphic/width", 15,
4711                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4712                                                               1, 100, 1.0, 10.0,
4713                                                               labels, values, G_N_ELEMENTS(labels),
4714                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
4715         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4716         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4717         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4718         }
4720         {
4721         /* Thinning */
4722             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4723             gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4724         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4725                                                               _("Stroke Thinning"), _("Thinning:"),
4726                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4727                                                               "/tools/calligraphic/thinning", 10,
4728                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4729                                                               -100, 100, 1, 10.0,
4730                                                               labels, values, G_N_ELEMENTS(labels),
4731                                                               sp_ddc_velthin_value_changed, 0.01, 0, 100);
4732         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4733         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4734         }
4736         {
4737         /* Angle */
4738         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4739         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4740         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4741                                                               _("Pen Angle"), _("Angle:"),
4742                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4743                                                               "/tools/calligraphic/angle", 30,
4744                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4745                                                               -90.0, 90.0, 1.0, 10.0,
4746                                                               labels, values, G_N_ELEMENTS(labels),
4747                                                               sp_ddc_angle_value_changed, 1, 0 );
4748         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4749         g_object_set_data( holder, "angle_action", eact );
4750         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4751         calligraphy_angle = eact;
4752         }
4754         {
4755         /* Fixation */
4756             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4757         gdouble values[] = {0, 20, 40, 60, 90, 100};
4758         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4759                                                               _("Fixation"), _("Fixation:"),
4760                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"),
4761                                                               "/tools/calligraphic/flatness", 90,
4762                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4763                                                               0.0, 100, 1.0, 10.0,
4764                                                               labels, values, G_N_ELEMENTS(labels),
4765                                                               sp_ddc_flatness_value_changed, 0.01, 0, 100 );
4766         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4767         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4768         }
4770         {
4771         /* Cap Rounding */
4772             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4773         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4774         // TRANSLATORS: "cap" means "end" (both start and finish) here
4775         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4776                                                               _("Cap rounding"), _("Caps:"),
4777                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4778                                                               "/tools/calligraphic/cap_rounding", 0.0,
4779                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4780                                                               0.0, 5.0, 0.01, 0.1,
4781                                                               labels, values, G_N_ELEMENTS(labels),
4782                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4783         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4784         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4785         }
4787         {
4788         /* Tremor */
4789             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4790         gdouble values[] = {0, 10, 20, 40, 60, 100};
4791         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4792                                                               _("Stroke Tremor"), _("Tremor:"),
4793                                                               _("Increase to make strokes rugged and trembling"),
4794                                                               "/tools/calligraphic/tremor", 0.0,
4795                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4796                                                               0.0, 100, 1, 10.0,
4797                                                               labels, values, G_N_ELEMENTS(labels),
4798                                                               sp_ddc_tremor_value_changed, 0.01, 0, 100 );
4800         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4801         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4802         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4803         }
4805         {
4806         /* Wiggle */
4807         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4808         gdouble values[] = {0, 20, 40, 60, 100};
4809         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4810                                                               _("Pen Wiggle"), _("Wiggle:"),
4811                                                               _("Increase to make the pen waver and wiggle"),
4812                                                               "/tools/calligraphic/wiggle", 0.0,
4813                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4814                                                               0.0, 100, 1, 10.0,
4815                                                               labels, values, G_N_ELEMENTS(labels),
4816                                                               sp_ddc_wiggle_value_changed, 0.01, 0, 100 );
4817         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4818         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4819         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4820         }
4822         {
4823         /* Mass */
4824             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4825         gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4826         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4827                                                               _("Pen Mass"), _("Mass:"),
4828                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
4829                                                               "/tools/calligraphic/mass", 2.0,
4830                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4831                                                               0.0, 100, 1, 10.0,
4832                                                               labels, values, G_N_ELEMENTS(labels),
4833                                                               sp_ddc_mass_value_changed, 0.01, 0, 100 );
4834         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4835         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4836         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4837         }
4840         /* Trace Background button */
4841         {
4842             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4843                                                           _("Trace Background"),
4844                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4845                                                           INKSCAPE_ICON_DRAW_TRACE_BACKGROUND,
4846                                                           Inkscape::ICON_SIZE_DECORATION );
4847             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4848             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4849             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/tracebackground", false) );
4850             g_object_set_data( holder, "tracebackground", act );
4851         }
4853         /* Use Pressure button */
4854         {
4855             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4856                                                           _("Pressure"),
4857                                                           _("Use the pressure of the input device to alter the width of the pen"),
4858                                                           INKSCAPE_ICON_DRAW_USE_PRESSURE,
4859                                                           Inkscape::ICON_SIZE_DECORATION );
4860             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4861             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4862             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usepressure", true) );
4863             g_object_set_data( holder, "usepressure", act );
4864         }
4866         /* Use Tilt button */
4867         {
4868             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4869                                                           _("Tilt"),
4870                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
4871                                                           INKSCAPE_ICON_DRAW_USE_TILT,
4872                                                           Inkscape::ICON_SIZE_DECORATION );
4873             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4874             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4875             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs->getBool("/tools/calligraphic/usetilt", true) );
4876             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usetilt", true) );
4877             g_object_set_data( holder, "usetilt", act );
4878         }
4880         /*calligraphic profile */
4881         {
4882             GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
4883             EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
4884             ege_select_one_action_set_appearance (act1, "compact");
4885             g_object_set_data (holder, "profile_selector", act1 );
4887             g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
4889             sp_dcc_build_presets_list (holder);
4891             g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
4892             gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
4893         }
4894     }
4898 //########################
4899 //##    Circle / Arc    ##
4900 //########################
4902 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4904     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4905     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4907     if (v1 == 0 && v2 == 0) {
4908         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4909             gtk_action_set_sensitive( ocb, FALSE );
4910             gtk_action_set_sensitive( make_whole, FALSE );
4911         }
4912     } else {
4913         gtk_action_set_sensitive( ocb, TRUE );
4914         gtk_action_set_sensitive( make_whole, TRUE );
4915     }
4918 static void
4919 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4921     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4923     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4924         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4925         prefs->setDouble(Glib::ustring("/tools/shapes/arc") + value_name, (adj->value * M_PI)/ 180);
4926     }
4928     // quit if run by the attr_changed listener
4929     if (g_object_get_data( tbl, "freeze" )) {
4930         return;
4931     }
4933     // in turn, prevent listener from responding
4934     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4936     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4938     bool modmade = false;
4939     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4940          items != NULL;
4941          items = items->next)
4942     {
4943         SPItem *item = SP_ITEM(items->data);
4945         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4947             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4948             SPArc *arc = SP_ARC(item);
4950             if (!strcmp(value_name, "start"))
4951                 ge->start = (adj->value * M_PI)/ 180;
4952             else
4953                 ge->end = (adj->value * M_PI)/ 180;
4955             sp_genericellipse_normalize(ge);
4956             ((SPObject *)arc)->updateRepr();
4957             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4959             modmade = true;
4960         }
4961     }
4963     g_free(namespaced_name);
4965     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4967     sp_arctb_sensitivize( tbl, adj->value, other->value );
4969     if (modmade) {
4970         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4971                                    _("Arc: Change start/end"));
4972     }
4974     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4978 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
4980     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
4983 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4985     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
4989 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4991     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4992     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4993         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4994         prefs->setBool("/tools/shapes/arc/open", ege_select_one_action_get_active(act) != 0);
4995     }
4997     // quit if run by the attr_changed listener
4998     if (g_object_get_data( tbl, "freeze" )) {
4999         return;
5000     }
5002     // in turn, prevent listener from responding
5003     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5005     bool modmade = false;
5007     if ( ege_select_one_action_get_active(act) != 0 ) {
5008         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5009              items != NULL;
5010              items = items->next)
5011         {
5012             if (SP_IS_ARC((SPItem *) items->data)) {
5013                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5014                 repr->setAttribute("sodipodi:open", "true");
5015                 SP_OBJECT((SPItem *) items->data)->updateRepr();
5016                 modmade = true;
5017             }
5018         }
5019     } else {
5020         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5021              items != NULL;
5022              items = items->next)
5023         {
5024             if (SP_IS_ARC((SPItem *) items->data))    {
5025                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5026                 repr->setAttribute("sodipodi:open", NULL);
5027                 SP_OBJECT((SPItem *) items->data)->updateRepr();
5028                 modmade = true;
5029             }
5030         }
5031     }
5033     if (modmade) {
5034         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
5035                                    _("Arc: Change open/closed"));
5036     }
5038     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5041 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
5043     GtkAdjustment *adj;
5044     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
5045     gtk_adjustment_set_value(adj, 0.0);
5046     gtk_adjustment_value_changed(adj);
5048     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
5049     gtk_adjustment_set_value(adj, 0.0);
5050     gtk_adjustment_value_changed(adj);
5052     spinbutton_defocus( GTK_OBJECT(obj) );
5055 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
5056                                       gchar const */*old_value*/, gchar const */*new_value*/,
5057                                       bool /*is_interactive*/, gpointer data)
5059     GObject *tbl = G_OBJECT(data);
5061     // quit if run by the _changed callbacks
5062     if (g_object_get_data( tbl, "freeze" )) {
5063         return;
5064     }
5066     // in turn, prevent callbacks from responding
5067     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5069     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
5070     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
5072     GtkAdjustment *adj1,*adj2;
5073     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
5074     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
5075     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
5076     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
5078     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
5080     char const *openstr = NULL;
5081     openstr = repr->attribute("sodipodi:open");
5082     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
5084     if (openstr) {
5085         ege_select_one_action_set_active( ocb, 1 );
5086     } else {
5087         ege_select_one_action_set_active( ocb, 0 );
5088     }
5090     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5093 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
5094     NULL, /* child_added */
5095     NULL, /* child_removed */
5096     arc_tb_event_attr_changed,
5097     NULL, /* content_changed */
5098     NULL  /* order_changed */
5099 };
5102 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
5104     int n_selected = 0;
5105     Inkscape::XML::Node *repr = NULL;
5107     purge_repr_listener( tbl, tbl );
5109     for (GSList const *items = selection->itemList();
5110          items != NULL;
5111          items = items->next)
5112     {
5113         if (SP_IS_ARC((SPItem *) items->data)) {
5114             n_selected++;
5115             repr = SP_OBJECT_REPR((SPItem *) items->data);
5116         }
5117     }
5119     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
5121     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
5122     if (n_selected == 0) {
5123         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
5124     } else if (n_selected == 1) {
5125         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
5126         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5128         if (repr) {
5129             g_object_set_data( tbl, "repr", repr );
5130             Inkscape::GC::anchor(repr);
5131             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
5132             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
5133         }
5134     } else {
5135         // FIXME: implement averaging of all parameters for multiple selected
5136         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
5137         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5138         sp_arctb_sensitivize( tbl, 1, 0 );
5139     }
5143 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5145     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5147     EgeAdjustmentAction* eact = 0;
5148     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
5151     {
5152         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
5153         ege_output_action_set_use_markup( act, TRUE );
5154         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5155         g_object_set_data( holder, "mode_action", act );
5156     }
5158     /* Start */
5159     {
5160         eact = create_adjustment_action( "ArcStartAction",
5161                                          _("Start"), _("Start:"),
5162                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
5163                                          "/tools/shapes/arc/start", 0.0,
5164                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
5165                                          -360.0, 360.0, 1.0, 10.0,
5166                                          0, 0, 0,
5167                                          sp_arctb_start_value_changed);
5168         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5169     }
5171     /* End */
5172     {
5173         eact = create_adjustment_action( "ArcEndAction",
5174                                          _("End"), _("End:"),
5175                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
5176                                          "/tools/shapes/arc/end", 0.0,
5177                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
5178                                          -360.0, 360.0, 1.0, 10.0,
5179                                          0, 0, 0,
5180                                          sp_arctb_end_value_changed);
5181         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5182     }
5184     /* Segments / Pie checkbox */
5185     {
5186         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5188         GtkTreeIter iter;
5189         gtk_list_store_append( model, &iter );
5190         gtk_list_store_set( model, &iter,
5191                             0, _("Closed arc"),
5192                             1, _("Switch to segment (closed shape with two radii)"),
5193                             2, INKSCAPE_ICON_DRAW_ELLIPSE_SEGMENT,
5194                             -1 );
5196         gtk_list_store_append( model, &iter );
5197         gtk_list_store_set( model, &iter,
5198                             0, _("Open Arc"),
5199                             1, _("Switch to arc (unclosed shape)"),
5200                             2, INKSCAPE_ICON_DRAW_ELLIPSE_ARC,
5201                             -1 );
5203         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5204         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5205         g_object_set_data( holder, "open_action", act );
5207         ege_select_one_action_set_appearance( act, "full" );
5208         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5209         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5210         ege_select_one_action_set_icon_column( act, 2 );
5211         ege_select_one_action_set_icon_size( act, secondarySize );
5212         ege_select_one_action_set_tooltip_column( act, 1  );
5214         bool isClosed = !prefs->getBool("/tools/shapes/arc/open", false);
5215         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
5216         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
5217     }
5219     /* Make Whole */
5220     {
5221         InkAction* inky = ink_action_new( "ArcResetAction",
5222                                           _("Make whole"),
5223                                           _("Make the shape a whole ellipse, not arc or segment"),
5224                                           INKSCAPE_ICON_DRAW_ELLIPSE_WHOLE,
5225                                           secondarySize );
5226         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
5227         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5228         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
5229         g_object_set_data( holder, "make_whole", inky );
5230     }
5232     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
5233     // sensitivize make whole and open checkbox
5234     {
5235         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
5236         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
5237         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
5238     }
5241     sigc::connection *connection = new sigc::connection(
5242         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
5243         );
5244     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
5245     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
5251 // toggle button callbacks and updaters
5253 //########################
5254 //##      Dropper       ##
5255 //########################
5257 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
5258     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5259     prefs->setInt( "/tools/dropper/pick", gtk_toggle_action_get_active( act ) );
5260     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
5261     if ( set_action ) {
5262         if ( gtk_toggle_action_get_active( act ) ) {
5263             gtk_action_set_sensitive( set_action, TRUE );
5264         } else {
5265             gtk_action_set_sensitive( set_action, FALSE );
5266         }
5267     }
5269     spinbutton_defocus(GTK_OBJECT(tbl));
5272 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
5273     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5274     prefs->setBool( "/tools/dropper/setalpha", gtk_toggle_action_get_active( act ) );
5275     spinbutton_defocus(GTK_OBJECT(tbl));
5279 /**
5280  * Dropper auxiliary toolbar construction and setup.
5281  *
5282  * TODO: Would like to add swatch of current color.
5283  * TODO: Add queue of last 5 or so colors selected with new swatches so that
5284  *       can drag and drop places. Will provide a nice mixing palette.
5285  */
5286 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
5288     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5289     gint pickAlpha = prefs->getInt( "/tools/dropper/pick", 1 );
5291     {
5292         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
5293         ege_output_action_set_use_markup( act, TRUE );
5294         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5295     }
5297     {
5298         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
5299                                                       _("Pick opacity"),
5300                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
5301                                                       NULL,
5302                                                       Inkscape::ICON_SIZE_DECORATION );
5303         g_object_set( act, "short_label", _("Pick"), NULL );
5304         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5305         g_object_set_data( holder, "pick_action", act );
5306         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
5307         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
5308     }
5310     {
5311         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
5312                                                       _("Assign opacity"),
5313                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
5314                                                       NULL,
5315                                                       Inkscape::ICON_SIZE_DECORATION );
5316         g_object_set( act, "short_label", _("Assign"), NULL );
5317         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5318         g_object_set_data( holder, "set_action", act );
5319         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/dropper/setalpha", true) );
5320         // make sure it's disabled if we're not picking alpha
5321         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
5322         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
5323     }
5327 //########################
5328 //##      LPETool       ##
5329 //########################
5331 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
5333 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
5334 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
5336     using namespace Inkscape::LivePathEffect;
5338     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
5339     SPEventContext *ec = desktop->event_context;
5340     if (!SP_IS_LPETOOL_CONTEXT(ec)) {
5341         return;
5342     }
5344     // only take action if run by the attr_changed listener
5345     if (!g_object_get_data(tbl, "freeze")) {
5346         // in turn, prevent listener from responding
5347         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5349         gint mode = ege_select_one_action_get_active(act);
5350         EffectType type = lpesubtools[mode];
5352         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5353         bool success = lpetool_try_construction(lc, type);
5354         if (success) {
5355             // since the construction was already performed, we set the state back to inactive
5356             ege_select_one_action_set_active(act, 0);
5357             mode = 0;
5358         } else {
5359             // switch to the chosen subtool
5360             SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
5361         }
5363         if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5364             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5365             prefs->setInt( "/tools/lpetool/mode", mode );
5366         }
5368         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
5369     }
5372 void sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject */*tbl*/)
5374     SPEventContext *ec = selection->desktop()->event_context;
5375     if (!SP_IS_LPETOOL_CONTEXT(ec))
5376         return;
5378     lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
5381 void
5382 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
5384     using namespace Inkscape::LivePathEffect;
5385     SPEventContext *ec = selection->desktop()->event_context;
5386     if (!SP_IS_LPETOOL_CONTEXT(ec))
5387         return;
5388     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
5390     lpetool_delete_measuring_items(lc);
5391     lpetool_create_measuring_items(lc, selection);
5393     // activate line segment combo box if a single item with LPELineSegment is selected
5394     GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
5395     SPItem *item = selection->singleItem();
5396     if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
5397         SPLPEItem *lpeitem = SP_LPE_ITEM(item);
5398         Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
5399         if (lpe && lpe->effectType() == LINE_SEGMENT) {
5400             LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
5401             g_object_set_data(tbl, "currentlpe", lpe);
5402             g_object_set_data(tbl, "currentlpeitem", lpeitem);
5403             gtk_action_set_sensitive(w, TRUE);
5404             ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
5405         } else {
5406             g_object_set_data(tbl, "currentlpe", NULL);
5407             g_object_set_data(tbl, "currentlpeitem", NULL);
5408             gtk_action_set_sensitive(w, FALSE);
5409         }
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     }
5417 static void
5418 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
5419     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5420     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5422     bool show = gtk_toggle_action_get_active( act );
5423     prefs->setBool("/tools/lpetool/show_bbox",  show);
5425     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5426         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5427         lpetool_context_reset_limiting_bbox(lc);
5428     }
5431 static void
5432 lpetool_toggle_show_measuring_info (GtkToggleAction *act, GObject *tbl) {
5433     SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
5434     if (!tools_isactive(desktop, TOOLS_LPETOOL))
5435         return;
5437     GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
5438     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5439     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5440         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5441         bool show = gtk_toggle_action_get_active( act );
5442         prefs->setBool("/tools/lpetool/show_measuring_info",  show);
5443         lpetool_show_measuring_info(lc, show);
5444         gtk_action_set_sensitive(GTK_ACTION(unitact), show);
5445     }
5448 static void lpetool_unit_changed(GtkAction* /*act*/, GObject* tbl) {
5449     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
5450     SPUnit const *unit = tracker->getActiveUnit();
5451     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5452     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5454     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5455     if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
5456         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5457         lpetool_delete_measuring_items(lc);
5458         lpetool_create_measuring_items(lc);
5459     }
5462 static void
5463 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
5464     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5465     Inkscape::Selection *selection = desktop->selection;
5467     Geom::OptRect bbox = selection->bounds();
5469     if (bbox) {
5470         Geom::Point A(bbox->min());
5471         Geom::Point B(bbox->max());
5473         A *= desktop->doc2dt();
5474         B *= desktop->doc2dt();
5476         // TODO: should we provide a way to store points in prefs?
5477         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5478         prefs->setDouble("/tools/lpetool/bbox_upperleftx", A[Geom::X]);
5479         prefs->setDouble("/tools/lpetool/bbox_upperlefty", A[Geom::Y]);
5480         prefs->setDouble("/tools/lpetool/bbox_lowerrightx", B[Geom::X]);
5481         prefs->setDouble("/tools/lpetool/bbox_lowerrighty", B[Geom::Y]);
5483         lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
5484     }
5486     gtk_toggle_action_set_active(act, false);
5489 static void
5490 sp_line_segment_build_list(GObject *tbl)
5492     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
5494     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
5495     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
5496     gtk_list_store_clear (model);
5498     // TODO: we add the entries of rht combo box manually; later this should be done automatically
5499     {
5500         GtkTreeIter iter;
5501         gtk_list_store_append( model, &iter );
5502         gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
5503         gtk_list_store_append( model, &iter );
5504         gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
5505         gtk_list_store_append( model, &iter );
5506         gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
5507         gtk_list_store_append( model, &iter );
5508         gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
5509     }
5511     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5514 static void
5515 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
5516     using namespace Inkscape::LivePathEffect;
5518     // quit if run by the attr_changed listener
5519     if (g_object_get_data(tbl, "freeze")) {
5520         return;
5521     }
5523     // in turn, prevent listener from responding
5524     g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5526     LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
5527     SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5528     if (lpeitem) {
5529         SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5530         lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
5531         sp_lpe_item_update_patheffect(lpeitem, true, true);
5532     }
5534     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5537 static void
5538 lpetool_open_lpe_dialog (GtkToggleAction *act, gpointer data) {
5539     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5541     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5542         sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5543     }
5544     gtk_toggle_action_set_active(act, false);
5547 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5549     UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5550     tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5551     g_object_set_data(holder, "tracker", tracker);
5552     SPUnit const *unit = tracker->getActiveUnit();
5554     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5555     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5557     /** Automatically create a list of LPEs that get added to the toolbar **/
5558     {
5559         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5561         GtkTreeIter iter;
5563         // the first toggle button represents the state that no subtool is active (remove this when
5564         // this can be modeled by EgeSelectOneAction or some other action)
5565         gtk_list_store_append( model, &iter );
5566         gtk_list_store_set( model, &iter,
5567                             0, _("All inactive"),
5568                             1, _("No geometric tool is active"),
5569                             2, _("all_inactive"),
5570                             -1 );
5572         Inkscape::LivePathEffect::EffectType type;
5573         for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5574             type =  lpesubtools[i];
5575             gtk_list_store_append( model, &iter );
5576             gtk_list_store_set( model, &iter,
5577                                 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5578                                 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5579                                 2, Inkscape::LivePathEffect::LPETypeConverter.get_key(type).c_str(),
5580                                 -1 );
5581         }
5583         EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5584         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5585         g_object_set_data( holder, "lpetool_mode_action", act );
5587         ege_select_one_action_set_appearance( act, "full" );
5588         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5589         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5590         ege_select_one_action_set_icon_column( act, 2 );
5591         ege_select_one_action_set_tooltip_column( act, 1  );
5593         gint lpeToolMode = prefs->getInt("/tools/lpetool/mode", 0);
5594         ege_select_one_action_set_active( act, lpeToolMode );
5595         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5596     }
5598     /* Show limiting bounding box */
5599     {
5600         InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5601                                                       _("Show limiting bounding box"),
5602                                                       _("Show bounding box (used to cut infinite lines)"),
5603                                                       "lpetool_show_bbox",
5604                                                       Inkscape::ICON_SIZE_DECORATION );
5605         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5606         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5607         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_bbox", true ) );
5608     }
5610     /* Set limiting bounding box to bbox of current selection */
5611     {
5612         InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5613                                                       _("Get limiting bounding box from selection"),
5614                                                       _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5615                                                       "lpetool_set_bbox",
5616                                                       Inkscape::ICON_SIZE_DECORATION );
5617         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5618         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5619         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5620     }
5623     /* Combo box to choose line segment type */
5624     {
5625         GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5626         EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5627         ege_select_one_action_set_appearance (act, "compact");
5628         g_object_set_data (holder, "lpetool_line_segment_action", act );
5630         g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5632         sp_line_segment_build_list (holder);
5634         g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5635         gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5636         gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5637     }
5639     /* Display measuring info for selected items */
5640     {
5641         InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5642                                                       _("Display measuring info"),
5643                                                       _("Display measuring info for selected items"),
5644                                                       "lpetool_measuring_info",
5645                                                       Inkscape::ICON_SIZE_DECORATION );
5646         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5647         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
5648         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_measuring_info", true ) );
5649     }
5651     // add the units menu
5652     {
5653         GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5654         gtk_action_group_add_action( mainActions, act );
5655         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5656         g_object_set_data(holder, "lpetool_units_action", act);
5657         gtk_action_set_sensitive(act, prefs->getBool("/tools/lpetool/show_measuring_info", true));
5658     }
5660     /* Open LPE dialog (to adapt parameters numerically) */
5661     {
5662         InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
5663                                                       _("Open LPE dialog"),
5664                                                       _("Open LPE dialog (to adapt parameters numerically)"),
5665                                                       "lpetool_open_lpe_dialog",
5666                                                       Inkscape::ICON_SIZE_DECORATION );
5667         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5668         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
5669         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5670     }
5672     //watch selection
5673     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5675     sigc::connection *c_selection_modified =
5676         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5677                               (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
5678     pool->add_connection ("selection-modified", c_selection_modified);
5680     sigc::connection *c_selection_changed =
5681         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5682                               (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5683     pool->add_connection ("selection-changed", c_selection_changed);
5686 //########################
5687 //##       Eraser       ##
5688 //########################
5690 static void sp_erc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
5692     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5693     prefs->setDouble( "/tools/eraser/width", adj->value * 0.01 );
5694     update_presets_list(tbl);
5697 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5699     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5700     bool eraserMode = ege_select_one_action_get_active( act ) != 0;
5701     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5702         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5703         prefs->setBool( "/tools/eraser/mode", eraserMode );
5704     }
5706     // only take action if run by the attr_changed listener
5707     if (!g_object_get_data( tbl, "freeze" )) {
5708         // in turn, prevent listener from responding
5709         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5711         if ( eraserMode != 0 ) {
5712         } else {
5713         }
5714         // TODO finish implementation
5716         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5717     }
5720 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5722     {
5723         /* Width */
5724         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5725         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5726         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5727                                                               _("Pen Width"), _("Width:"),
5728                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
5729                                                               "/tools/eraser/width", 15,
5730                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5731                                                               1, 100, 1.0, 10.0,
5732                                                               labels, values, G_N_ELEMENTS(labels),
5733                                                               sp_erc_width_value_changed,  0.01, 0, 100 );
5734         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5735         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5736         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5737     }
5739     {
5740         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5742         GtkTreeIter iter;
5743         gtk_list_store_append( model, &iter );
5744         gtk_list_store_set( model, &iter,
5745                             0, _("Delete"),
5746                             1, _("Delete objects touched by the eraser"),
5747                             2, INKSCAPE_ICON_DRAW_ERASER_DELETE_OBJECTS,
5748                             -1 );
5750         gtk_list_store_append( model, &iter );
5751         gtk_list_store_set( model, &iter,
5752                             0, _("Cut"),
5753                             1, _("Cut out from objects"),
5754                             2, INKSCAPE_ICON_PATH_DIFFERENCE,
5755                             -1 );
5757         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5758         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5759         g_object_set_data( holder, "eraser_mode_action", act );
5761         ege_select_one_action_set_appearance( act, "full" );
5762         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5763         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5764         ege_select_one_action_set_icon_column( act, 2 );
5765         ege_select_one_action_set_tooltip_column( act, 1  );
5767         /// @todo Convert to boolean?
5768         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5769         gint eraserMode = prefs->getBool("/tools/eraser/mode") ? 1 : 0;
5770         ege_select_one_action_set_active( act, eraserMode );
5771         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
5772     }
5776 //########################
5777 //##    Text Toolbox    ##
5778 //########################
5779 /*
5780 static void
5781 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
5783     //Call back for letter sizing spinbutton
5786 static void
5787 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
5789     //Call back for line height spinbutton
5792 static void
5793 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5795     //Call back for horizontal kerning spinbutton
5798 static void
5799 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5801     //Call back for vertical kerning spinbutton
5804 static void
5805 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
5807     //Call back for letter rotation spinbutton
5808 }*/
5810 namespace {
5812 bool popdown_visible = false;
5813 bool popdown_hasfocus = false;
5815 void
5816 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
5818     SPStyle *query =
5819         sp_style_new (SP_ACTIVE_DOCUMENT);
5821 //    int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5823     int result_family =
5824         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5826     int result_style =
5827         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5829     int result_numbers =
5830         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5832     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5834     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5835     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
5836         // there are no texts in selection, read from prefs
5838             sp_style_read_from_prefs(query, "/tools/text");
5840             if (g_object_get_data(tbl, "text_style_from_prefs")) {
5841             // do not reset the toolbar style from prefs if we already did it last time
5842             sp_style_unref(query);
5843             return;
5844         }
5845         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
5846     } else {
5847         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
5848     }
5850     if (query->text)
5851     {
5852         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
5853             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5854             gtk_entry_set_text (GTK_ENTRY (entry), "");
5856         } else if (query->text->font_specification.value || query->text->font_family.value) {
5858             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5860             // Get the font that corresponds
5861             Glib::ustring familyName;
5863             font_instance * font = font_factory::Default()->FaceFromStyle(query);
5864             if (font) {
5865                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
5866                 font->Unref();
5867                 font = NULL;
5868             }
5870             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
5872             Gtk::TreePath path;
5873             try {
5874                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
5875             } catch (...) {
5876                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
5877                 sp_style_unref(query);
5878                 return;
5879             }
5881             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5882             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5884             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
5886             gtk_tree_selection_select_path (tselection, path.gobj());
5887             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5889             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
5890         }
5892         //Size
5893         {
5894             GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
5895             gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
5896             g_object_set_data(tbl, "size-block", gpointer(1));
5897             gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
5898             g_object_set_data(tbl, "size-block", gpointer(0));
5899             g_free(str);
5900         }
5902         //Anchor
5903         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
5904         {
5905             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
5906             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5907             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5908             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5909         }
5910         else
5911         {
5912             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
5913             {
5914                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
5915                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5916                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5917                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5918             }
5919             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
5920             {
5921                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
5922                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5923                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5924                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5925             }
5926             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
5927             {
5928                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
5929                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5930                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5931                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5932             }
5933         }
5935         //Style
5936         {
5937             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
5939             gboolean active = gtk_toggle_button_get_active (button);
5940             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
5942             if (active != check)
5943             {
5944                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5945                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5946                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5947             }
5948         }
5950         {
5951             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
5953             gboolean active = gtk_toggle_button_get_active (button);
5954             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
5956             if (active != check)
5957             {
5958                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5959                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5960                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5961             }
5962         }
5964         //Orientation
5965         //locking both buttons, changing one affect all group (both)
5966         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
5967         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5969         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
5970         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
5972         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
5973         {
5974             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5975         }
5976         else
5977         {
5978             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
5979         }
5980         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5981         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
5982     }
5984     sp_style_unref(query);
5987 void
5988 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
5990     sp_text_toolbox_selection_changed (selection, tbl);
5993 void
5994 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
5996     sp_text_toolbox_selection_changed (NULL, tbl);
5999 void
6000 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
6001                                 GObject             *tbl)
6003     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
6004     GtkTreeModel *model = 0;
6005     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6006     GtkTreeIter   iter;
6007     char         *family = 0;
6009     gdk_pointer_ungrab (GDK_CURRENT_TIME);
6010     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
6012     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
6013         return;
6014     }
6016     gtk_tree_model_get (model, &iter, 0, &family, -1);
6018     if (g_object_get_data (G_OBJECT (selection), "block"))
6019     {
6020         gtk_entry_set_text (GTK_ENTRY (entry), family);
6021         return;
6022     }
6024     gtk_entry_set_text (GTK_ENTRY (entry), family);
6026     SPStyle *query =
6027         sp_style_new (SP_ACTIVE_DOCUMENT);
6029     int result_fontspec =
6030         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6032     //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6034     SPCSSAttr *css = sp_repr_css_attr_new ();
6037     // First try to get the font spec from the stored value
6038     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
6040     if (fontSpec.empty()) {
6041         // Construct a new font specification if it does not yet exist
6042         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6043         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6044         fontFromStyle->Unref();
6045     }
6047     if (!fontSpec.empty()) {
6048         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
6049         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
6050             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
6051             if (font) {
6052                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6054                 // Set all the these just in case they were altered when finding the best
6055                 // match for the new family and old style...
6057                 gchar c[256];
6059                 font->Family(c, 256);
6060                 sp_repr_css_set_property (css, "font-family", c);
6062                 font->Attribute( "weight", c, 256);
6063                 sp_repr_css_set_property (css, "font-weight", c);
6065                 font->Attribute("style", c, 256);
6066                 sp_repr_css_set_property (css, "font-style", c);
6068                 font->Attribute("stretch", c, 256);
6069                 sp_repr_css_set_property (css, "font-stretch", c);
6071                 font->Attribute("variant", c, 256);
6072                 sp_repr_css_set_property (css, "font-variant", c);
6074                 font->Unref();
6075             }
6076         }
6077     }
6079     // If querying returned nothing, set the default style of the tool (for new texts)
6080     if (result_fontspec == QUERY_STYLE_NOTHING)
6081     {
6082         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6083         prefs->setStyle("/tools/text/style", css);
6084         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
6085     }
6086     else
6087     {
6088         sp_desktop_set_style (desktop, css, true, true);
6089     }
6091     sp_style_unref(query);
6093     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6094                                    _("Text: Change font family"));
6095     sp_repr_css_attr_unref (css);
6096     g_free(family);
6097     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6099     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6102 /* This is where execution comes when the contents of the font family box have been completed
6103    by the press of the return key */
6104 void
6105 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
6106                                        GObject      *tbl)
6108     const char *family = gtk_entry_get_text (entry);   // Fetch the requested font family
6110 // Try to match that to a known font. If not, then leave current font alone and remain focused on text box
6111     try {
6112         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
6113         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
6114         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
6115         gtk_tree_selection_select_path (selection, path.gobj());
6116         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
6117         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6118     } catch (...) {
6119         if (family && strlen (family))
6120         {
6121             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6122         }
6123     }
6126 void
6127 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
6128                                    gpointer          data)
6130     if (g_object_get_data (G_OBJECT (button), "block")) return;
6131     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
6132     int prop = GPOINTER_TO_INT(data);
6134     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6135     SPCSSAttr *css = sp_repr_css_attr_new ();
6137     switch (prop)
6138     {
6139         case 0:
6140         {
6141             sp_repr_css_set_property (css, "text-anchor", "start");
6142             sp_repr_css_set_property (css, "text-align", "start");
6143             break;
6144         }
6145         case 1:
6146         {
6147             sp_repr_css_set_property (css, "text-anchor", "middle");
6148             sp_repr_css_set_property (css, "text-align", "center");
6149             break;
6150         }
6152         case 2:
6153         {
6154             sp_repr_css_set_property (css, "text-anchor", "end");
6155             sp_repr_css_set_property (css, "text-align", "end");
6156             break;
6157         }
6159         case 3:
6160         {
6161             sp_repr_css_set_property (css, "text-anchor", "start");
6162             sp_repr_css_set_property (css, "text-align", "justify");
6163             break;
6164         }
6165     }
6167     SPStyle *query =
6168         sp_style_new (SP_ACTIVE_DOCUMENT);
6169     int result_numbers =
6170         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6172     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6173     if (result_numbers == QUERY_STYLE_NOTHING)
6174     {
6175         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6176         prefs->setStyle("/tools/text/style", css);
6177     }
6179     sp_style_unref(query);
6181     sp_desktop_set_style (desktop, css, true, true);
6182     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6183                                    _("Text: Change alignment"));
6184     sp_repr_css_attr_unref (css);
6186     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6189 void
6190 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
6191                                gpointer          data)
6193     if (g_object_get_data (G_OBJECT (button), "block")) return;
6195     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
6196     SPCSSAttr   *css        = sp_repr_css_attr_new ();
6197     int          prop       = GPOINTER_TO_INT(data);
6198     bool         active     = gtk_toggle_button_get_active (button);
6200     SPStyle *query =
6201         sp_style_new (SP_ACTIVE_DOCUMENT);
6203     int result_fontspec =
6204         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6206     //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6207     //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6208     //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6210     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
6211     Glib::ustring newFontSpec = "";
6213     if (fontSpec.empty()) {
6214         // Construct a new font specification if it does not yet exist
6215         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6216         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6217         fontFromStyle->Unref();
6218     }
6220     switch (prop)
6221     {
6222         case 0:
6223         {
6224             if (!fontSpec.empty()) {
6225                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
6226             }
6227             if (fontSpec != newFontSpec) {
6228                 // Don't even set the bold if the font didn't exist on the system
6229                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
6230             }
6231             break;
6232         }
6234         case 1:
6235         {
6236             if (!fontSpec.empty()) {
6237                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
6238             }
6239             if (fontSpec != newFontSpec) {
6240                 // Don't even set the italic if the font didn't exist on the system
6241                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
6242             }
6243             break;
6244         }
6245     }
6247     if (!newFontSpec.empty()) {
6248         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6249     }
6251     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6252     if (result_fontspec == QUERY_STYLE_NOTHING)
6253     {
6254         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6255         prefs->setStyle("/tools/text/style", css);
6256     }
6258     sp_style_unref(query);
6260     sp_desktop_set_style (desktop, css, true, true);
6261     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6262                                    _("Text: Change font style"));
6263     sp_repr_css_attr_unref (css);
6265     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6268 void
6269 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
6270                                      gpointer         data)
6272     if (g_object_get_data (G_OBJECT (button), "block")) {
6273         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6274         return;
6275     }
6277     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
6278     SPCSSAttr   *css        = sp_repr_css_attr_new ();
6279     int          prop       = GPOINTER_TO_INT(data);
6281     switch (prop)
6282     {
6283         case 0:
6284         {
6285             sp_repr_css_set_property (css, "writing-mode", "lr");
6286             break;
6287         }
6289         case 1:
6290         {
6291             sp_repr_css_set_property (css, "writing-mode", "tb");
6292             break;
6293         }
6294     }
6296     SPStyle *query =
6297         sp_style_new (SP_ACTIVE_DOCUMENT);
6298     int result_numbers =
6299         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6301     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6302     if (result_numbers == QUERY_STYLE_NOTHING)
6303     {
6304         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6305         prefs->setStyle("/tools/text/style", css);
6306     }
6308     sp_desktop_set_style (desktop, css, true, true);
6309     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6310                                    _("Text: Change orientation"));
6311     sp_repr_css_attr_unref (css);
6313     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6316 gboolean
6317 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6319     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6320     if (!desktop) return FALSE;
6322     switch (get_group0_keyval (event)) {
6323         case GDK_Escape: // defocus
6324             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6325             sp_text_toolbox_selection_changed (NULL, tbl); // update
6326             return TRUE; // I consumed the event
6327             break;
6328     }
6329     return FALSE;
6332 gboolean
6333 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
6335     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6336     if (!desktop) return FALSE;
6338     switch (get_group0_keyval (event)) {
6339         case GDK_KP_Enter:
6340         case GDK_Return:
6341         case GDK_Escape: // defocus
6342             gtk_widget_hide (w);
6343             popdown_visible = false;
6344             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6345             return TRUE; // I consumed the event
6346             break;
6347         case GDK_w:
6348         case GDK_W:
6349             if (event->state & GDK_CONTROL_MASK) {
6350                 gtk_widget_hide (w);
6351                 popdown_visible = false;
6352                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6353                 return TRUE; // I consumed the event
6354             }
6355             break;
6356     }
6357     return FALSE;
6361 void
6362 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
6363                                GObject     *tbl)
6365     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6367     if (g_object_get_data (tbl, "size-block")) return;
6369     // If this is not from selecting a size in the list (in which case get_active will give the
6370     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
6371     // process this event. This fixes GTK's stupid insistence on sending an activate change every
6372     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
6373     if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
6374         return;
6376     gdouble value = -1;
6377     {
6378         gchar *endptr;
6379         gchar *const text = gtk_combo_box_get_active_text(cbox);
6380         if (text) {
6381             value = g_strtod(text, &endptr);
6382             if (endptr == text) {  // Conversion failed, non-numeric input.
6383                 value = -1;
6384             }
6385             g_free(text);
6386         }
6387     }
6388     if (value <= 0) {
6389         return; // could not parse value
6390     }
6392     SPCSSAttr *css = sp_repr_css_attr_new ();
6393     Inkscape::CSSOStringStream osfs;
6394     osfs << value;
6395     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
6397     SPStyle *query =
6398         sp_style_new (SP_ACTIVE_DOCUMENT);
6399     int result_numbers =
6400         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6402     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6403     if (result_numbers == QUERY_STYLE_NOTHING)
6404     {
6405         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6406         prefs->setStyle("/tools/text/style", css);
6407     }
6409     sp_style_unref(query);
6411     sp_desktop_set_style (desktop, css, true, true);
6412     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
6413                                    _("Text: Change font size"));
6414     sp_repr_css_attr_unref (css);
6416     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6419 gboolean
6420 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
6422     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6423     if (!desktop) return FALSE;
6425     if (!g_object_get_data (tbl, "esc-pressed")) {
6426         g_object_set_data (tbl, "enter-pressed", gpointer(1));
6427         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6428         sp_text_toolbox_size_changed (cbox, tbl);
6429         g_object_set_data (tbl, "enter-pressed", gpointer(0));
6430     }
6431     return FALSE; // I consumed the event
6435 gboolean
6436 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6438     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6439     if (!desktop) return FALSE;
6441     switch (get_group0_keyval (event)) {
6442         case GDK_Escape: // defocus
6443             g_object_set_data (tbl, "esc-pressed", gpointer(1));
6444             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6445             g_object_set_data (tbl, "esc-pressed", gpointer(0));
6446             return TRUE; // I consumed the event
6447             break;
6448         case GDK_Return: // defocus
6449         case GDK_KP_Enter:
6450             g_object_set_data (tbl, "enter-pressed", gpointer(1));
6451             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6452             sp_text_toolbox_size_changed (cbox, tbl);
6453             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6454             g_object_set_data (tbl, "enter-pressed", gpointer(0));
6455             return TRUE; // I consumed the event
6456             break;
6457     }
6458     return FALSE;
6461 void
6462 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
6463                                          GObject            *tbl)
6465     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
6466     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6467     int x, y;
6469     if (!popdown_visible)
6470     {
6471         gdk_window_get_origin (widget->window, &x, &y);
6472         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
6473         gtk_widget_show_all (popdown);
6474         //sp_transientize (popdown);
6476         gdk_pointer_grab (widget->window, TRUE,
6477                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
6478                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
6479                                         GDK_POINTER_MOTION_MASK),
6480                           NULL, NULL, GDK_CURRENT_TIME);
6482         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
6484         popdown_visible = true;
6485     }
6486     else
6487     {
6488         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6489         gdk_pointer_ungrab (GDK_CURRENT_TIME);
6490         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
6491         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6492         gtk_widget_hide (popdown);
6493         popdown_visible = false;
6494     }
6497 gboolean
6498 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
6499                                  GdkEventFocus    */*event*/,
6500                                  GObject          */*tbl*/)
6502     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
6503     return FALSE;
6506 gboolean
6507 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
6508                                    GdkEventFocus    */*event*/,
6509                                    GObject          */*tbl*/)
6511     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6513     if (popdown_hasfocus) {
6514         gtk_widget_hide (popdown);
6515         popdown_hasfocus = false;
6516         popdown_visible = false;
6517         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6518         return TRUE;
6519     }
6520     return FALSE;
6523 gboolean
6524 sp_text_toolbox_popdown_focus_in (GtkWidget        */*popdown*/,
6525                                    GdkEventFocus    */*event*/,
6526                                    GObject          */*tbl*/)
6528     popdown_hasfocus = true;
6529     return TRUE;
6533 void
6534 cell_data_func  (GtkTreeViewColumn */*column*/,
6535                  GtkCellRenderer   *cell,
6536                  GtkTreeModel      *tree_model,
6537                  GtkTreeIter       *iter,
6538                  gpointer           /*data*/)
6540     gchar *family;
6541     gtk_tree_model_get(tree_model, iter, 0, &family, -1);
6542     gchar *const family_escaped = g_markup_escape_text(family, -1);
6544     static char const *const sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
6545     gchar *const sample_escaped = g_markup_escape_text(sample, -1);
6547     std::stringstream markup;
6548     markup << family_escaped << "  <span foreground='darkgray' font_family='"
6549            << family_escaped << "'>" << sample_escaped << "</span>";
6550     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6552     g_free(family);
6553     g_free(family_escaped);
6554     g_free(sample_escaped);
6557 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
6558     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
6559     if (completion) {
6560         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
6561         g_object_unref (completion);
6562     }
6565 GtkWidget*
6566 sp_text_toolbox_new (SPDesktop *desktop)
6568     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
6569     GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("/toolbox/secondary", 1));
6571     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
6572     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
6574     GtkTooltips *tt = gtk_tooltips_new();
6575     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
6577     ////////////Family
6578     //Window
6579     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
6580     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
6582     //Entry
6583     GtkWidget           *entry = gtk_entry_new ();
6584     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
6585     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
6586     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
6587     gtk_entry_completion_set_text_column (completion, 0);
6588     gtk_entry_completion_set_minimum_key_length (completion, 1);
6589     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
6590     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
6591     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
6592     gtk_toolbar_append_widget( tbl, entry, "", "" );
6593     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
6595     //Button
6596     GtkWidget   *button = gtk_button_new ();
6597     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
6598     gtk_toolbar_append_widget( tbl, button, "", "");
6600     //Popdown
6601     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
6602     GtkWidget           *treeview = gtk_tree_view_new ();
6604     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
6605     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
6606     gtk_tree_view_column_pack_start (column, cell, FALSE);
6607     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
6608     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
6609     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
6611     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
6612     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
6613     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
6615     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
6617     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
6618     gtk_container_add (GTK_CONTAINER (sw), treeview);
6620     gtk_container_add (GTK_CONTAINER (window), sw);
6621     gtk_widget_set_size_request (window, 300, 450);
6623     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
6624     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
6625     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
6627     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
6629     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
6630     g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
6631     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
6633     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
6634     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6636     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
6637     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
6638     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
6639     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
6640     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
6642     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
6643     GtkWidget *box = gtk_event_box_new ();
6644     gtk_container_add (GTK_CONTAINER (box), image);
6645     gtk_toolbar_append_widget( tbl, box, "", "");
6646     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
6647     GtkTooltips *tooltips = gtk_tooltips_new ();
6648     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
6649     gtk_widget_hide (GTK_WIDGET (box));
6650     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
6652     ////////////Size
6653     gchar const *const sizes[] = {
6654         "4", "6", "8", "9", "10", "11", "12", "13", "14",
6655         "16", "18", "20", "22", "24", "28",
6656         "32", "36", "40", "48", "56", "64", "72", "144"
6657     };
6659     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
6660     for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
6661         gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
6662     }
6663     gtk_widget_set_size_request (cbox, 80, -1);
6664     gtk_toolbar_append_widget( tbl, cbox, "", "");
6665     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
6666     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
6667     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
6668     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
6670     ////////////Text anchor
6671     GtkWidget *group   = gtk_radio_button_new (NULL);
6672     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
6673     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
6675     // left
6676     GtkWidget *rbutton = group;
6677     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6678     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
6679     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6681     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6682     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
6683     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
6684     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
6686     // center
6687     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6688     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6689     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
6690     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6692     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6693     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
6694     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
6695     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
6697     // right
6698     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6699     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6700     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
6701     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6703     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6704     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
6705     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
6706     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
6708     // fill
6709     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6710     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6711     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
6712     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6714     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6715     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
6716     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
6717     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
6719     gtk_toolbar_append_widget( tbl, row, "", "");
6721     //spacer
6722     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6724     ////////////Text style
6725     row = gtk_hbox_new (FALSE, 4);
6727     // bold
6728     rbutton = gtk_toggle_button_new ();
6729     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6730     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
6731     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6732     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
6734     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6735     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
6736     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
6738     // italic
6739     rbutton = gtk_toggle_button_new ();
6740     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6741     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
6742     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6743     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
6745     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6746     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
6747     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
6749     gtk_toolbar_append_widget( tbl, row, "", "");
6751     //spacer
6752     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6754     // Text orientation
6755     group   = gtk_radio_button_new (NULL);
6756     row     = gtk_hbox_new (FALSE, 4);
6757     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
6759     // horizontal
6760     rbutton = group;
6761     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6762     gtk_container_add           (GTK_CONTAINER (rbutton),
6763                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_HORIZONTAL));
6764     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6765     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
6767     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6768     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
6769     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
6771     // vertical
6772     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6773     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6774     gtk_container_add           (GTK_CONTAINER (rbutton),
6775                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_VERTICAL));
6776     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6777     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
6779     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6780     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
6781     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
6782     gtk_toolbar_append_widget( tbl, row, "", "" );
6785     //watch selection
6786     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
6788     sigc::connection *c_selection_changed =
6789         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
6790                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
6791     pool->add_connection ("selection-changed", c_selection_changed);
6793     sigc::connection *c_selection_modified =
6794         new sigc::connection (sp_desktop_selection (desktop)->connectModified
6795                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
6796     pool->add_connection ("selection-modified", c_selection_modified);
6798     sigc::connection *c_subselection_changed =
6799         new sigc::connection (desktop->connectToolSubselectionChanged
6800                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
6801     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
6803     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
6806     gtk_widget_show_all( GTK_WIDGET(tbl) );
6808     return GTK_WIDGET(tbl);
6809 } // end of sp_text_toolbox_new()
6811 }//<unnamed> namespace
6814 //#########################
6815 //##      Connector      ##
6816 //#########################
6818 static void sp_connector_path_set_avoid(void)
6820     cc_selection_set_avoid(true);
6824 static void sp_connector_path_set_ignore(void)
6826     cc_selection_set_avoid(false);
6831 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
6833     // quit if run by the _changed callbacks
6834     if (g_object_get_data( tbl, "freeze" )) {
6835         return;
6836     }
6838     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
6839     SPDocument *doc = sp_desktop_document(desktop);
6841     if (!sp_document_get_undo_sensitive(doc))
6842     {
6843         return;
6844     }
6846     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6848     if ( repr->attribute("inkscape:connector-spacing") ) {
6849         gdouble priorValue = gtk_adjustment_get_value(adj);
6850         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
6851         if ( priorValue == gtk_adjustment_get_value(adj) ) {
6852             return;
6853         }
6854     } else if ( adj->value == defaultConnSpacing ) {
6855         return;
6856     }
6858     // in turn, prevent callbacks from responding
6859     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6861     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
6862     SP_OBJECT(desktop->namedview)->updateRepr();
6864     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
6865     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
6866         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
6867         Geom::Matrix m = Geom::identity();
6868         avoid_item_move(&m, item);
6869     }
6871     if (items) {
6872         g_slist_free(items);
6873     }
6875     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
6876             _("Change connector spacing"));
6878     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6880     spinbutton_defocus(GTK_OBJECT(tbl));
6883 static void sp_connector_graph_layout(void)
6885     if (!SP_ACTIVE_DESKTOP) return;
6886     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6888     // hack for clones, see comment in align-and-distribute.cpp
6889     int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
6890     prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
6892     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
6894     prefs->setInt("/options/clonecompensation/value", saved_compensation);
6896     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
6899 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6901     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6902     prefs->setBool("/tools/connector/directedlayout",
6903                 gtk_toggle_action_get_active( act ));
6906 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6908     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6909     prefs->setBool("/tools/connector/avoidoverlaplayout",
6910                 gtk_toggle_action_get_active( act ));
6914 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
6916     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6917     prefs->setDouble("/tools/connector/length", adj->value);
6918     spinbutton_defocus(GTK_OBJECT(tbl));
6921 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
6922                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
6923                                             bool /*is_interactive*/, gpointer data)
6925     GtkWidget *tbl = GTK_WIDGET(data);
6927     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6928         return;
6929     }
6930     if (strcmp(name, "inkscape:connector-spacing") != 0) {
6931         return;
6932     }
6934     GtkAdjustment *adj = (GtkAdjustment*)
6935             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
6936     gdouble spacing = defaultConnSpacing;
6937     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
6939     gtk_adjustment_set_value(adj, spacing);
6943 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
6944     NULL, /* child_added */
6945     NULL, /* child_removed */
6946     connector_tb_event_attr_changed,
6947     NULL, /* content_changed */
6948     NULL  /* order_changed */
6949 };
6952 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
6954     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6955     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
6957     {
6958         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
6959                                           _("Avoid"),
6960                                           _("Make connectors avoid selected objects"),
6961                                           INKSCAPE_ICON_CONNECTOR_AVOID,
6962                                           secondarySize );
6963         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
6964         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6965     }
6967     {
6968         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
6969                                           _("Ignore"),
6970                                           _("Make connectors ignore selected objects"),
6971                                           INKSCAPE_ICON_CONNECTOR_IGNORE,
6972                                           secondarySize );
6973         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
6974         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6975     }
6977     EgeAdjustmentAction* eact = 0;
6979     // Spacing spinbox
6980     eact = create_adjustment_action( "ConnectorSpacingAction",
6981                                      _("Connector Spacing"), _("Spacing:"),
6982                                      _("The amount of space left around objects by auto-routing connectors"),
6983                                      "/tools/connector/spacing", defaultConnSpacing,
6984                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
6985                                      0, 100, 1.0, 10.0,
6986                                      0, 0, 0,
6987                                      connector_spacing_changed, 1, 0 );
6988     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6990     // Graph (connector network) layout
6991     {
6992         InkAction* inky = ink_action_new( "ConnectorGraphAction",
6993                                           _("Graph"),
6994                                           _("Nicely arrange selected connector network"),
6995                                           INKSCAPE_ICON_DISTRIBUTE_GRAPH,
6996                                           secondarySize );
6997         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
6998         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6999     }
7001     // Default connector length spinbox
7002     eact = create_adjustment_action( "ConnectorLengthAction",
7003                                      _("Connector Length"), _("Length:"),
7004                                      _("Ideal length for connectors when layout is applied"),
7005                                      "/tools/connector/length", 100,
7006                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
7007                                      10, 1000, 10.0, 100.0,
7008                                      0, 0, 0,
7009                                      connector_length_changed, 1, 0 );
7010     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7013     // Directed edges toggle button
7014     {
7015         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
7016                                                       _("Downwards"),
7017                                                       _("Make connectors with end-markers (arrows) point downwards"),
7018                                                       INKSCAPE_ICON_DISTRIBUTE_GRAPH_DIRECTED,
7019                                                       Inkscape::ICON_SIZE_DECORATION );
7020         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7022         bool tbuttonstate = prefs->getBool("/tools/connector/directedlayout");
7023         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7025         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
7026     }
7028     // Avoid overlaps toggle button
7029     {
7030         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
7031                                                       _("Remove overlaps"),
7032                                                       _("Do not allow overlapping shapes"),
7033                                                       INKSCAPE_ICON_DISTRIBUTE_REMOVE_OVERLAPS,
7034                                                       Inkscape::ICON_SIZE_DECORATION );
7035         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7037         bool tbuttonstate = prefs->getBool("/tools/connector/avoidoverlaplayout");
7038         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), (tbuttonstate ? TRUE : FALSE ));
7040         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
7041     }
7043     // Code to watch for changes to the connector-spacing attribute in
7044     // the XML.
7045     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7046     g_assert(repr != NULL);
7048     purge_repr_listener( holder, holder );
7050     if (repr) {
7051         g_object_set_data( holder, "repr", repr );
7052         Inkscape::GC::anchor(repr);
7053         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
7054         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
7055     }
7056 } // end of sp_connector_toolbox_prep()
7059 //#########################
7060 //##     Paintbucket     ##
7061 //#########################
7063 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
7065     gint channels = ege_select_one_action_get_active( act );
7066     flood_channels_set_channels( channels );
7069 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
7071     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7072     prefs->setInt("/tools/paintbucket/threshold", (gint)adj->value);
7075 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
7077     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7078     prefs->setBool("/tools/paintbucket/autogap", ege_select_one_action_get_active( act ));
7081 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
7083     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
7084     SPUnit const *unit = tracker->getActiveUnit();
7085     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7087     prefs->setDouble("/tools/paintbucket/offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
7088     prefs->setString("/tools/paintbucket/offsetunits", sp_unit_get_abbreviation(unit));
7091 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
7093     // FIXME: make defaults settable via Inkscape Options
7094     struct KeyValue {
7095         char const *key;
7096         double value;
7097     } const key_values[] = {
7098         {"threshold", 15},
7099         {"offset", 0.0}
7100     };
7102     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
7103         KeyValue const &kv = key_values[i];
7104         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
7105         if ( adj ) {
7106             gtk_adjustment_set_value(adj, kv.value);
7107         }
7108     }
7110     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
7111     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
7112     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
7113     ege_select_one_action_set_active( autogap_action, 0 );
7116 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
7118     EgeAdjustmentAction* eact = 0;
7119     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7121     {
7122         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7124         GList* items = 0;
7125         gint count = 0;
7126         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
7127         {
7128             GtkTreeIter iter;
7129             gtk_list_store_append( model, &iter );
7130             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7131             count++;
7132         }
7133         g_list_free( items );
7134         items = 0;
7135         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
7136         g_object_set( act1, "short_label", _("Fill by:"), NULL );
7137         ege_select_one_action_set_appearance( act1, "compact" );
7138         ege_select_one_action_set_active( act1, prefs->getInt("/tools/paintbucket/channels", 0) );
7139         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
7140         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
7141         g_object_set_data( holder, "channels_action", act1 );
7142     }
7144     // Spacing spinbox
7145     {
7146         eact = create_adjustment_action(
7147             "ThresholdAction",
7148             _("Fill Threshold"), _("Threshold:"),
7149             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
7150             "/tools/paintbucket/threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
7151             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
7152             0, 0, 0,
7153             paintbucket_threshold_changed, 1, 0 );
7155         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
7156         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7157     }
7159     // Create the units menu.
7160     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
7161     Glib::ustring stored_unit = prefs->getString("/tools/paintbucket/offsetunits");
7162     if (!stored_unit.empty())
7163         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit.data()));
7164     g_object_set_data( holder, "tracker", tracker );
7165     {
7166         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
7167         gtk_action_group_add_action( mainActions, act );
7168     }
7170     // Offset spinbox
7171     {
7172         eact = create_adjustment_action(
7173             "OffsetAction",
7174             _("Grow/shrink by"), _("Grow/shrink by:"),
7175             _("The amount to grow (positive) or shrink (negative) the created fill path"),
7176             "/tools/paintbucket/offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
7177             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
7178             0, 0, 0,
7179             paintbucket_offset_changed, 1, 2);
7180         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
7182         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7183     }
7185     /* Auto Gap */
7186     {
7187         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7189         GList* items = 0;
7190         gint count = 0;
7191         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
7192         {
7193             GtkTreeIter iter;
7194             gtk_list_store_append( model, &iter );
7195             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7196             count++;
7197         }
7198         g_list_free( items );
7199         items = 0;
7200         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
7201         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
7202         ege_select_one_action_set_appearance( act2, "compact" );
7203         ege_select_one_action_set_active( act2, prefs->getBool("/tools/paintbucket/autogap") );
7204         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
7205         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
7206         g_object_set_data( holder, "autogap_action", act2 );
7207     }
7209     /* Reset */
7210     {
7211         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
7212                                           _("Defaults"),
7213                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
7214                                           GTK_STOCK_CLEAR );
7215         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
7216         gtk_action_group_add_action( mainActions, act );
7217         gtk_action_set_sensitive( act, TRUE );
7218     }
7222 /*
7223   Local Variables:
7224   mode:c++
7225   c-file-style:"stroustrup"
7226   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
7227   indent-tabs-mode:nil
7228   fill-column:99
7229   End:
7230 */
7231 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :