Code

updated spanish.nsh and inkscape.nsi to reflect latest file-changes
[inkscape.git] / trunk / 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 "calligraphic-profile-rename.h"
40 #include "../conn-avoid-ref.h"
41 #include "../connection-pool.h"
42 #include "../connector-context.h"
43 #include "../desktop.h"
44 #include "../desktop-handles.h"
45 #include "../desktop-style.h"
46 #include "../dialogs/dialog-events.h"
47 #include "../dialogs/text-edit.h"
48 #include "../document-private.h"
49 #include "../ege-adjustment-action.h"
50 #include "../ege-output-action.h"
51 #include "../ege-select-one-action.h"
52 #include "../flood-context.h"
53 #include "gradient-toolbar.h"
54 #include "../graphlayout/graphlayout.h"
55 #include "../helper/unit-menu.h"
56 #include "../helper/units.h"
57 #include "../helper/unit-tracker.h"
58 #include "icon.h"
59 #include "../ink-action.h"
60 #include "../inkscape.h"
61 #include "../inkscape-stock.h"
62 #include "../interface.h"
63 #include "../libnrtype/font-instance.h"
64 #include "../libnrtype/font-lister.h"
65 #include "../live_effects/effect.h"
66 #include "../live_effects/lpe-angle_bisector.h"
67 #include "../live_effects/lpe-line_segment.h"
68 #include "../lpe-tool-context.h"
69 #include "../mod360.h"
70 #include "../node-context.h"
71 #include "../pen-context.h"
72 #include "../preferences.h"
73 #include "../selection-chemistry.h"
74 #include "../selection.h"
75 #include "select-toolbar.h"
76 #include "../shape-editor.h"
77 #include "../shortcuts.h"
78 #include "../sp-clippath.h"
79 #include "../sp-ellipse.h"
80 #include "../sp-flowtext.h"
81 #include "../sp-mask.h"
82 #include "../sp-namedview.h"
83 #include "../sp-rect.h"
84 #include "../sp-spiral.h"
85 #include "../sp-star.h"
86 #include "../sp-text.h"
87 #include "../style.h"
88 #include "../svg/css-ostringstream.h"
89 #include "../tools-switch.h"
90 #include "../tweak-context.h"
91 #include "../ui/widget/style-swatch.h"
92 #include "../verbs.h"
93 #include "../widgets/button.h"
94 #include "../widgets/spinbutton-events.h"
95 #include "../widgets/spw-utilities.h"
96 #include "../widgets/widget-sizes.h"
97 #include "../xml/attribute-record.h"
98 #include "../xml/node-event-vector.h"
99 #include "../xml/repr.h"
101 #include "toolbox.h"
103 using Inkscape::UnitTracker;
105 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
106 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
108 static void       sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
109 static void       sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
110 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
111 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
112 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
113 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
114 static void       box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
115 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
116 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
117 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
118 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
119 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
120 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
121 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
122 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
123 static void       sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 static void       sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
126 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
129 Inkscape::IconSize prefToSize( Glib::ustring const &path, int base ) {
130     static Inkscape::IconSize sizeChoices[] = {
131         Inkscape::ICON_SIZE_LARGE_TOOLBAR,
132         Inkscape::ICON_SIZE_SMALL_TOOLBAR,
133         Inkscape::ICON_SIZE_MENU
134     };
135     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
136     int index = prefs->getIntLimited( path, base, 0, G_N_ELEMENTS(sizeChoices) );
137     return sizeChoices[index];
140 static struct {
141     gchar const *type_name;
142     gchar const *data_name;
143     sp_verb_t verb;
144     sp_verb_t doubleclick_verb;
145 } const tools[] = {
146     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
147     { "SPNodeContext",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
148     { "SPTweakContext",    "tweak_tool",     SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
149     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
150     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
151     { "Box3DContext",      "3dbox_tool",     SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
152     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
153     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
154     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
155     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
156     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
157     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
158     { "SPLPEToolContext",  "lpetool_tool",   SP_VERB_CONTEXT_LPETOOL, SP_VERB_CONTEXT_LPETOOL_PREFS },
159     { "SPEraserContext",   "eraser_tool",    SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
160     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
161     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
162     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
163     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
164     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
165     { NULL, NULL, 0, 0 }
166 };
168 static struct {
169     gchar const *type_name;
170     gchar const *data_name;
171     GtkWidget *(*create_func)(SPDesktop *desktop);
172     void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
173     gchar const *ui_name;
174     gint swatch_verb_id;
175     gchar const *swatch_tool;
176     gchar const *swatch_tip;
177 } const aux_toolboxes[] = {
178     { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep,            "SelectToolbar",
179       SP_VERB_INVALID, 0, 0},
180     { "SPNodeContext",   "node_toolbox",   0, sp_node_toolbox_prep,              "NodeToolbar",
181       SP_VERB_INVALID, 0, 0},
182     { "SPTweakContext",   "tweak_toolbox",   0, sp_tweak_toolbox_prep,              "TweakToolbar",
183       SP_VERB_CONTEXT_TWEAK_PREFS, "/tools/tweak", N_("Color/opacity used for color tweaking")},
184     { "SPZoomContext",   "zoom_toolbox",   0, sp_zoom_toolbox_prep,              "ZoomToolbar",
185       SP_VERB_INVALID, 0, 0},
186     { "SPStarContext",   "star_toolbox",   0, sp_star_toolbox_prep,              "StarToolbar",
187       SP_VERB_CONTEXT_STAR_PREFS,   "/tools/shapes/star",     N_("Style of new stars")},
188     { "SPRectContext",   "rect_toolbox",   0, sp_rect_toolbox_prep,              "RectToolbar",
189       SP_VERB_CONTEXT_RECT_PREFS,   "/tools/shapes/rect",     N_("Style of new rectangles")},
190     { "Box3DContext",  "3dbox_toolbox",  0, box3d_toolbox_prep,             "3DBoxToolbar",
191       SP_VERB_CONTEXT_3DBOX_PREFS,  "/tools/shapes/3dbox",    N_("Style of new 3D boxes")},
192     { "SPArcContext",    "arc_toolbox",    0, sp_arc_toolbox_prep,               "ArcToolbar",
193       SP_VERB_CONTEXT_ARC_PREFS,    "/tools/shapes/arc",      N_("Style of new ellipses")},
194     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
195       SP_VERB_CONTEXT_SPIRAL_PREFS, "/tools/shapes/spiral",   N_("Style of new spirals")},
196     { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep,            "PencilToolbar",
197       SP_VERB_CONTEXT_PENCIL_PREFS, "/tools/freehand/pencil", N_("Style of new paths created by Pencil")},
198     { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep,                     "PenToolbar",
199       SP_VERB_CONTEXT_PEN_PREFS,    "/tools/freehand/pen",    N_("Style of new paths created by Pen")},
200     { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
201       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "/tools/calligraphic", N_("Style of new calligraphic strokes")},
202     { "SPEraserContext", "eraser_toolbox", 0, sp_eraser_toolbox_prep,"EraserToolbar",
203       SP_VERB_CONTEXT_ERASER_PREFS, "/tools/eraser", _("TBD")},
204     { "SPLPEToolContext", "lpetool_toolbox", 0, sp_lpetool_toolbox_prep, "LPEToolToolbar",
205       SP_VERB_CONTEXT_LPETOOL_PREFS, "/tools/lpetool", _("TBD")},
206     { "SPTextContext",   "text_toolbox",   sp_text_toolbox_new, 0,               0,
207       SP_VERB_INVALID, 0, 0},
208     { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep,         "DropperToolbar",
209       SP_VERB_INVALID, 0, 0},
210     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0,       0,
211       SP_VERB_INVALID, 0, 0},
212     { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep,   "ConnectorToolbar",
213       SP_VERB_INVALID, 0, 0},
214     { "SPFloodContext",  "paintbucket_toolbox",  0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
215       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "/tools/paintbucket", N_("Style of Paint Bucket fill objects")},
216     { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
217 };
219 #define TOOLBAR_SLIDER_HINT "full"
221 static gchar const * ui_descr =
222         "<ui>"
223         "  <toolbar name='SelectToolbar'>"
224         "    <toolitem action='EditSelectAll' />"
225         "    <toolitem action='EditSelectAllInAllLayers' />"
226         "    <toolitem action='EditDeselect' />"
227         "    <separator />"
228         "    <toolitem action='ObjectRotate90CCW' />"
229         "    <toolitem action='ObjectRotate90' />"
230         "    <toolitem action='ObjectFlipHorizontally' />"
231         "    <toolitem action='ObjectFlipVertically' />"
232         "    <separator />"
233         "    <toolitem action='SelectionToBack' />"
234         "    <toolitem action='SelectionLower' />"
235         "    <toolitem action='SelectionRaise' />"
236         "    <toolitem action='SelectionToFront' />"
237         "    <separator />"
238         "    <toolitem action='XAction' />"
239         "    <toolitem action='YAction' />"
240         "    <toolitem action='WidthAction' />"
241         "    <toolitem action='LockAction' />"
242         "    <toolitem action='HeightAction' />"
243         "    <toolitem action='UnitsAction' />"
244         "    <separator />"
245         "    <toolitem action='transform_affect_label' />"
246         "    <toolitem action='transform_stroke' />"
247         "    <toolitem action='transform_corners' />"
248         "    <toolitem action='transform_gradient' />"
249         "    <toolitem action='transform_pattern' />"
250         "  </toolbar>"
252         "  <toolbar name='NodeToolbar'>"
253         "    <toolitem action='NodeInsertAction' />"
254         "    <toolitem action='NodeDeleteAction' />"
255         "    <separator />"
256         "    <toolitem action='NodeJoinAction' />"
257         "    <toolitem action='NodeBreakAction' />"
258         "    <separator />"
259         "    <toolitem action='NodeJoinSegmentAction' />"
260         "    <toolitem action='NodeDeleteSegmentAction' />"
261         "    <separator />"
262         "    <toolitem action='NodeCuspAction' />"
263         "    <toolitem action='NodeSmoothAction' />"
264         "    <toolitem action='NodeSymmetricAction' />"
265         "    <toolitem action='NodeAutoAction' />"
266         "    <separator />"
267         "    <toolitem action='NodeLineAction' />"
268         "    <toolitem action='NodeCurveAction' />"
269         "    <separator />"
270         "    <toolitem action='ObjectToPath' />"
271         "    <toolitem action='StrokeToPath' />"
272         "    <separator />"
273         "    <toolitem action='NodeXAction' />"
274         "    <toolitem action='NodeYAction' />"
275         "    <toolitem action='NodeUnitsAction' />"
276         "    <separator />"
277         "    <toolitem action='ObjectEditClipPathAction' />"
278         "    <toolitem action='ObjectEditMaskPathAction' />"
279         "    <toolitem action='EditNextLPEParameterAction' />"
280         "    <separator />"
281         "    <toolitem action='NodesShowHandlesAction' />"
282         "    <toolitem action='NodesShowHelperpath' />"
283         "  </toolbar>"
285         "  <toolbar name='TweakToolbar'>"
286         "    <toolitem action='TweakWidthAction' />"
287         "    <separator />"
288         "    <toolitem action='TweakForceAction' />"
289         "    <toolitem action='TweakPressureAction' />"
290         "    <separator />"
291         "    <toolitem action='TweakModeAction' />"
292         "    <separator />"
293         "    <toolitem action='TweakFidelityAction' />"
294         "    <separator />"
295         "    <toolitem action='TweakChannelsLabel' />"
296         "    <toolitem action='TweakDoH' />"
297         "    <toolitem action='TweakDoS' />"
298         "    <toolitem action='TweakDoL' />"
299         "    <toolitem action='TweakDoO' />"
300         "  </toolbar>"
302         "  <toolbar name='ZoomToolbar'>"
303         "    <toolitem action='ZoomIn' />"
304         "    <toolitem action='ZoomOut' />"
305         "    <separator />"
306         "    <toolitem action='Zoom1:0' />"
307         "    <toolitem action='Zoom1:2' />"
308         "    <toolitem action='Zoom2:1' />"
309         "    <separator />"
310         "    <toolitem action='ZoomSelection' />"
311         "    <toolitem action='ZoomDrawing' />"
312         "    <toolitem action='ZoomPage' />"
313         "    <toolitem action='ZoomPageWidth' />"
314         "    <separator />"
315         "    <toolitem action='ZoomPrev' />"
316         "    <toolitem action='ZoomNext' />"
317         "  </toolbar>"
319         "  <toolbar name='StarToolbar'>"
320         "    <separator />"
321         "    <toolitem action='StarStateAction' />"
322         "    <separator />"
323         "    <toolitem action='FlatAction' />"
324         "    <separator />"
325         "    <toolitem action='MagnitudeAction' />"
326         "    <toolitem action='SpokeAction' />"
327         "    <toolitem action='RoundednessAction' />"
328         "    <toolitem action='RandomizationAction' />"
329         "    <separator />"
330         "    <toolitem action='StarResetAction' />"
331         "  </toolbar>"
333         "  <toolbar name='RectToolbar'>"
334         "    <toolitem action='RectStateAction' />"
335         "    <toolitem action='RectWidthAction' />"
336         "    <toolitem action='RectHeightAction' />"
337         "    <toolitem action='RadiusXAction' />"
338         "    <toolitem action='RadiusYAction' />"
339         "    <toolitem action='RectUnitsAction' />"
340         "    <separator />"
341         "    <toolitem action='RectResetAction' />"
342         "  </toolbar>"
344         "  <toolbar name='3DBoxToolbar'>"
345         "    <toolitem action='3DBoxAngleXAction' />"
346         "    <toolitem action='3DBoxVPXStateAction' />"
347         "    <separator />"
348         "    <toolitem action='3DBoxAngleYAction' />"
349         "    <toolitem action='3DBoxVPYStateAction' />"
350         "    <separator />"
351         "    <toolitem action='3DBoxAngleZAction' />"
352         "    <toolitem action='3DBoxVPZStateAction' />"
353         "  </toolbar>"
355         "  <toolbar name='SpiralToolbar'>"
356         "    <toolitem action='SpiralStateAction' />"
357         "    <toolitem action='SpiralRevolutionAction' />"
358         "    <toolitem action='SpiralExpansionAction' />"
359         "    <toolitem action='SpiralT0Action' />"
360         "    <separator />"
361         "    <toolitem action='SpiralResetAction' />"
362         "  </toolbar>"
364         "  <toolbar name='PenToolbar'>"
365         "    <toolitem action='FreehandModeActionPen' />"
366         "    <separator />"
367         "    <toolitem action='SetPenShapeAction'/>"
368         "  </toolbar>"
370         "  <toolbar name='PencilToolbar'>"
371         "    <toolitem action='FreehandModeActionPencil' />"
372         "    <separator />"
373         "    <toolitem action='PencilToleranceAction' />"
374         "    <separator />"
375         "    <toolitem action='PencilResetAction' />"
376         "    <separator />"
377         "    <toolitem action='SetPencilShapeAction'/>"
378         "  </toolbar>"
380         "  <toolbar name='CalligraphyToolbar'>"
381         "    <separator />"
382         "    <toolitem action='SetProfileAction'/>"
383         "    <separator />"
384         "    <toolitem action='CalligraphyWidthAction' />"
385         "    <toolitem action='PressureAction' />"
386         "    <toolitem action='TraceAction' />"
387         "    <toolitem action='ThinningAction' />"
388         "    <separator />"
389         "    <toolitem action='AngleAction' />"
390         "    <toolitem action='TiltAction' />"
391         "    <toolitem action='FixationAction' />"
392         "    <separator />"
393         "    <toolitem action='CapRoundingAction' />"
394         "    <separator />"
395         "    <toolitem action='TremorAction' />"
396         "    <toolitem action='WiggleAction' />"
397         "    <toolitem action='MassAction' />"
398         "    <separator />"
399         "  </toolbar>"
401         "  <toolbar name='ArcToolbar'>"
402         "    <toolitem action='ArcStateAction' />"
403         "    <separator />"
404         "    <toolitem action='ArcStartAction' />"
405         "    <toolitem action='ArcEndAction' />"
406         "    <separator />"
407         "    <toolitem action='ArcOpenAction' />"
408         "    <separator />"
409         "    <toolitem action='ArcResetAction' />"
410         "    <separator />"
411         "  </toolbar>"
413         "  <toolbar name='PaintbucketToolbar'>"
414         "    <toolitem action='ChannelsAction' />"
415         "    <separator />"
416         "    <toolitem action='ThresholdAction' />"
417         "    <separator />"
418         "    <toolitem action='OffsetAction' />"
419         "    <toolitem action='PaintbucketUnitsAction' />"
420         "    <separator />"
421         "    <toolitem action='AutoGapAction' />"
422         "    <separator />"
423         "    <toolitem action='PaintbucketResetAction' />"
424         "  </toolbar>"
426         "  <toolbar name='EraserToolbar'>"
427         "    <toolitem action='EraserWidthAction' />"
428         "    <separator />"
429         "    <toolitem action='EraserModeAction' />"
430         "  </toolbar>"
432         "  <toolbar name='LPEToolToolbar'>"
433         "    <toolitem action='LPEToolModeAction' />"
434         "    <separator />"
435         "    <toolitem action='LPEShowBBoxAction' />"
436         "    <toolitem action='LPEBBoxFromSelectionAction' />"
437         "    <separator />"
438         "    <toolitem action='LPELineSegmentAction' />"
439         "    <separator />"
440         "    <toolitem action='LPEMeasuringAction' />"
441         "    <toolitem action='LPEToolUnitsAction' />"
442         "    <separator />"
443         "    <toolitem action='LPEOpenLPEDialogAction' />"
444         "  </toolbar>"
446         "  <toolbar name='DropperToolbar'>"
447         "    <toolitem action='DropperOpacityAction' />"
448         "    <toolitem action='DropperPickAlphaAction' />"
449         "    <toolitem action='DropperSetAlphaAction' />"
450         "  </toolbar>"
452         "  <toolbar name='ConnectorToolbar'>"
453         "    <toolitem action='ConnectorAvoidAction' />"
454         "    <toolitem action='ConnectorIgnoreAction' />"
455         "    <toolitem action='ConnectorSpacingAction' />"
456         "    <toolitem action='ConnectorGraphAction' />"
457         "    <toolitem action='ConnectorLengthAction' />"
458         "    <toolitem action='ConnectorDirectedAction' />"
459         "    <toolitem action='ConnectorOverlapAction' />"
460         "  </toolbar>"
462         "</ui>"
465 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
467 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
469 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
470 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
472 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
473 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
475 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
476 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
478 GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick( GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
479                                                               Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
480                                                               Inkscape::UI::View::View *view, GtkTooltips *tt);
482 class VerbAction : public Gtk::Action {
483 public:
484     static Glib::RefPtr<VerbAction> create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
486     virtual ~VerbAction();
487     virtual void set_active(bool active = true);
489 protected:
490     virtual Gtk::Widget* create_menu_item_vfunc();
491     virtual Gtk::Widget* create_tool_item_vfunc();
493     virtual void connect_proxy_vfunc(Gtk::Widget* proxy);
494     virtual void disconnect_proxy_vfunc(Gtk::Widget* proxy);
496     virtual void on_activate();
498 private:
499     Inkscape::Verb* verb;
500     Inkscape::Verb* verb2;
501     Inkscape::UI::View::View *view;
502     GtkTooltips *tooltips;
503     bool active;
505     VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
506 };
509 Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips)
511     Glib::RefPtr<VerbAction> result;
512     SPAction *action = verb->get_action(view);
513     if ( action ) {
514         //SPAction* action2 = verb2 ? verb2->get_action(view) : 0;
515         result = Glib::RefPtr<VerbAction>(new VerbAction(verb, verb2, view, tooltips));
516     }
518     return result;
521 VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips) :
522     Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(verb->get_image()), Glib::ustring(_(verb->get_name())), Glib::ustring(_(verb->get_tip()))),
523     verb(verb),
524     verb2(verb2),
525     view(view),
526     tooltips(tooltips),
527     active(false)
531 VerbAction::~VerbAction()
535 Gtk::Widget* VerbAction::create_menu_item_vfunc()
537 // First call in to get the icon rendered if present in SVG
538     Gtk::Widget *widget = sp_icon_get_icon( property_stock_id().get_value().get_string(), Inkscape::ICON_SIZE_MENU );
539     delete widget;
540     widget = 0;
542     Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
543 //     g_message("create_menu_item_vfunc() = %p  for '%s'", widg, verb->get_id());
544     return widg;
547 Gtk::Widget* VerbAction::create_tool_item_vfunc()
549 //     Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
550     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
551     GtkWidget* toolbox = 0;
552     GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
553                                                                           SP_BUTTON_TYPE_TOGGLE,
554                                                                           verb,
555                                                                           verb2,
556                                                                           view,
557                                                                           tooltips );
558     if ( active ) {
559         sp_button_toggle_set_down( SP_BUTTON(button), active);
560     }
561     gtk_widget_show_all( button );
562     Gtk::Widget* wrapped = Glib::wrap(button);
563     Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
564     holder->add(*wrapped);
566 //     g_message("create_tool_item_vfunc() = %p  for '%s'", holder, verb->get_id());
567     return holder;
570 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
572 //     g_message("connect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
573     Gtk::Action::connect_proxy_vfunc(proxy);
576 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
578 //     g_message("disconnect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
579     Gtk::Action::disconnect_proxy_vfunc(proxy);
582 void VerbAction::set_active(bool active)
584     this->active = active;
585     Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
586     for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
587         Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
588         if (ti) {
589             // *should* have one child that is the SPButton
590             Gtk::Widget* child = ti->get_child();
591             if ( child && SP_IS_BUTTON(child->gobj()) ) {
592                 SPButton* button = SP_BUTTON(child->gobj());
593                 sp_button_toggle_set_down( button, active );
594             }
595         }
596     }
599 void VerbAction::on_activate()
601     if ( verb ) {
602         SPAction *action = verb->get_action(view);
603         if ( action ) {
604             sp_action_perform(action, 0);
605         }
606     }
609 /* Global text entry widgets necessary for update */
610 /* GtkWidget *dropper_rgb_entry,
611           *dropper_opacity_entry ; */
612 // should be made a private member once this is converted to class
614 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
615     connection->disconnect();
616     delete connection;
619 static void purge_repr_listener( GObject* obj, GObject* tbl )
621     (void)obj;
622     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
623     if (oldrepr) { // remove old listener
624         sp_repr_remove_listener_by_data(oldrepr, tbl);
625         Inkscape::GC::release(oldrepr);
626         oldrepr = 0;
627         g_object_set_data( tbl, "repr", NULL );
628     }
631 GtkWidget *
632 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
633                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
634                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
636     SPAction *action = verb->get_action(view);
637     if (!action) return NULL;
639     SPAction *doubleclick_action;
640     if (doubleclick_verb)
641         doubleclick_action = doubleclick_verb->get_action(view);
642     else
643         doubleclick_action = NULL;
645     /* fixme: Handle sensitive/unsensitive */
646     /* fixme: Implement sp_button_new_from_action */
647     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
648     gtk_widget_show(b);
651     unsigned int shortcut = sp_shortcut_get_primary(verb);
652     if (shortcut) {
653         gchar key[256];
654         sp_ui_shortcut_string(shortcut, key);
655         gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
656         if ( t ) {
657             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
658         }
659         g_free(tip);
660     } else {
661         if ( t ) {
662             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
663         }
664     }
666     return b;
670 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
672     SPAction* targetAction = SP_ACTION(user_data);
673     if ( targetAction ) {
674         sp_action_perform( targetAction, NULL );
675     }
678 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
680     if ( data ) {
681         GtkAction* act = GTK_ACTION(data);
682         gtk_action_set_sensitive( act, sensitive );
683     }
686 static SPActionEventVector action_event_vector = {
687     {NULL},
688     NULL,
689     NULL,
690     sp_action_action_set_sensitive,
691     NULL,
692     NULL
693 };
695 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
697     GtkAction* act = 0;
699     SPAction* targetAction = verb->get_action(view);
700     InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size  );
701     act = GTK_ACTION(inky);
702     gtk_action_set_sensitive( act, targetAction->sensitive );
704     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
706     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
707     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
709     return act;
712 Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
714     Inkscape::UI::View::View *view = desktop;
715     gint verbsToUse[] = {
716         // disabled until we have icons for them:
717         //find
718         //SP_VERB_EDIT_TILE,
719         //SP_VERB_EDIT_UNTILE,
720         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
721         SP_VERB_DIALOG_DISPLAY,
722         SP_VERB_DIALOG_FILL_STROKE,
723         SP_VERB_DIALOG_NAMEDVIEW,
724         SP_VERB_DIALOG_TEXT,
725         SP_VERB_DIALOG_XML_EDITOR,
726         SP_VERB_EDIT_CLONE,
727         SP_VERB_EDIT_COPY,
728         SP_VERB_EDIT_CUT,
729         SP_VERB_EDIT_DUPLICATE,
730         SP_VERB_EDIT_PASTE,
731         SP_VERB_EDIT_REDO,
732         SP_VERB_EDIT_UNDO,
733         SP_VERB_EDIT_UNLINK_CLONE,
734         SP_VERB_FILE_EXPORT,
735         SP_VERB_FILE_IMPORT,
736         SP_VERB_FILE_NEW,
737         SP_VERB_FILE_OPEN,
738         SP_VERB_FILE_PRINT,
739         SP_VERB_FILE_SAVE,
740         SP_VERB_OBJECT_TO_CURVE,
741         SP_VERB_SELECTION_GROUP,
742         SP_VERB_SELECTION_OUTLINE,
743         SP_VERB_SELECTION_UNGROUP,
744         SP_VERB_ZOOM_1_1,
745         SP_VERB_ZOOM_1_2,
746         SP_VERB_ZOOM_2_1,
747         SP_VERB_ZOOM_DRAWING,
748         SP_VERB_ZOOM_IN,
749         SP_VERB_ZOOM_NEXT,
750         SP_VERB_ZOOM_OUT,
751         SP_VERB_ZOOM_PAGE,
752         SP_VERB_ZOOM_PAGE_WIDTH,
753         SP_VERB_ZOOM_PREV,
754         SP_VERB_ZOOM_SELECTION,
755     };
757     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
759     static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
760     Glib::RefPtr<Gtk::ActionGroup> mainActions;
761     if ( groups.find(desktop) != groups.end() ) {
762         mainActions = groups[desktop];
763     }
765     if ( !mainActions ) {
766         mainActions = Gtk::ActionGroup::create("main");
767         groups[desktop] = mainActions;
768     }
770     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
771         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
772         if ( verb ) {
773             if (!mainActions->get_action(verb->get_id())) {
774                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
775                 mainActions->add(Glib::wrap(act));
776             }
777         }
778     }
780     if ( !mainActions->get_action("ToolZoom") ) {
781         GtkTooltips *tt = gtk_tooltips_new();
782         for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
783             Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
784             if ( va ) {
785                 mainActions->add(va);
786                 if ( i == 0 ) {
787                     va->set_active(true);
788                 }
789             }
790         }
791     }
794     return mainActions;
798 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
800     gtk_widget_set_size_request( widget,
801                                  widget->allocation.width,
802                                  widget->allocation.height );
805 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
807     gtk_widget_set_size_request( widget, -1, -1 );
812 GtkWidget *
813 sp_tool_toolbox_new()
815     GtkTooltips *tt = gtk_tooltips_new();
816     GtkWidget* tb = gtk_toolbar_new();
817     gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
818     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
820     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
821     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
823     gtk_widget_set_sensitive(tb, FALSE);
825     GtkWidget *hb = gtk_handle_box_new();
826     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
827     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
828     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
830     gtk_container_add(GTK_CONTAINER(hb), tb);
831     gtk_widget_show(GTK_WIDGET(tb));
833     sigc::connection* conn = new sigc::connection;
834     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
836     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
837     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
839     return hb;
842 GtkWidget *
843 sp_aux_toolbox_new()
845     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
847     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
849     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
851     gtk_widget_set_sensitive(tb, FALSE);
853     GtkWidget *hb = gtk_handle_box_new();
854     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
855     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
856     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
858     gtk_container_add(GTK_CONTAINER(hb), tb);
859     gtk_widget_show(GTK_WIDGET(tb));
861     sigc::connection* conn = new sigc::connection;
862     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
864     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
865     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
867     return hb;
870 //####################################
871 //# Commands Bar
872 //####################################
874 GtkWidget *
875 sp_commands_toolbox_new()
877     GtkWidget *tb = gtk_toolbar_new();
879     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
880     gtk_widget_set_sensitive(tb, FALSE);
882     GtkWidget *hb = gtk_handle_box_new();
883     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
884     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
885     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
887     gtk_container_add(GTK_CONTAINER(hb), tb);
888     gtk_widget_show(GTK_WIDGET(tb));
890     sigc::connection* conn = new sigc::connection;
891     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
893     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
894     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
896     return hb;
899 GtkWidget *
900 sp_snap_toolbox_new()
902         GtkWidget *tb = gtk_vbox_new(FALSE, 0);
903         gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
904         g_object_set_data(G_OBJECT(tb), "desktop", NULL);
906         //GtkWidget *tb = gtk_toolbar_new();
907     //g_object_set_data(G_OBJECT(tb), "desktop", NULL);
909     gtk_widget_set_sensitive(tb, FALSE);
911     GtkWidget *hb = gtk_handle_box_new();
912     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
913     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
914     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
916     gtk_container_add(GTK_CONTAINER(hb), tb);
917     gtk_widget_show(GTK_WIDGET(tb));
919     sigc::connection* conn = new sigc::connection;
920     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
922     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
923     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
925     return hb;
928 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
929                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
930                                                        Glib::ustring const &path, gdouble def,
931                                                        GtkWidget *focusTarget,
932                                                        GtkWidget *us,
933                                                        GObject *dataKludge,
934                                                        gboolean altx, gchar const *altx_mark,
935                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
936                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
937                                                        void (*callback)(GtkAdjustment *, GObject *),
938                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
940     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
941     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs->getDouble(path, def) * factor,
942                                                              lower, upper, step, page, 0 ) );
943     if (us) {
944         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
945     }
947     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
949     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
950     if ( shortLabel ) {
951         g_object_set( act, "short_label", shortLabel, NULL );
952     }
954     if ( (descrCount > 0) && descrLabels && descrValues ) {
955         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
956     }
958     if ( focusTarget ) {
959         ege_adjustment_action_set_focuswidget( act, focusTarget );
960     }
962     if ( altx && altx_mark ) {
963         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
964     }
966     if ( dataKludge ) {
967         // Rather lame, but it's the only place where we need to get the entry name
968         // but we don't have an Entry
969         g_object_set_data( dataKludge, prefs->getEntry(path).getEntryName().data(), adj );
970     }
972     // Using a cast just to make sure we pass in the right kind of function pointer
973     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
975     return act;
979 //####################################
980 //# node editing callbacks
981 //####################################
983 /**
984  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
985  */
986 static ShapeEditor *get_current_shape_editor()
988     if (!SP_ACTIVE_DESKTOP) {
989         return NULL;
990     }
992     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
994     if (!SP_IS_NODE_CONTEXT(event_context)) {
995         return NULL;
996     }
998     return event_context->shape_editor;
1002 void
1003 sp_node_path_edit_add(void)
1005     ShapeEditor *shape_editor = get_current_shape_editor();
1006     if (shape_editor) shape_editor->add_node();
1009 void
1010 sp_node_path_edit_delete(void)
1012     ShapeEditor *shape_editor = get_current_shape_editor();
1013     if (shape_editor) shape_editor->delete_nodes_preserving_shape();
1016 void
1017 sp_node_path_edit_delete_segment(void)
1019     ShapeEditor *shape_editor = get_current_shape_editor();
1020     if (shape_editor) shape_editor->delete_segment();
1023 void
1024 sp_node_path_edit_break(void)
1026     ShapeEditor *shape_editor = get_current_shape_editor();
1027     if (shape_editor) shape_editor->break_at_nodes();
1030 void
1031 sp_node_path_edit_join(void)
1033     ShapeEditor *shape_editor = get_current_shape_editor();
1034     if (shape_editor) shape_editor->join_nodes();
1037 void
1038 sp_node_path_edit_join_segment(void)
1040     ShapeEditor *shape_editor = get_current_shape_editor();
1041     if (shape_editor) shape_editor->join_segments();
1044 void
1045 sp_node_path_edit_toline(void)
1047     ShapeEditor *shape_editor = get_current_shape_editor();
1048     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1051 void
1052 sp_node_path_edit_tocurve(void)
1054     ShapeEditor *shape_editor = get_current_shape_editor();
1055     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1058 void
1059 sp_node_path_edit_cusp(void)
1061     ShapeEditor *shape_editor = get_current_shape_editor();
1062     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1065 void
1066 sp_node_path_edit_smooth(void)
1068     ShapeEditor *shape_editor = get_current_shape_editor();
1069     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1072 void
1073 sp_node_path_edit_symmetrical(void)
1075     ShapeEditor *shape_editor = get_current_shape_editor();
1076     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1079 void
1080 sp_node_path_edit_auto(void)
1082     ShapeEditor *shape_editor = get_current_shape_editor();
1083     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_AUTO);
1086 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1087     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1088     bool show = gtk_toggle_action_get_active( act );
1089     prefs->setBool("/tools/nodes/show_handles",  show);
1090     ShapeEditor *shape_editor = get_current_shape_editor();
1091     if (shape_editor) shape_editor->show_handles(show);
1094 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1095     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1096     bool show = gtk_toggle_action_get_active( act );
1097     prefs->setBool("/tools/nodes/show_helperpath",  show);
1098     ShapeEditor *shape_editor = get_current_shape_editor();
1099     if (shape_editor) shape_editor->show_helperpath(show);
1102 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1103     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1106 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1107     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1110 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1111     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1114 /* is called when the node selection is modified */
1115 static void
1116 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1118     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1119     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1120     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1121     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1123     // quit if run by the attr_changed listener
1124     if (g_object_get_data( tbl, "freeze" )) {
1125         return;
1126     }
1128     // in turn, prevent listener from responding
1129     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1131     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1132     SPUnit const *unit = tracker->getActiveUnit();
1134     ShapeEditor *shape_editor = get_current_shape_editor();
1135     if (shape_editor && shape_editor->has_nodepath()) {
1136         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1137         int n_selected = 0;
1138         if (nodepath) {
1139             n_selected = nodepath->numSelected();
1140         }
1142         if (n_selected == 0) {
1143             gtk_action_set_sensitive(xact, FALSE);
1144             gtk_action_set_sensitive(yact, FALSE);
1145         } else {
1146             gtk_action_set_sensitive(xact, TRUE);
1147             gtk_action_set_sensitive(yact, TRUE);
1148             Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1149             Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1151             if (n_selected == 1) {
1152                 Geom::Point sel_node = nodepath->singleSelectedCoords();
1153                 if (oldx != sel_node[Geom::X] || oldy != sel_node[Geom::Y]) {
1154                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[Geom::X], *unit));
1155                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[Geom::Y], *unit));
1156                 }
1157             } else {
1158                 boost::optional<Geom::Coord> x = sp_node_selected_common_coord(nodepath, Geom::X);
1159                 boost::optional<Geom::Coord> y = sp_node_selected_common_coord(nodepath, Geom::Y);
1160                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1161                     /* Note: Currently x and y will always have a value, even if the coordinates of the
1162                        selected nodes don't coincide (in this case we use the coordinates of the center
1163                        of the bounding box). So the entries are never set to zero. */
1164                     // FIXME: Maybe we should clear the entry if several nodes are selected
1165                     //        instead of providing a kind of average value
1166                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1167                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1168                 }
1169             }
1170         }
1171     } else {
1172         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1173         gtk_action_set_sensitive(xact, FALSE);
1174         gtk_action_set_sensitive(yact, FALSE);
1175     }
1177     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1180 static void
1181 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1183     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1184     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1186     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1187     SPUnit const *unit = tracker->getActiveUnit();
1189     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1190         prefs->setDouble(Glib::ustring("/tools/nodes/") + value_name, sp_units_get_pixels(adj->value, *unit));
1191     }
1193     // quit if run by the attr_changed listener
1194     if (g_object_get_data( tbl, "freeze" )) {
1195         return;
1196     }
1198     // in turn, prevent listener from responding
1199     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1201     ShapeEditor *shape_editor = get_current_shape_editor();
1202     if (shape_editor && shape_editor->has_nodepath()) {
1203         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1204         if (!strcmp(value_name, "x")) {
1205             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::X);
1206         }
1207         if (!strcmp(value_name, "y")) {
1208             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::Y);
1209         }
1210     }
1212     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1215 static void
1216 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1218     sp_node_path_value_changed(adj, tbl, "x");
1221 static void
1222 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1224     sp_node_path_value_changed(adj, tbl, "y");
1227 void
1228 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1230     {
1231     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1232     SPItem *item = selection->singleItem();
1233     if (item && SP_IS_LPE_ITEM(item)) {
1234        if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1235            gtk_action_set_sensitive(w, TRUE);
1236        } else {
1237            gtk_action_set_sensitive(w, FALSE);
1238        }
1239     } else {
1240        gtk_action_set_sensitive(w, FALSE);
1241     }
1242     }
1244     {
1245     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1246     SPItem *item = selection->singleItem();
1247     if (item && item->clip_ref && item->clip_ref->getObject()) {
1248        gtk_action_set_sensitive(w, TRUE);
1249     } else {
1250        gtk_action_set_sensitive(w, FALSE);
1251     }
1252     }
1254     {
1255     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1256     SPItem *item = selection->singleItem();
1257     if (item && item->mask_ref && item->mask_ref->getObject()) {
1258        gtk_action_set_sensitive(w, TRUE);
1259     } else {
1260        gtk_action_set_sensitive(w, FALSE);
1261     }
1262     }
1265 void
1266 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1268     sp_node_toolbox_sel_changed (selection, tbl);
1273 //################################
1274 //##    Node Editing Toolbox    ##
1275 //################################
1277 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1279     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1280     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1281     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1282     g_object_set_data( holder, "tracker", tracker );
1284     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
1286     {
1287         InkAction* inky = ink_action_new( "NodeInsertAction",
1288                                           _("Insert node"),
1289                                           _("Insert new nodes into selected segments"),
1290                                           "node_insert",
1291                                           secondarySize );
1292         g_object_set( inky, "short_label", _("Insert"), NULL );
1293         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1294         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1295     }
1297     {
1298         InkAction* inky = ink_action_new( "NodeDeleteAction",
1299                                           _("Delete node"),
1300                                           _("Delete selected nodes"),
1301                                           "node_delete",
1302                                           secondarySize );
1303         g_object_set( inky, "short_label", _("Delete"), NULL );
1304         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1305         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1306     }
1308     {
1309         InkAction* inky = ink_action_new( "NodeJoinAction",
1310                                           _("Join endnodes"),
1311                                           _("Join selected endnodes"),
1312                                           "node_join",
1313                                           secondarySize );
1314         g_object_set( inky, "short_label", _("Join"), NULL );
1315         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1316         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1317     }
1319     {
1320         InkAction* inky = ink_action_new( "NodeBreakAction",
1321                                           _("Break nodes"),
1322                                           _("Break path at selected nodes"),
1323                                           "node_break",
1324                                           secondarySize );
1325         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1326         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1327     }
1330     {
1331         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1332                                           _("Join with segment"),
1333                                           _("Join selected endnodes with a new segment"),
1334                                           "node_join_segment",
1335                                           secondarySize );
1336         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1337         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1338     }
1340     {
1341         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1342                                           _("Delete segment"),
1343                                           _("Delete segment between two non-endpoint nodes"),
1344                                           "node_delete_segment",
1345                                           secondarySize );
1346         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1347         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1348     }
1350     {
1351         InkAction* inky = ink_action_new( "NodeCuspAction",
1352                                           _("Node Cusp"),
1353                                           _("Make selected nodes corner"),
1354                                           "node_cusp",
1355                                           secondarySize );
1356         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1357         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1358     }
1360     {
1361         InkAction* inky = ink_action_new( "NodeSmoothAction",
1362                                           _("Node Smooth"),
1363                                           _("Make selected nodes smooth"),
1364                                           "node_smooth",
1365                                           secondarySize );
1366         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1367         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1368     }
1370     {
1371         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1372                                           _("Node Symmetric"),
1373                                           _("Make selected nodes symmetric"),
1374                                           "node_symmetric",
1375                                           secondarySize );
1376         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1377         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1378     }
1380     {
1381         InkAction* inky = ink_action_new( "NodeAutoAction",
1382                                           _("Node Auto"),
1383                                           _("Make selected nodes auto-smooth"),
1384                                           "node_auto",
1385                                           secondarySize );
1386         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_auto), 0 );
1387         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1388     }
1390     {
1391         InkAction* inky = ink_action_new( "NodeLineAction",
1392                                           _("Node Line"),
1393                                           _("Make selected segments lines"),
1394                                           "node_line",
1395                                           secondarySize );
1396         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1397         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1398     }
1400     {
1401         InkAction* inky = ink_action_new( "NodeCurveAction",
1402                                           _("Node Curve"),
1403                                           _("Make selected segments curves"),
1404                                           "node_curve",
1405                                           secondarySize );
1406         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1407         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1408     }
1410     {
1411         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1412                                                       _("Show Handles"),
1413                                                       _("Show the Bezier handles of selected nodes"),
1414                                                       "nodes_show_handles",
1415                                                       Inkscape::ICON_SIZE_DECORATION );
1416         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1417         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1418         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_handles", true) );
1419     }
1421     {
1422         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1423                                                       _("Show Outline"),
1424                                                       _("Show the outline of the path"),
1425                                                       "nodes_show_helperpath",
1426                                                       Inkscape::ICON_SIZE_DECORATION );
1427         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1428         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1429         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_helperpath", false) );
1430     }
1432     {
1433         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1434                                           _("Next path effect parameter"),
1435                                           _("Show next path effect parameter for editing"),
1436                                           "edit_next_parameter",
1437                                           Inkscape::ICON_SIZE_DECORATION );
1438         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1439         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1440         g_object_set_data( holder, "nodes_lpeedit", inky);
1441     }
1443     {
1444         InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1445                                           _("Edit clipping path"),
1446                                           _("Edit the clipping path of the object"),
1447                                           "nodeedit-clippath",
1448                                           Inkscape::ICON_SIZE_DECORATION );
1449         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1450         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1451         g_object_set_data( holder, "nodes_clippathedit", inky);
1452     }
1454     {
1455         InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1456                                           _("Edit mask path"),
1457                                           _("Edit the mask of the object"),
1458                                           "nodeedit-mask",
1459                                           Inkscape::ICON_SIZE_DECORATION );
1460         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1461         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1462         g_object_set_data( holder, "nodes_maskedit", inky);
1463     }
1465     /* X coord of selected node(s) */
1466     {
1467         EgeAdjustmentAction* eact = 0;
1468         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1469         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1470         eact = create_adjustment_action( "NodeXAction",
1471                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1472                                          "/tools/nodes/Xcoord", 0,
1473                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1474                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1475                                          labels, values, G_N_ELEMENTS(labels),
1476                                          sp_node_path_x_value_changed );
1477         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1478         g_object_set_data( holder, "nodes_x_action", eact );
1479         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1480         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1481     }
1483     /* Y coord of selected node(s) */
1484     {
1485         EgeAdjustmentAction* eact = 0;
1486         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1487         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1488         eact = create_adjustment_action( "NodeYAction",
1489                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1490                                          "/tools/nodes/Ycoord", 0,
1491                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1492                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1493                                          labels, values, G_N_ELEMENTS(labels),
1494                                          sp_node_path_y_value_changed );
1495         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1496         g_object_set_data( holder, "nodes_y_action", eact );
1497         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1498         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1499     }
1501     // add the units menu
1502     {
1503         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1504         gtk_action_group_add_action( mainActions, act );
1505     }
1508     sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1510     //watch selection
1511     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1513     sigc::connection *c_selection_changed =
1514         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1515                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1516     pool->add_connection ("selection-changed", c_selection_changed);
1518     sigc::connection *c_selection_modified =
1519         new sigc::connection (sp_desktop_selection (desktop)->connectModified
1520                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1521     pool->add_connection ("selection-modified", c_selection_modified);
1523     sigc::connection *c_subselection_changed =
1524         new sigc::connection (desktop->connectToolSubselectionChanged
1525                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1526     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1528     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1530     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1531 } // end of sp_node_toolbox_prep()
1534 //########################
1535 //##    Zoom Toolbox    ##
1536 //########################
1538 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1540     // no custom GtkAction setup needed
1541 } // end of sp_zoom_toolbox_prep()
1543 void
1544 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1546     toolbox_set_desktop(toolbox,
1547                         desktop,
1548                         setup_tool_toolbox,
1549                         update_tool_toolbox,
1550                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1551                                                                          "event_context_connection")));
1555 void
1556 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1558     toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1559                         desktop,
1560                         setup_aux_toolbox,
1561                         update_aux_toolbox,
1562                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1563                                                                          "event_context_connection")));
1566 void
1567 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1569     toolbox_set_desktop(toolbox,
1570                         desktop,
1571                         setup_commands_toolbox,
1572                         update_commands_toolbox,
1573                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1574                                                                          "event_context_connection")));
1577 void
1578 sp_snap_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1580     toolbox_set_desktop(toolbox,
1581                         desktop,
1582                         setup_snap_toolbox,
1583                         update_snap_toolbox,
1584                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1585                                                                          "event_context_connection")));
1589 static void
1590 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1592     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1593     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1595     if (old_desktop) {
1596         GList *children, *iter;
1598         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1599         for ( iter = children ; iter ; iter = iter->next ) {
1600             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1601         }
1602         g_list_free(children);
1603     }
1605     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1607     if (desktop) {
1608         gtk_widget_set_sensitive(toolbox, TRUE);
1609         setup_func(toolbox, desktop);
1610         update_func(desktop, desktop->event_context, toolbox);
1611         *conn = desktop->connectEventContextChanged
1612             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1613     } else {
1614         gtk_widget_set_sensitive(toolbox, FALSE);
1615     }
1617 } // end of toolbox_set_desktop()
1620 static void
1621 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1623     gchar const * descr =
1624         "<ui>"
1625         "  <toolbar name='ToolToolbar'>"
1626         "    <toolitem action='ToolSelector' />"
1627         "    <toolitem action='ToolNode' />"
1628         "    <toolitem action='ToolTweak' />"
1629         "    <toolitem action='ToolZoom' />"
1630         "    <toolitem action='ToolRect' />"
1631         "    <toolitem action='Tool3DBox' />"
1632         "    <toolitem action='ToolArc' />"
1633         "    <toolitem action='ToolStar' />"
1634         "    <toolitem action='ToolSpiral' />"
1635         "    <toolitem action='ToolPencil' />"
1636         "    <toolitem action='ToolPen' />"
1637         "    <toolitem action='ToolCalligraphic' />"
1638         "    <toolitem action='ToolEraser' />"
1639 //        "    <toolitem action='ToolLPETool' />"
1640         "    <toolitem action='ToolPaintBucket' />"
1641         "    <toolitem action='ToolText' />"
1642         "    <toolitem action='ToolConnector' />"
1643         "    <toolitem action='ToolGradient' />"
1644         "    <toolitem action='ToolDropper' />"
1645         "  </toolbar>"
1646         "</ui>";
1647     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1648     GtkUIManager* mgr = gtk_ui_manager_new();
1649     GError* errVal = 0;
1650     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1652     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1653     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1655     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" );
1656     if ( prefs->getBool("/toolbox/icononly", true) ) {
1657         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1658     }
1659     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
1660     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1662     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
1663     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1665     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1667     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1668     if ( child ) {
1669         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1670     }
1672     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1673 //     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
1677 static void
1678 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1680     gchar const *const tname = ( eventcontext
1681                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1682                                  : NULL );
1683     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1685     for (int i = 0 ; tools[i].type_name ; i++ ) {
1686         Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1687         if ( act ) {
1688             bool setActive = tname && !strcmp(tname, tools[i].type_name);
1689             Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1690             if ( verbAct ) {
1691                 verbAct->set_active(setActive);
1692             }
1693         }
1694     }
1697 static void
1698 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1700     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1701     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1702     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1703     GtkUIManager* mgr = gtk_ui_manager_new();
1704     GError* errVal = 0;
1705     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1706     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1708     std::map<std::string, GtkWidget*> dataHolders;
1710     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1711         if ( aux_toolboxes[i].prep_func ) {
1712             // converted to GtkActions and UIManager
1714             GtkWidget* kludge = gtk_toolbar_new();
1715             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1716             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1717             dataHolders[aux_toolboxes[i].type_name] = kludge;
1718             aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1719         } else {
1721             GtkWidget *sub_toolbox = 0;
1722             if (aux_toolboxes[i].create_func == NULL)
1723                 sub_toolbox = sp_empty_toolbox_new(desktop);
1724             else {
1725                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1726             }
1728             gtk_size_group_add_widget( grouper, sub_toolbox );
1730             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1731             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1733         }
1734     }
1736     // Second pass to create toolbars *after* all GtkActions are created
1737     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1738         if ( aux_toolboxes[i].prep_func ) {
1739             // converted to GtkActions and UIManager
1741             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1743             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1744             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1746             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1747             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1748             g_free( tmp );
1749             tmp = 0;
1751             if ( prefs->getBool( "/toolbox/icononly", true) ) {
1752                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1753             }
1755             Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
1756             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1758             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1760             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1761                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1762                 swatch->setDesktop( desktop );
1763                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1764                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1765                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1766                 gtk_table_attach( GTK_TABLE(holder), swatch_, 1, 2, 0, 1, (GtkAttachOptions)(GTK_SHRINK | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), AUX_BETWEEN_BUTTON_GROUPS, 0 );
1767             }
1769             gtk_widget_show_all( holder );
1770             sp_set_font_size_smaller( holder );
1772             gtk_size_group_add_widget( grouper, holder );
1774             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1775             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1776         }
1777     }
1779     g_object_unref( G_OBJECT(grouper) );
1782 static void
1783 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1785     gchar const *tname = ( eventcontext
1786                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1787                            : NULL );
1788     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1789         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1790         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1791             gtk_widget_show_all(sub_toolbox);
1792             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1793         } else {
1794             gtk_widget_hide(sub_toolbox);
1795         }
1796     }
1799 static void
1800 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1802     gchar const * descr =
1803         "<ui>"
1804         "  <toolbar name='CommandsToolbar'>"
1805         "    <toolitem action='FileNew' />"
1806         "    <toolitem action='FileOpen' />"
1807         "    <toolitem action='FileSave' />"
1808         "    <toolitem action='FilePrint' />"
1809         "    <separator />"
1810         "    <toolitem action='FileImport' />"
1811         "    <toolitem action='FileExport' />"
1812         "    <separator />"
1813         "    <toolitem action='EditUndo' />"
1814         "    <toolitem action='EditRedo' />"
1815         "    <separator />"
1816         "    <toolitem action='EditCopy' />"
1817         "    <toolitem action='EditCut' />"
1818         "    <toolitem action='EditPaste' />"
1819         "    <separator />"
1820         "    <toolitem action='ZoomSelection' />"
1821         "    <toolitem action='ZoomDrawing' />"
1822         "    <toolitem action='ZoomPage' />"
1823         "    <separator />"
1824         "    <toolitem action='EditDuplicate' />"
1825         "    <toolitem action='EditClone' />"
1826         "    <toolitem action='EditUnlinkClone' />"
1827         "    <separator />"
1828         "    <toolitem action='SelectionGroup' />"
1829         "    <toolitem action='SelectionUnGroup' />"
1830         "    <separator />"
1831         "    <toolitem action='DialogFillStroke' />"
1832         "    <toolitem action='DialogText' />"
1833         "    <toolitem action='DialogXMLEditor' />"
1834         "    <toolitem action='DialogAlignDistribute' />"
1835         "    <separator />"
1836         "    <toolitem action='DialogPreferences' />"
1837         "    <toolitem action='DialogDocumentProperties' />"
1838         "  </toolbar>"
1839         "</ui>";
1840     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1841     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1843     GtkUIManager* mgr = gtk_ui_manager_new();
1844     GError* errVal = 0;
1846     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1847     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1849     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1850     if ( prefs->getBool("/toolbox/icononly", true) ) {
1851         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1852     }
1854     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
1855     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1857     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1858     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1861     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1863     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1864     if ( child ) {
1865         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1866     }
1868     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1871 static void
1872 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1876 void toggle_snap_callback (GtkToggleAction *act, gpointer data) { //data points to the toolbox
1878         if (g_object_get_data(G_OBJECT(data), "freeze" )) {
1879                 return;
1880         }
1882         gpointer ptr = g_object_get_data(G_OBJECT(data), "desktop");
1883         g_assert(ptr != NULL);
1885         SPDesktop *dt = reinterpret_cast<SPDesktop*>(ptr);
1886         SPNamedView *nv = sp_desktop_namedview(dt);
1888         if (dt == NULL || nv == NULL) {
1889                 g_warning("No desktop or namedview specified (in toggle_snap_callback)!");
1890                 return;
1891         }
1893         Inkscape::XML::Node *repr = SP_OBJECT_REPR(nv);
1895         if (repr == NULL) {
1896                 g_warning("This namedview doesn't have a xml representation attached!");
1897                 return;
1898         }
1900         bool v = false;
1901         SPAttributeEnum attr = (SPAttributeEnum) GPOINTER_TO_INT(g_object_get_data(G_OBJECT(act), "SP_ATTR_INKSCAPE"));
1903         switch (attr) {
1904         case SP_ATTR_INKSCAPE_SNAP_GLOBAL:
1905                 dt->toggleSnapGlobal();
1906                 break;
1907         case SP_ATTR_INKSCAPE_SNAP_BBOX:
1908                 v = nv->snap_manager.snapprefs.getSnapModeBBox();
1909                 sp_repr_set_boolean(repr, "inkscape:snap-bbox", !v);
1910                 break;
1911         case SP_ATTR_INKSCAPE_BBOX_PATHS:
1912                 v = nv->snap_manager.snapprefs.getSnapToBBoxPath();
1913                 sp_repr_set_boolean(repr, "inkscape:bbox-paths", !v);
1914                 break;
1915         case SP_ATTR_INKSCAPE_BBOX_NODES:
1916                 v = nv->snap_manager.snapprefs.getSnapToBBoxNode();
1917                 sp_repr_set_boolean(repr, "inkscape:bbox-nodes", !v);
1918                 break;
1919         case SP_ATTR_INKSCAPE_SNAP_NODES:
1920                 v = nv->snap_manager.snapprefs.getSnapModeNode();
1921                 sp_repr_set_boolean(repr, "inkscape:snap-nodes", !v);
1922                 break;
1923         case SP_ATTR_INKSCAPE_OBJECT_PATHS:
1924                 v = nv->snap_manager.snapprefs.getSnapToItemPath();
1925                 sp_repr_set_boolean(repr, "inkscape:object-paths", !v);
1926                 break;
1927         case SP_ATTR_INKSCAPE_OBJECT_NODES:
1928                 v = nv->snap_manager.snapprefs.getSnapToItemNode();
1929                 sp_repr_set_boolean(repr, "inkscape:object-nodes", !v);
1930                 break;
1931         case SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES:
1932                 v = nv->snap_manager.snapprefs.getSnapSmoothNodes();
1933                 sp_repr_set_boolean(repr, "inkscape:snap-smooth-nodes", !v);
1934                 break;
1935         case SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS:
1936                 v = nv->snap_manager.snapprefs.getSnapIntersectionCS();
1937                 sp_repr_set_boolean(repr, "inkscape:snap-intersection-paths", !v);
1938                 break;
1939         case SP_ATTR_INKSCAPE_SNAP_CENTER:
1940                 v = nv->snap_manager.snapprefs.getIncludeItemCenter();
1941                 sp_repr_set_boolean(repr, "inkscape:snap-center", !v);
1942                 break;
1943         case SP_ATTR_INKSCAPE_SNAP_GRIDS:
1944                 v = nv->snap_manager.snapprefs.getSnapToGrids();
1945                 sp_repr_set_boolean(repr, "inkscape:snap-grids", !v);
1946                 break;
1947         case SP_ATTR_INKSCAPE_SNAP_TO_GUIDES:
1948                 v = nv->snap_manager.snapprefs.getSnapToGuides();
1949                 sp_repr_set_boolean(repr, "inkscape:snap-to-guides", !v);
1950                 break;
1951         case SP_ATTR_INKSCAPE_SNAP_PAGE:
1952                 v = nv->snap_manager.snapprefs.getSnapToPageBorder();
1953                 sp_repr_set_boolean(repr, "inkscape:snap-page", !v);
1954                 break;
1955         /*case SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE:
1956                 v = nv->snap_manager.snapprefs.getSnapIntersectionGG();
1957                 sp_repr_set_boolean(repr, "inkscape:snap-intersection-grid-guide", !v);
1958                 break;*/
1959         case SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS:
1960                 v = nv->snap_manager.snapprefs.getSnapLineMidpoints();
1961                 sp_repr_set_boolean(repr, "inkscape:snap-midpoints", !v);
1962                 break;
1963         case SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS:
1964                 v = nv->snap_manager.snapprefs.getSnapObjectMidpoints();
1965                 sp_repr_set_boolean(repr, "inkscape:snap-object-midpoints", !v);
1966                 break;
1967         case SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS:
1968                 v = nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints();
1969                 sp_repr_set_boolean(repr, "inkscape:snap-bbox-edge-midpoints", !v);
1970                 break;
1971         case SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS:
1972                 v = nv->snap_manager.snapprefs.getSnapBBoxMidpoints();
1973                 sp_repr_set_boolean(repr, "inkscape:snap-bbox-midpoints", !v);
1974                 break;
1975         default:
1976                 g_warning("toggle_snap_callback has been called with an ID for which no action has been defined");
1977                 break;
1978         }
1980         // The snapping preferences are stored in the document, and therefore toggling makes the document dirty
1981         SPDocument *doc = SP_OBJECT_DOCUMENT(nv);
1982         doc->setModifiedSinceSave();
1985 void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1987         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1988         Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
1990         gchar const * descr =
1991                 "<ui>"
1992                 "  <toolbar name='SnapToolbar'>"
1993                 "    <toolitem action='ToggleSnapGlobal' />"
1994                         "    <separator />"
1995                         "    <toolitem action='ToggleSnapFromBBoxCorner' />"
1996                         "    <toolitem action='ToggleSnapToBBoxPath' />"
1997                         "    <toolitem action='ToggleSnapToBBoxNode' />"
1998                         "    <toolitem action='ToggleSnapToFromBBoxEdgeMidpoints' />"
1999                         "    <toolitem action='ToggleSnapToFromBBoxMidpoints' />"
2000                         "    <separator />"
2001                         "    <toolitem action='ToggleSnapFromNode' />"
2002                         "    <toolitem action='ToggleSnapToItemPath' />"
2003                         "    <toolitem action='ToggleSnapToPathIntersections' />"
2004                         "    <toolitem action='ToggleSnapToItemNode' />"
2005                         "    <toolitem action='ToggleSnapToSmoothNodes' />"
2006                         "    <toolitem action='ToggleSnapToFromLineMidpoints' />"
2007                         "    <toolitem action='ToggleSnapToFromObjectMidpoints' />"
2008                         "    <toolitem action='ToggleSnapToFromCenter' />"
2009                         "    <separator />"
2010                         "    <toolitem action='ToggleSnapToPageBorder' />"
2011                         "    <toolitem action='ToggleSnapToGrids' />"
2012                         "    <toolitem action='ToggleSnapToGuides' />"
2013                   //"    <toolitem action='ToggleSnapToGridGuideIntersections' />"
2014                         "  </toolbar>"
2015                 "</ui>";
2017         Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
2019         {
2020                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapGlobal",                // "name"
2021                                                                                                  _("Snap"),                                             // "label"
2022                                                                                                  _("Enable snapping"),          // "tooltip"
2023                                                                                                  "toggle_snap_global",                  // "iconId"
2024                                                                                                  secondarySize,
2025                                                                                                  SP_ATTR_INKSCAPE_SNAP_GLOBAL);
2027                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2028                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2029         }
2031         {
2032                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromBBoxCorner",// "name"
2033                                                                                                  _("Bounding box"),                             // "label"
2034                                                                                                  _("Snap bounding box corners"),        // "tooltip"
2035                                                                                                  "toggle_snap_bbox",                    // "iconId"
2036                                                                                                  secondarySize,
2037                                                                                                  SP_ATTR_INKSCAPE_SNAP_BBOX);
2039                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2040                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2041         }
2043         {
2044                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxPath",    // "name"
2045                                                                                                  _("Bounding box edges"),               // "label"
2046                                                                                                  _("Snap to edges of a bounding box"),  // "tooltip"
2047                                                                                                  "toggle_snap_to_bbox_path",    // "iconId"
2048                                                                                                  secondarySize,
2049                                                                                                  SP_ATTR_INKSCAPE_BBOX_PATHS);
2051                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2052                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2053         }
2055         {
2056                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxNode",    // "name"
2057                                                                                                  _("Bounding box corners"),             // "label"
2058                                                                                                  _("Snap to bounding box corners"),     // "tooltip"
2059                                                                                                  "toggle_snap_to_bbox_node",    // "iconId"
2060                                                                                                  secondarySize,
2061                                                                                                  SP_ATTR_INKSCAPE_BBOX_NODES);
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("ToggleSnapToFromBBoxEdgeMidpoints",       // "name"
2069                                                                                                  _("BBox Edge Midpoints"),                              // "label"
2070                                                                                                  _("Snap from and to midpoints of bounding box edges"), // "tooltip"
2071                                                                                                  "toggle_snap_to_bbox_edge_midpoints",  // "iconId"
2072                                                                                                  secondarySize,
2073                                                                                                  SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS);
2075                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2076                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2077         }
2079         {
2080                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxMidpoints",   // "name"
2081                                                                                                  _("BBox Midpoints"),                           // "label"
2082                                                                                                  _("Snapping from and to midpoints of bounding boxes"), // "tooltip"
2083                                                                                                  "toggle_snap_to_bbox_midpoints",       // "iconId"
2084                                                                                                  secondarySize,
2085                                                                                                  SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS);
2087                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2088                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2089         }
2091         {
2092                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromNode",              // "name"
2093                                                                                                  _("Nodes"),                                    // "label"
2094                                                                                                  _("Snap nodes"),       // "tooltip"
2095                                                                                                  "toggle_snap_nodes",                   // "iconId"
2096                                                                                                  secondarySize,
2097                                                                                                  SP_ATTR_INKSCAPE_SNAP_NODES);
2099                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2100                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2101         }
2103         {
2104                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemPath",    // "name"
2105                                                                                                  _("Paths"),                                    // "label"
2106                                                                                                  _("Snap to paths"),    // "tooltip"
2107                                                                                                  "toggle_snap_to_paths",                // "iconId"
2108                                                                                                  secondarySize,
2109                                                                                                  SP_ATTR_INKSCAPE_OBJECT_PATHS);
2111                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2112                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2113         }
2115         {
2116                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPathIntersections",                   // "name"
2117                                                                                                  _("Path intersections"),                                               // "label"
2118                                                                                                  _("Snap to path intersections"),       // "tooltip"
2119                                                                                                  "toggle_snap_to_path_intersections",                   // "iconId"
2120                                                                                                  secondarySize,
2121                                                                                                  SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS);
2123                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2124                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2125         }
2127         {
2128                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemNode",    // "name"
2129                                                                                                  _("To nodes"),                                 // "label"
2130                                                                                                  _("Snap to cusp nodes"), // "tooltip"
2131                                                                                                  "toggle_snap_to_nodes",                // "iconId"
2132                                                                                                  secondarySize,
2133                                                                                                  SP_ATTR_INKSCAPE_OBJECT_NODES);
2135                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2136                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2137         }
2139         {
2140                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToSmoothNodes", // "name"
2141                                                                                                  _("Smooth nodes"),                             // "label"
2142                                                                                                  _("Snap to smooth nodes"),// "tooltip"
2143                                                                                                  "toggle_snap_to_smooth_nodes", // "iconId"
2144                                                                                                  secondarySize,
2145                                                                                                  SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES);
2147                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2148                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2149         }
2151         {
2152                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromLineMidpoints",   // "name"
2153                                                                                                  _("Line Midpoints"),                           // "label"
2154                                                                                                  _("Snap from and to midpoints of line segments"),      // "tooltip"
2155                                                                                                  "toggle_snap_to_midpoints",    // "iconId"
2156                                                                                                  secondarySize,
2157                                                                                                  SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS);
2159                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2160                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2161         }
2163         {
2164                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromObjectMidpoints", // "name"
2165                                                                                                  _("Object Midpoints"),                         // "label"
2166                                                                                                  _("Snap from and to midpoints of objects"),    // "tooltip"
2167                                                                                                  "toggle_snap_to_object_midpoints",     // "iconId"
2168                                                                                                  secondarySize,
2169                                                                                                  SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS);
2171                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2172                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2173         }
2175         {
2176                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromCenter",// "name"
2177                                                                                                  _("Center"),                                   // "label"
2178                                                                                                  _("Snap from and to an item's rotation center"),       // "tooltip"
2179                                                                                                  "toggle_snap_center",                  // "iconId"
2180                                                                                                  secondarySize,
2181                                                                                                  SP_ATTR_INKSCAPE_SNAP_CENTER);
2183                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2184                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2185         }
2187         {
2188                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPageBorder",  // "name"
2189                                                                                                  _("Page border"),                              // "label"
2190                                                                                                  _("Snap to the page border"),  // "tooltip"
2191                                                                                                  "toggle_snap_page_border",             // "iconId"
2192                                                                                                  secondarySize,
2193                                                                                                  SP_ATTR_INKSCAPE_SNAP_PAGE);
2195                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2196                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2197         }
2199         {
2200                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGrids",       // "name"
2201                                                                                                  _("Grids"),                            // "label"
2202                                                                                                  _("Snap to grids"),    // "tooltip"
2203                                                                                                  "grid_xy",     // "iconId"
2204                                                                                                  secondarySize,
2205                                                                                                  SP_ATTR_INKSCAPE_SNAP_GRIDS);
2207                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2208                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2209         }
2211         {
2212                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGuides",      // "name"
2213                                                                                                  _("Guides"),                           // "label"
2214                                                                                                  _("Snap to guides"),   // "tooltip"
2215                                                                                                  "guide",       // "iconId"
2216                                                                                                  secondarySize,
2217                                                                                                  SP_ATTR_INKSCAPE_SNAP_TO_GUIDES);
2219                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2220                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2221         }
2223         /*{
2224                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGridGuideIntersections",      // "name"
2225                                                                                                  _("Grid/guide intersections"),                         // "label"
2226                                                                                                  _("Snap to intersections of a grid with a guide"),     // "tooltip"
2227                                                                                                  "toggle_snap_grid_guide_intersections",        // "iconId"
2228                                                                                                  secondarySize,
2229                                                                                                  SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE);
2231                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2232                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2233         }*/
2235     GtkUIManager* mgr = gtk_ui_manager_new();
2236     GError* errVal = 0;
2238     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
2239     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
2241     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/SnapToolbar" );
2242     if ( prefs->getBool("/toolbox/icononly", true) ) {
2243         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
2244     }
2246     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/secondary");
2247     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
2249     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
2250     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
2252     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
2254     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
2255     if ( child ) {
2256         gtk_container_remove( GTK_CONTAINER(toolbox), child );
2257     }
2259     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
2263 void update_snap_toolbox(SPDesktop *desktop, SPEventContext */*eventcontext*/, GtkWidget *toolbox)
2265         g_assert(desktop != NULL);
2266         g_assert(toolbox != NULL);
2268         SPNamedView *nv = sp_desktop_namedview(desktop);
2269         if (nv == NULL) {
2270                 g_warning("Namedview cannot be retrieved (in update_snap_toolbox)!");
2271                 return;
2272         }
2274         Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2276         Glib::RefPtr<Gtk::Action> act1 = mainActions->get_action("ToggleSnapGlobal");
2277         Glib::RefPtr<Gtk::Action> act2 = mainActions->get_action("ToggleSnapFromBBoxCorner");
2278         Glib::RefPtr<Gtk::Action> act3 = mainActions->get_action("ToggleSnapToBBoxPath");
2279         Glib::RefPtr<Gtk::Action> act4 = mainActions->get_action("ToggleSnapToBBoxNode");
2280         Glib::RefPtr<Gtk::Action> act4b = mainActions->get_action("ToggleSnapToFromBBoxEdgeMidpoints");
2281         Glib::RefPtr<Gtk::Action> act4c = mainActions->get_action("ToggleSnapToFromBBoxMidpoints");
2282         Glib::RefPtr<Gtk::Action> act5 = mainActions->get_action("ToggleSnapFromNode");
2283         Glib::RefPtr<Gtk::Action> act6 = mainActions->get_action("ToggleSnapToItemPath");
2284         Glib::RefPtr<Gtk::Action> act6b = mainActions->get_action("ToggleSnapToPathIntersections");
2285         Glib::RefPtr<Gtk::Action> act7 = mainActions->get_action("ToggleSnapToItemNode");
2286         Glib::RefPtr<Gtk::Action> act8 = mainActions->get_action("ToggleSnapToSmoothNodes");
2287         Glib::RefPtr<Gtk::Action> act9 = mainActions->get_action("ToggleSnapToFromLineMidpoints");
2288         Glib::RefPtr<Gtk::Action> act10 = mainActions->get_action("ToggleSnapToFromObjectMidpoints");
2289         Glib::RefPtr<Gtk::Action> act11 = mainActions->get_action("ToggleSnapToFromCenter");
2290         Glib::RefPtr<Gtk::Action> act12 = mainActions->get_action("ToggleSnapToPageBorder");
2291         //Glib::RefPtr<Gtk::Action> act13 = mainActions->get_action("ToggleSnapToGridGuideIntersections");
2292         Glib::RefPtr<Gtk::Action> act14 = mainActions->get_action("ToggleSnapToGrids");
2293         Glib::RefPtr<Gtk::Action> act15 = mainActions->get_action("ToggleSnapToGuides");
2296         if (!act1) {
2297                 return; // The snap actions haven't been defined yet (might be the case during startup)
2298         }
2300         // The ..._set_active calls below will toggle the buttons, but this shouldn't lead to
2301         // changes in our document because we're only updating the UI;
2302         // Setting the "freeze" parameter to true will block the code in toggle_snap_callback()
2303         g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(TRUE));
2305         bool const c1 = nv->snap_manager.snapprefs.getSnapEnabledGlobally();
2306         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act1->gobj()), c1);
2308         bool const c2 = nv->snap_manager.snapprefs.getSnapModeBBox();
2309         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act2->gobj()), c2);
2310         gtk_action_set_sensitive(GTK_ACTION(act2->gobj()), c1);
2312         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act3->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxPath());
2313         gtk_action_set_sensitive(GTK_ACTION(act3->gobj()), c1 && c2);
2314         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxNode());
2315         gtk_action_set_sensitive(GTK_ACTION(act4->gobj()), c1 && c2);
2316         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4b->gobj()), nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints());
2317         gtk_action_set_sensitive(GTK_ACTION(act4b->gobj()), c1 && c2);
2318         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4c->gobj()), nv->snap_manager.snapprefs.getSnapBBoxMidpoints());
2319         gtk_action_set_sensitive(GTK_ACTION(act4c->gobj()), c1 && c2);
2321         bool const c3 = nv->snap_manager.snapprefs.getSnapModeNode();
2322         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act5->gobj()), c3);
2323         gtk_action_set_sensitive(GTK_ACTION(act5->gobj()), c1);
2325         bool const c4 = nv->snap_manager.snapprefs.getSnapToItemPath();
2326         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6->gobj()), c4);
2327         gtk_action_set_sensitive(GTK_ACTION(act6->gobj()), c1 && c3);
2328         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6b->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionCS());
2329         gtk_action_set_sensitive(GTK_ACTION(act6b->gobj()), c1 && c3 && c4);
2330         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act7->gobj()), nv->snap_manager.snapprefs.getSnapToItemNode());
2331         gtk_action_set_sensitive(GTK_ACTION(act7->gobj()), c1 && c3);
2332         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act8->gobj()), nv->snap_manager.snapprefs.getSnapSmoothNodes());
2333         gtk_action_set_sensitive(GTK_ACTION(act8->gobj()), c1 && c3);
2334         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act9->gobj()), nv->snap_manager.snapprefs.getSnapLineMidpoints());
2335         gtk_action_set_sensitive(GTK_ACTION(act9->gobj()), c1 && c3);
2336         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act10->gobj()), nv->snap_manager.snapprefs.getSnapObjectMidpoints());
2337         gtk_action_set_sensitive(GTK_ACTION(act10->gobj()), c1 && c3);
2338         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act11->gobj()), nv->snap_manager.snapprefs.getIncludeItemCenter());
2339         gtk_action_set_sensitive(GTK_ACTION(act11->gobj()), c1 && c3);
2341         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act12->gobj()), nv->snap_manager.snapprefs.getSnapToPageBorder());
2342         gtk_action_set_sensitive(GTK_ACTION(act12->gobj()), c1);
2343         //gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act13->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionGG());
2344         //gtk_action_set_sensitive(GTK_ACTION(act13->gobj()), c1);
2346         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act14->gobj()), nv->snap_manager.snapprefs.getSnapToGrids());
2347         gtk_action_set_sensitive(GTK_ACTION(act14->gobj()), c1);
2348         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act15->gobj()), nv->snap_manager.snapprefs.getSnapToGuides());
2349         gtk_action_set_sensitive(GTK_ACTION(act15->gobj()), c1);
2352         g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(FALSE)); // unfreeze (see above)
2355 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
2357     gtk_widget_show(toolbox_toplevel);
2358     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
2360     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
2361     if (!shown_toolbox) {
2362         return;
2363     }
2364     gtk_widget_show(toolbox);
2366     gtk_widget_show_all(shown_toolbox);
2369 static GtkWidget *
2370 sp_empty_toolbox_new(SPDesktop *desktop)
2372     GtkWidget *tbl = gtk_toolbar_new();
2373     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
2374     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
2376     gtk_widget_show_all(tbl);
2377     sp_set_font_size_smaller (tbl);
2379     return tbl;
2382 #define MODE_LABEL_WIDTH 70
2384 //########################
2385 //##       Star         ##
2386 //########################
2388 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2390     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2392     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2393         // do not remember prefs if this call is initiated by an undo change, because undoing object
2394         // creation sets bogus values to its attributes before it is deleted
2395         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2396         prefs->setInt("/tools/shapes/star/magnitude", (gint)adj->value);
2397     }
2399     // quit if run by the attr_changed listener
2400     if (g_object_get_data( dataKludge, "freeze" )) {
2401         return;
2402     }
2404     // in turn, prevent listener from responding
2405     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2407     bool modmade = false;
2409     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2410     GSList const *items = selection->itemList();
2411     for (; items != NULL; items = items->next) {
2412         if (SP_IS_STAR((SPItem *) items->data)) {
2413             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2414             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
2415             sp_repr_set_svg_double(repr, "sodipodi:arg2",
2416                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
2417                                     + M_PI / (gint)adj->value));
2418             SP_OBJECT((SPItem *) items->data)->updateRepr();
2419             modmade = true;
2420         }
2421     }
2422     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2423                                    _("Star: Change number of corners"));
2425     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2428 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2430     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2432     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2433         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2434         prefs->setDouble("/tools/shapes/star/proportion", adj->value);
2435     }
2437     // quit if run by the attr_changed listener
2438     if (g_object_get_data( dataKludge, "freeze" )) {
2439         return;
2440     }
2442     // in turn, prevent listener from responding
2443     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2445     bool modmade = false;
2446     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2447     GSList const *items = selection->itemList();
2448     for (; items != NULL; items = items->next) {
2449         if (SP_IS_STAR((SPItem *) items->data)) {
2450             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2452             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2453             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2454             if (r2 < r1) {
2455                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
2456             } else {
2457                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
2458             }
2460             SP_OBJECT((SPItem *) items->data)->updateRepr();
2461             modmade = true;
2462         }
2463     }
2465     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2466                                    _("Star: Change spoke ratio"));
2468     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2471 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
2473     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2474     bool flat = ege_select_one_action_get_active( act ) == 0;
2476     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2477         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2478         prefs->setBool( "/tools/shapes/star/isflatsided", flat);
2479     }
2481     // quit if run by the attr_changed listener
2482     if (g_object_get_data( dataKludge, "freeze" )) {
2483         return;
2484     }
2486     // in turn, prevent listener from responding
2487     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2489     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2490     GSList const *items = selection->itemList();
2491     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2492     bool modmade = false;
2494     if ( prop_action ) {
2495         gtk_action_set_sensitive( prop_action, !flat );
2496     }
2498     for (; items != NULL; items = items->next) {
2499         if (SP_IS_STAR((SPItem *) items->data)) {
2500             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2501             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
2502             SP_OBJECT((SPItem *) items->data)->updateRepr();
2503             modmade = true;
2504         }
2505     }
2507     if (modmade) {
2508         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2509                          flat ? _("Make polygon") : _("Make star"));
2510     }
2512     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2515 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2517     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2519     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2520         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2521         prefs->setDouble("/tools/shapes/star/rounded", (gdouble) adj->value);
2522     }
2524     // quit if run by the attr_changed listener
2525     if (g_object_get_data( dataKludge, "freeze" )) {
2526         return;
2527     }
2529     // in turn, prevent listener from responding
2530     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2532     bool modmade = false;
2534     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2535     GSList const *items = selection->itemList();
2536     for (; items != NULL; items = items->next) {
2537         if (SP_IS_STAR((SPItem *) items->data)) {
2538             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2539             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2540             SP_OBJECT(items->data)->updateRepr();
2541             modmade = true;
2542         }
2543     }
2544     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2545                                    _("Star: Change rounding"));
2547     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2550 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2552     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2554     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2555         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2556         prefs->setDouble("/tools/shapes/star/randomized", (gdouble) adj->value);
2557     }
2559     // quit if run by the attr_changed listener
2560     if (g_object_get_data( dataKludge, "freeze" )) {
2561         return;
2562     }
2564     // in turn, prevent listener from responding
2565     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2567     bool modmade = false;
2569     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2570     GSList const *items = selection->itemList();
2571     for (; items != NULL; items = items->next) {
2572         if (SP_IS_STAR((SPItem *) items->data)) {
2573             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2574             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2575             SP_OBJECT(items->data)->updateRepr();
2576             modmade = true;
2577         }
2578     }
2579     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2580                                    _("Star: Change randomization"));
2582     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2586 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2587                                        gchar const */*old_value*/, gchar const */*new_value*/,
2588                                        bool /*is_interactive*/, gpointer data)
2590     GtkWidget *tbl = GTK_WIDGET(data);
2592     // quit if run by the _changed callbacks
2593     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2594         return;
2595     }
2597     // in turn, prevent callbacks from responding
2598     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2600     GtkAdjustment *adj = 0;
2602     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2603     bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2605     if (!strcmp(name, "inkscape:randomized")) {
2606         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2607         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2608     } else if (!strcmp(name, "inkscape:rounded")) {
2609         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2610         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2611     } else if (!strcmp(name, "inkscape:flatsided")) {
2612         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2613         char const *flatsides = repr->attribute("inkscape:flatsided");
2614         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2615         if ( flatsides && !strcmp(flatsides,"false") ) {
2616             ege_select_one_action_set_active( flat_action, 1 );
2617             gtk_action_set_sensitive( prop_action, TRUE );
2618         } else {
2619             ege_select_one_action_set_active( flat_action, 0 );
2620             gtk_action_set_sensitive( prop_action, FALSE );
2621         }
2622     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2623         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2624         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2625         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2626         if (r2 < r1) {
2627             gtk_adjustment_set_value(adj, r2/r1);
2628         } else {
2629             gtk_adjustment_set_value(adj, r1/r2);
2630         }
2631     } else if (!strcmp(name, "sodipodi:sides")) {
2632         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2633         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2634     }
2636     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2640 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2642     NULL, /* child_added */
2643     NULL, /* child_removed */
2644     star_tb_event_attr_changed,
2645     NULL, /* content_changed */
2646     NULL  /* order_changed */
2647 };
2650 /**
2651  *  \param selection Should not be NULL.
2652  */
2653 static void
2654 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2656     int n_selected = 0;
2657     Inkscape::XML::Node *repr = NULL;
2659     purge_repr_listener( tbl, tbl );
2661     for (GSList const *items = selection->itemList();
2662          items != NULL;
2663          items = items->next)
2664     {
2665         if (SP_IS_STAR((SPItem *) items->data)) {
2666             n_selected++;
2667             repr = SP_OBJECT_REPR((SPItem *) items->data);
2668         }
2669     }
2671     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2673     if (n_selected == 0) {
2674         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2675     } else if (n_selected == 1) {
2676         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2678         if (repr) {
2679             g_object_set_data( tbl, "repr", repr );
2680             Inkscape::GC::anchor(repr);
2681             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2682             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2683         }
2684     } else {
2685         // FIXME: implement averaging of all parameters for multiple selected stars
2686         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2687         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2688     }
2692 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2694     // FIXME: in this and all other _default functions, set some flag telling the value_changed
2695     // callbacks to lump all the changes for all selected objects in one undo step
2697     GtkAdjustment *adj = 0;
2699     // fixme: make settable in prefs!
2700     gint mag = 5;
2701     gdouble prop = 0.5;
2702     gboolean flat = FALSE;
2703     gdouble randomized = 0;
2704     gdouble rounded = 0;
2706     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2707     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2709     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2710     gtk_action_set_sensitive( sb2, !flat );
2712     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2713     gtk_adjustment_set_value(adj, mag);
2714     gtk_adjustment_value_changed(adj);
2716     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2717     gtk_adjustment_set_value(adj, prop);
2718     gtk_adjustment_value_changed(adj);
2720     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2721     gtk_adjustment_set_value(adj, rounded);
2722     gtk_adjustment_value_changed(adj);
2724     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2725     gtk_adjustment_set_value(adj, randomized);
2726     gtk_adjustment_value_changed(adj);
2730 void
2731 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2733     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2734     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2735     GtkWidget *l = gtk_label_new(NULL);
2736     gtk_label_set_markup(GTK_LABEL(l), title);
2737     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2738     if ( GTK_IS_TOOLBAR(tbl) ) {
2739         gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2740     } else {
2741         gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2742     }
2743     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2747 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2749     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
2751     {
2752         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2753         ege_output_action_set_use_markup( act, TRUE );
2754         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2755         g_object_set_data( holder, "mode_action", act );
2756     }
2758     {
2759         EgeAdjustmentAction* eact = 0;
2760         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2761         bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2763         /* Flatsided checkbox */
2764         {
2765             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2767             GtkTreeIter iter;
2768             gtk_list_store_append( model, &iter );
2769             gtk_list_store_set( model, &iter,
2770                                 0, _("Polygon"),
2771                                 1, _("Regular polygon (with one handle) instead of a star"),
2772                                 2, "star_flat",
2773                                 -1 );
2775             gtk_list_store_append( model, &iter );
2776             gtk_list_store_set( model, &iter,
2777                                 0, _("Star"),
2778                                 1, _("Star instead of a regular polygon (with one handle)"),
2779                                 2, "star_angled",
2780                                 -1 );
2782             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2783             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2784             g_object_set_data( holder, "flat_action", act );
2786             ege_select_one_action_set_appearance( act, "full" );
2787             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2788             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2789             ege_select_one_action_set_icon_column( act, 2 );
2790             ege_select_one_action_set_icon_size( act, secondarySize );
2791             ege_select_one_action_set_tooltip_column( act, 1  );
2793             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2794             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2795         }
2797         /* Magnitude */
2798         {
2799         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2800         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2801         eact = create_adjustment_action( "MagnitudeAction",
2802                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2803                                          "/tools/shapes/star/magnitude", 3,
2804                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2805                                          3, 1024, 1, 5,
2806                                          labels, values, G_N_ELEMENTS(labels),
2807                                          sp_stb_magnitude_value_changed,
2808                                          1.0, 0 );
2809         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2810         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2811         }
2813         /* Spoke ratio */
2814         {
2815         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2816         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2817         eact = create_adjustment_action( "SpokeAction",
2818                                          _("Spoke ratio"), _("Spoke ratio:"),
2819                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2820                                          // Base radius is the same for the closest handle.
2821                                          _("Base radius to tip radius ratio"),
2822                                          "/tools/shapes/star/proportion", 0.5,
2823                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2824                                          0.01, 1.0, 0.01, 0.1,
2825                                          labels, values, G_N_ELEMENTS(labels),
2826                                          sp_stb_proportion_value_changed );
2827         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2828         g_object_set_data( holder, "prop_action", eact );
2829         }
2831         if ( !isFlatSided ) {
2832             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2833         } else {
2834             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2835         }
2837         /* Roundedness */
2838         {
2839         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2840         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2841         eact = create_adjustment_action( "RoundednessAction",
2842                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2843                                          "/tools/shapes/star/rounded", 0.0,
2844                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2845                                          -10.0, 10.0, 0.01, 0.1,
2846                                          labels, values, G_N_ELEMENTS(labels),
2847                                          sp_stb_rounded_value_changed );
2848         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2849         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2850         }
2852         /* Randomization */
2853         {
2854         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2855         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2856         eact = create_adjustment_action( "RandomizationAction",
2857                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2858                                          "/tools/shapes/star/randomized", 0.0,
2859                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2860                                          -10.0, 10.0, 0.001, 0.01,
2861                                          labels, values, G_N_ELEMENTS(labels),
2862                                          sp_stb_randomized_value_changed, 0.1, 3 );
2863         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2864         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2865         }
2866     }
2868     {
2869         /* Reset */
2870         {
2871             GtkAction* act = gtk_action_new( "StarResetAction",
2872                                              _("Defaults"),
2873                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2874                                              GTK_STOCK_CLEAR );
2875             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2876             gtk_action_group_add_action( mainActions, act );
2877             gtk_action_set_sensitive( act, TRUE );
2878         }
2879     }
2881     sigc::connection *connection = new sigc::connection(
2882         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2883         );
2884     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2885     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2889 //########################
2890 //##       Rect         ##
2891 //########################
2893 static void sp_rtb_sensitivize( GObject *tbl )
2895     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2896     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2897     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2899     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2900         gtk_action_set_sensitive( not_rounded, FALSE );
2901     } else {
2902         gtk_action_set_sensitive( not_rounded, TRUE );
2903     }
2907 static void
2908 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2909                           void (*setter)(SPRect *, gdouble))
2911     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2913     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2914     SPUnit const *unit = tracker->getActiveUnit();
2916     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2917         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2918         prefs->setDouble(Glib::ustring("/tools/shapes/rect/") + value_name, sp_units_get_pixels(adj->value, *unit));
2919     }
2921     // quit if run by the attr_changed listener
2922     if (g_object_get_data( tbl, "freeze" )) {
2923         return;
2924     }
2926     // in turn, prevent listener from responding
2927     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2929     bool modmade = false;
2930     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2931     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2932         if (SP_IS_RECT(items->data)) {
2933             if (adj->value != 0) {
2934                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2935             } else {
2936                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2937             }
2938             modmade = true;
2939         }
2940     }
2942     sp_rtb_sensitivize( tbl );
2944     if (modmade) {
2945         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2946                                    _("Change rectangle"));
2947     }
2949     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2952 static void
2953 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2955     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2958 static void
2959 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2961     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2964 static void
2965 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2967     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2970 static void
2971 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2973     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2978 static void
2979 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2981     GtkAdjustment *adj = 0;
2983     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2984     gtk_adjustment_set_value(adj, 0.0);
2985     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2986     gtk_adjustment_value_changed(adj);
2988     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2989     gtk_adjustment_set_value(adj, 0.0);
2990     gtk_adjustment_value_changed(adj);
2992     sp_rtb_sensitivize( obj );
2995 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2996                                        gchar const */*old_value*/, gchar const */*new_value*/,
2997                                        bool /*is_interactive*/, gpointer data)
2999     GObject *tbl = G_OBJECT(data);
3001     // quit if run by the _changed callbacks
3002     if (g_object_get_data( tbl, "freeze" )) {
3003         return;
3004     }
3006     // in turn, prevent callbacks from responding
3007     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3009     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
3010     SPUnit const *unit = tracker->getActiveUnit();
3012     gpointer item = g_object_get_data( tbl, "item" );
3013     if (item && SP_IS_RECT(item)) {
3014         {
3015             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
3016             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
3017             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
3018         }
3020         {
3021             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
3022             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
3023             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
3024         }
3026         {
3027             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
3028             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
3029             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
3030         }
3032         {
3033             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
3034             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
3035             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
3036         }
3037     }
3039     sp_rtb_sensitivize( tbl );
3041     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3045 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
3046     NULL, /* child_added */
3047     NULL, /* child_removed */
3048     rect_tb_event_attr_changed,
3049     NULL, /* content_changed */
3050     NULL  /* order_changed */
3051 };
3053 /**
3054  *  \param selection should not be NULL.
3055  */
3056 static void
3057 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3059     int n_selected = 0;
3060     Inkscape::XML::Node *repr = NULL;
3061     SPItem *item = NULL;
3063     if ( g_object_get_data( tbl, "repr" ) ) {
3064         g_object_set_data( tbl, "item", NULL );
3065     }
3066     purge_repr_listener( tbl, tbl );
3068     for (GSList const *items = selection->itemList();
3069          items != NULL;
3070          items = items->next) {
3071         if (SP_IS_RECT((SPItem *) items->data)) {
3072             n_selected++;
3073             item = (SPItem *) items->data;
3074             repr = SP_OBJECT_REPR(item);
3075         }
3076     }
3078     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3080     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3082     if (n_selected == 0) {
3083         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3085         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3086         gtk_action_set_sensitive(w, FALSE);
3087         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3088         gtk_action_set_sensitive(h, FALSE);
3090     } else if (n_selected == 1) {
3091         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3092         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3094         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3095         gtk_action_set_sensitive(w, TRUE);
3096         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3097         gtk_action_set_sensitive(h, TRUE);
3099         if (repr) {
3100             g_object_set_data( tbl, "repr", repr );
3101             g_object_set_data( tbl, "item", item );
3102             Inkscape::GC::anchor(repr);
3103             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
3104             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
3105         }
3106     } else {
3107         // FIXME: implement averaging of all parameters for multiple selected
3108         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3109         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3110         sp_rtb_sensitivize( tbl );
3111     }
3115 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3117     EgeAdjustmentAction* eact = 0;
3118     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3120     {
3121         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
3122         ege_output_action_set_use_markup( act, TRUE );
3123         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3124         g_object_set_data( holder, "mode_action", act );
3125     }
3127     // rx/ry units menu: create
3128     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
3129     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
3130     // fixme: add % meaning per cent of the width/height
3131     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
3132     g_object_set_data( holder, "tracker", tracker );
3134     /* W */
3135     {
3136         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3137         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3138         eact = create_adjustment_action( "RectWidthAction",
3139                                          _("Width"), _("W:"), _("Width of rectangle"),
3140                                          "/tools/shapes/rect/width", 0,
3141                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
3142                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3143                                          labels, values, G_N_ELEMENTS(labels),
3144                                          sp_rtb_width_value_changed );
3145         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3146         g_object_set_data( holder, "width_action", eact );
3147         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3148         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3149     }
3151     /* H */
3152     {
3153         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3154         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3155         eact = create_adjustment_action( "RectHeightAction",
3156                                          _("Height"), _("H:"), _("Height of rectangle"),
3157                                          "/tools/shapes/rect/height", 0,
3158                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3159                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3160                                          labels, values, G_N_ELEMENTS(labels),
3161                                          sp_rtb_height_value_changed );
3162         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3163         g_object_set_data( holder, "height_action", eact );
3164         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3165         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3166     }
3168     /* rx */
3169     {
3170         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3171         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3172         eact = create_adjustment_action( "RadiusXAction",
3173                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
3174                                          "/tools/shapes/rect/rx", 0,
3175                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3176                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3177                                          labels, values, G_N_ELEMENTS(labels),
3178                                          sp_rtb_rx_value_changed);
3179         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3180         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3181     }
3183     /* ry */
3184     {
3185         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3186         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3187         eact = create_adjustment_action( "RadiusYAction",
3188                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
3189                                          "/tools/shapes/rect/ry", 0,
3190                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3191                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3192                                          labels, values, G_N_ELEMENTS(labels),
3193                                          sp_rtb_ry_value_changed);
3194         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3195         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3196     }
3198     // add the units menu
3199     {
3200         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
3201         gtk_action_group_add_action( mainActions, act );
3202     }
3204     /* Reset */
3205     {
3206         InkAction* inky = ink_action_new( "RectResetAction",
3207                                           _("Not rounded"),
3208                                           _("Make corners sharp"),
3209                                           "squared_corner",
3210                                           secondarySize );
3211         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
3212         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3213         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3214         g_object_set_data( holder, "not_rounded", inky );
3215     }
3217     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
3218     sp_rtb_sensitivize( holder );
3220     sigc::connection *connection = new sigc::connection(
3221         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
3222         );
3223     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3224     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3227 //########################
3228 //##       3D Box       ##
3229 //########################
3231 // normalize angle so that it lies in the interval [0,360]
3232 static double box3d_normalize_angle (double a) {
3233     double angle = a + ((int) (a/360.0))*360;
3234     if (angle < 0) {
3235         angle += 360.0;
3236     }
3237     return angle;
3240 static void
3241 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
3242                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
3243     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
3244     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
3245     //       are reset).
3246     bool is_infinite = !persp3d_VP_is_finite(persp, axis);
3248     if (is_infinite) {
3249         gtk_toggle_action_set_active(tact, TRUE);
3250         gtk_action_set_sensitive(act, TRUE);
3252         double angle = persp3d_get_infinite_angle(persp, axis);
3253         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
3254             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
3255         }
3256     } else {
3257         gtk_toggle_action_set_active(tact, FALSE);
3258         gtk_action_set_sensitive(act, FALSE);
3259     }
3262 static void
3263 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
3264     if (!persp_repr) {
3265         g_print ("No perspective given to box3d_resync_toolbar().\n");
3266         return;
3267     }
3269     GtkWidget *tbl = GTK_WIDGET(data);
3270     GtkAdjustment *adj = 0;
3271     GtkAction *act = 0;
3272     GtkToggleAction *tact = 0;
3273     Persp3D *persp = persp3d_get_from_repr(persp_repr);
3274     {
3275         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
3276         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
3277         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
3279         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
3280     }
3281     {
3282         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
3283         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
3284         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
3286         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
3287     }
3288     {
3289         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
3290         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
3291         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
3293         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
3294     }
3297 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3298                                                   gchar const */*old_value*/, gchar const */*new_value*/,
3299                                                   bool /*is_interactive*/, gpointer data)
3301     GtkWidget *tbl = GTK_WIDGET(data);
3303     // quit if run by the attr_changed or selection changed listener
3304     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3305         return;
3306     }
3308     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
3309     // sp_document_maybe_done() when the document is undo insensitive)
3310     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3312     // TODO: Only update the appropriate part of the toolbar
3313 //    if (!strcmp(name, "inkscape:vp_z")) {
3314         box3d_resync_toolbar(repr, G_OBJECT(tbl));
3315 //    }
3317     Persp3D *persp = persp3d_get_from_repr(repr);
3318     persp3d_update_box_reprs(persp);
3320     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3323 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
3325     NULL, /* child_added */
3326     NULL, /* child_removed */
3327     box3d_persp_tb_event_attr_changed,
3328     NULL, /* content_changed */
3329     NULL  /* order_changed */
3330 };
3332 /**
3333  *  \param selection Should not be NULL.
3334  */
3335 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
3336 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
3337 static void
3338 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3340     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
3341     // disable the angle entry fields for this direction (otherwise entering a value in them should only
3342     // update the perspectives with infinite VPs and leave the other ones untouched).
3344     Inkscape::XML::Node *persp_repr = NULL;
3345     purge_repr_listener(tbl, tbl);
3347     SPItem *item = selection->singleItem();
3348     if (item && SP_IS_BOX3D(item)) {
3349         // FIXME: Also deal with multiple selected boxes
3350         SPBox3D *box = SP_BOX3D(item);
3351         Persp3D *persp = box3d_get_perspective(box);
3352         persp_repr = SP_OBJECT_REPR(persp);
3353         if (persp_repr) {
3354             g_object_set_data(tbl, "repr", persp_repr);
3355             Inkscape::GC::anchor(persp_repr);
3356             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
3357             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
3358         }
3360         inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
3361         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3362         prefs->setString("/tools/shapes/3dbox/persp", persp_repr->attribute("id"));
3364         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
3365         box3d_resync_toolbar(persp_repr, tbl);
3366         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
3367     }
3370 static void
3371 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
3373     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3374     SPDocument *document = sp_desktop_document(desktop);
3376     // quit if run by the attr_changed or selection changed listener
3377     if (g_object_get_data( dataKludge, "freeze" )) {
3378         return;
3379     }
3381     // in turn, prevent listener from responding
3382     g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
3384     //Persp3D *persp = document->current_persp3d;
3385     std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
3386     if (sel_persps.empty()) {
3387         // this can happen when the document is created; we silently ignore it
3388         return;
3389     }
3390     Persp3D *persp = sel_persps.front();
3392     persp->tmat.set_infinite_direction (axis, adj->value);
3393     SP_OBJECT(persp)->updateRepr();
3395     // TODO: use the correct axis here, too
3396     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
3398     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
3402 static void
3403 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3405     box3d_angle_value_changed(adj, dataKludge, Proj::X);
3408 static void
3409 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3411     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
3414 static void
3415 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3417     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
3421 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
3423     // TODO: Take all selected perspectives into account
3424     std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
3425     if (sel_persps.empty()) {
3426         // this can happen when the document is created; we silently ignore it
3427         return;
3428     }
3429     Persp3D *persp = sel_persps.front();
3431     bool set_infinite = gtk_toggle_action_get_active(act);
3432     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
3435 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3437     box3d_vp_state_changed(act, box3d_angle, Proj::X);
3440 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3442     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
3445 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3447     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
3450 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3452     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3453     EgeAdjustmentAction* eact = 0;
3454     SPDocument *document = sp_desktop_document (desktop);
3455     Persp3D *persp = document->current_persp3d;
3457     EgeAdjustmentAction* box3d_angle_x = 0;
3458     EgeAdjustmentAction* box3d_angle_y = 0;
3459     EgeAdjustmentAction* box3d_angle_z = 0;
3461     /* Angle X */
3462     {
3463         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3464         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3465         eact = create_adjustment_action( "3DBoxAngleXAction",
3466                                          _("Angle in X direction"), _("Angle X:"),
3467                                          // Translators: PL is short for 'perspective line'
3468                                          _("Angle of PLs in X direction"),
3469                                          "/tools/shapes/3dbox/box3d_angle_x", 30,
3470                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
3471                                          -360.0, 360.0, 1.0, 10.0,
3472                                          labels, values, G_N_ELEMENTS(labels),
3473                                          box3d_angle_x_value_changed );
3474         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3475         g_object_set_data( holder, "box3d_angle_x_action", eact );
3476         box3d_angle_x = eact;
3477     }
3479     if (!persp3d_VP_is_finite(persp, Proj::X)) {
3480         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3481     } else {
3482         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3483     }
3486     /* VP X state */
3487     {
3488         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
3489                                                       // Translators: VP is short for 'vanishing point'
3490                                                       _("State of VP in X direction"),
3491                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
3492                                                       "toggle_vp_x",
3493                                                       Inkscape::ICON_SIZE_DECORATION );
3494         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3495         g_object_set_data( holder, "box3d_vp_x_state_action", act );
3496         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
3497         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3498         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3499     }
3501     /* Angle Y */
3502     {
3503         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3504         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3505         eact = create_adjustment_action( "3DBoxAngleYAction",
3506                                          _("Angle in Y direction"), _("Angle Y:"),
3507                                          // Translators: PL is short for 'perspective line'
3508                                          _("Angle of PLs in Y direction"),
3509                                          "/tools/shapes/3dbox/box3d_angle_y", 30,
3510                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3511                                          -360.0, 360.0, 1.0, 10.0,
3512                                          labels, values, G_N_ELEMENTS(labels),
3513                                          box3d_angle_y_value_changed );
3514         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3515         g_object_set_data( holder, "box3d_angle_y_action", eact );
3516         box3d_angle_y = eact;
3517     }
3519     if (!persp3d_VP_is_finite(persp, Proj::Y)) {
3520         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3521     } else {
3522         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3523     }
3525     /* VP Y state */
3526     {
3527         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
3528                                                       // Translators: VP is short for 'vanishing point'
3529                                                       _("State of VP in Y direction"),
3530                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
3531                                                       "toggle_vp_y",
3532                                                       Inkscape::ICON_SIZE_DECORATION );
3533         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3534         g_object_set_data( holder, "box3d_vp_y_state_action", act );
3535         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
3536         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3537         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3538     }
3540     /* Angle Z */
3541     {
3542         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3543         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3544         eact = create_adjustment_action( "3DBoxAngleZAction",
3545                                          _("Angle in Z direction"), _("Angle Z:"),
3546                                          // Translators: PL is short for 'perspective line'
3547                                          _("Angle of PLs in Z direction"),
3548                                          "/tools/shapes/3dbox/box3d_angle_z", 30,
3549                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3550                                          -360.0, 360.0, 1.0, 10.0,
3551                                          labels, values, G_N_ELEMENTS(labels),
3552                                          box3d_angle_z_value_changed );
3553         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3554         g_object_set_data( holder, "box3d_angle_z_action", eact );
3555         box3d_angle_z = eact;
3556     }
3558     if (!persp3d_VP_is_finite(persp, Proj::Z)) {
3559         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3560     } else {
3561         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3562     }
3564     /* VP Z state */
3565     {
3566         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3567                                                       // Translators: VP is short for 'vanishing point'
3568                                                       _("State of VP in Z direction"),
3569                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3570                                                       "toggle_vp_z",
3571                                                       Inkscape::ICON_SIZE_DECORATION );
3572         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3573         g_object_set_data( holder, "box3d_vp_z_state_action", act );
3574         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3575         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3576         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3577     }
3579     sigc::connection *connection = new sigc::connection(
3580         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3581        );
3582     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3583     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3586 //########################
3587 //##       Spiral       ##
3588 //########################
3590 static void
3591 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, Glib::ustring const &value_name)
3593     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3595     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3596         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3597         prefs->setDouble("/tools/shapes/spiral/" + value_name, adj->value);
3598     }
3600     // quit if run by the attr_changed listener
3601     if (g_object_get_data( tbl, "freeze" )) {
3602         return;
3603     }
3605     // in turn, prevent listener from responding
3606     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3608     gchar* namespaced_name = g_strconcat("sodipodi:", value_name.data(), NULL);
3610     bool modmade = false;
3611     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3612          items != NULL;
3613          items = items->next)
3614     {
3615         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3616             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3617             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3618             SP_OBJECT((SPItem *) items->data)->updateRepr();
3619             modmade = true;
3620         }
3621     }
3623     g_free(namespaced_name);
3625     if (modmade) {
3626         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3627                                    _("Change spiral"));
3628     }
3630     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3633 static void
3634 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3636     sp_spl_tb_value_changed(adj, tbl, "revolution");
3639 static void
3640 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3642     sp_spl_tb_value_changed(adj, tbl, "expansion");
3645 static void
3646 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3648     sp_spl_tb_value_changed(adj, tbl, "t0");
3651 static void
3652 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3654     GtkWidget *tbl = GTK_WIDGET(obj);
3656     GtkAdjustment *adj;
3658     // fixme: make settable
3659     gdouble rev = 5;
3660     gdouble exp = 1.0;
3661     gdouble t0 = 0.0;
3663     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3664     gtk_adjustment_set_value(adj, rev);
3665     gtk_adjustment_value_changed(adj);
3667     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3668     gtk_adjustment_set_value(adj, exp);
3669     gtk_adjustment_value_changed(adj);
3671     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3672     gtk_adjustment_set_value(adj, t0);
3673     gtk_adjustment_value_changed(adj);
3675     spinbutton_defocus(GTK_OBJECT(tbl));
3679 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3680                                          gchar const */*old_value*/, gchar const */*new_value*/,
3681                                          bool /*is_interactive*/, gpointer data)
3683     GtkWidget *tbl = GTK_WIDGET(data);
3685     // quit if run by the _changed callbacks
3686     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3687         return;
3688     }
3690     // in turn, prevent callbacks from responding
3691     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3693     GtkAdjustment *adj;
3694     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3695     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3697     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3698     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3700     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3701     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3703     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3707 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3708     NULL, /* child_added */
3709     NULL, /* child_removed */
3710     spiral_tb_event_attr_changed,
3711     NULL, /* content_changed */
3712     NULL  /* order_changed */
3713 };
3715 static void
3716 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3718     int n_selected = 0;
3719     Inkscape::XML::Node *repr = NULL;
3721     purge_repr_listener( tbl, tbl );
3723     for (GSList const *items = selection->itemList();
3724          items != NULL;
3725          items = items->next)
3726     {
3727         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3728             n_selected++;
3729             repr = SP_OBJECT_REPR((SPItem *) items->data);
3730         }
3731     }
3733     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3735     if (n_selected == 0) {
3736         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3737     } else if (n_selected == 1) {
3738         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3740         if (repr) {
3741             g_object_set_data( tbl, "repr", repr );
3742             Inkscape::GC::anchor(repr);
3743             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3744             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3745         }
3746     } else {
3747         // FIXME: implement averaging of all parameters for multiple selected
3748         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3749         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3750     }
3754 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3756     EgeAdjustmentAction* eact = 0;
3757     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3759     {
3760         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3761         ege_output_action_set_use_markup( act, TRUE );
3762         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3763         g_object_set_data( holder, "mode_action", act );
3764     }
3766     /* Revolution */
3767     {
3768         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3769         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3770         eact = create_adjustment_action( "SpiralRevolutionAction",
3771                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3772                                          "/tools/shapes/spiral/revolution", 3.0,
3773                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3774                                          0.01, 1024.0, 0.1, 1.0,
3775                                          labels, values, G_N_ELEMENTS(labels),
3776                                          sp_spl_tb_revolution_value_changed, 1, 2);
3777         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3778     }
3780     /* Expansion */
3781     {
3782         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3783         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3784         eact = create_adjustment_action( "SpiralExpansionAction",
3785                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3786                                          "/tools/shapes/spiral/expansion", 1.0,
3787                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3788                                          0.0, 1000.0, 0.01, 1.0,
3789                                          labels, values, G_N_ELEMENTS(labels),
3790                                          sp_spl_tb_expansion_value_changed);
3791         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3792     }
3794     /* T0 */
3795     {
3796         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3797         gdouble values[] = {0, 0.5, 0.9};
3798         eact = create_adjustment_action( "SpiralT0Action",
3799                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3800                                          "/tools/shapes/spiral/t0", 0.0,
3801                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3802                                          0.0, 0.999, 0.01, 1.0,
3803                                          labels, values, G_N_ELEMENTS(labels),
3804                                          sp_spl_tb_t0_value_changed);
3805         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3806     }
3808     /* Reset */
3809     {
3810         InkAction* inky = ink_action_new( "SpiralResetAction",
3811                                           _("Defaults"),
3812                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3813                                           GTK_STOCK_CLEAR,
3814                                           secondarySize );
3815         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3816         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3817     }
3820     sigc::connection *connection = new sigc::connection(
3821         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3822         );
3823     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3824     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3827 //########################
3828 //##     Pen/Pencil     ##
3829 //########################
3831 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3832 static Glib::ustring const
3833 freehand_tool_name(GObject *dataKludge)
3835     SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3836     return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3837              ? "/tools/freehand/pen"
3838              : "/tools/freehand/pencil" );
3841 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3843     gint mode = ege_select_one_action_get_active(act);
3845     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3846     prefs->setInt(freehand_tool_name(tbl) + "/freehand-mode", mode);
3848     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3850     // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3851     // preparatory work here
3852     if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3853         SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3854         sp_pen_context_set_polyline_mode(pc);
3855     }
3858 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3860     /* Freehand mode toggle buttons */
3861     {
3862         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3863         guint freehandMode = prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/freehand-mode" : "/tools/freehand/pen/freehand-mode" ), 0);
3864         Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3866         {
3867             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3869             GtkTreeIter iter;
3870             gtk_list_store_append( model, &iter );
3871             gtk_list_store_set( model, &iter,
3872                                 0, _("Bezier"),
3873                                 1, _("Create regular Bezier path"),
3874                                 2, "bezier_mode",
3875                                 -1 );
3877             gtk_list_store_append( model, &iter );
3878             gtk_list_store_set( model, &iter,
3879                                 0, _("Spiro"),
3880                                 1, _("Create Spiro path"),
3881                                 2, "spiro_splines_mode",
3882                                 -1 );
3884             if (!tool_is_pencil) {
3885                 gtk_list_store_append( model, &iter );
3886                 gtk_list_store_set( model, &iter,
3887                                     0, _("Zigzag"),
3888                                     1, _("Create a sequence of straight line segments"),
3889                                     2, "polylines_mode",
3890                                     -1 );
3892                 gtk_list_store_append( model, &iter );
3893                 gtk_list_store_set( model, &iter,
3894                                     0, _("Paraxial"),
3895                                     1, _("Create a sequence of paraxial line segments"),
3896                                     2, "paraxial_lines_mode",
3897                                     -1 );
3898             }
3900             EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3901                                                                 "FreehandModeActionPencil" :
3902                                                                 "FreehandModeActionPen",
3903                                                                 (_("Mode:")), ("Mode"), NULL, GTK_TREE_MODEL(model) );
3904             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3906             ege_select_one_action_set_appearance( act, "full" );
3907             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3908             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3909             ege_select_one_action_set_icon_column( act, 2 );
3910             ege_select_one_action_set_icon_size( act, secondarySize );
3911             ege_select_one_action_set_tooltip_column( act, 1  );
3913             ege_select_one_action_set_active( act, freehandMode);
3914             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3915         }
3916     }
3919 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3920     gint shape = ege_select_one_action_get_active( act );
3921     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3922     prefs->setInt(freehand_tool_name(dataKludge) + "/shape", shape);
3925 /**
3926  * \brief Generate the list of freehand advanced shape option entries.
3927  */
3928 GList * freehand_shape_dropdown_items_list() {
3929     GList *glist = NULL;
3931     glist = g_list_append (glist, _("None"));
3932     glist = g_list_append (glist, _("Triangle in"));
3933     glist = g_list_append (glist, _("Triangle out"));
3934     glist = g_list_append (glist, _("Ellipse"));
3935     glist = g_list_append (glist, _("From clipboard"));
3937     return glist;
3940 static void
3941 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3942     /*advanced shape options */
3943     {
3944         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3945         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3947         GList* items = 0;
3948         gint count = 0;
3949         for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3950         {
3951             GtkTreeIter iter;
3952             gtk_list_store_append( model, &iter );
3953             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3954             count++;
3955         }
3956         g_list_free( items );
3957         items = 0;
3958         EgeSelectOneAction* act1 = ege_select_one_action_new(
3959             tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3960             _("Shape:"), ("Shape"), NULL, GTK_TREE_MODEL(model));
3961         g_object_set( act1, "short_label", _("Shape:"), NULL );
3962         ege_select_one_action_set_appearance( act1, "compact" );
3963         ege_select_one_action_set_active( act1, prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/shape" : "/tools/freehand/pen/shape" ), 0) );
3964         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3965         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3966         g_object_set_data( holder, "shape_action", act1 );
3967     }
3970 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3972     sp_add_freehand_mode_toggle(mainActions, holder, false);
3973     freehand_add_advanced_shape_options(mainActions, holder, false);
3977 static void
3978 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3980     GtkWidget *tbl = GTK_WIDGET(obj);
3982     GtkAdjustment *adj;
3984     // fixme: make settable
3985     gdouble tolerance = 4;
3987     adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3988     gtk_adjustment_set_value(adj, tolerance);
3989     gtk_adjustment_value_changed(adj);
3991     spinbutton_defocus(GTK_OBJECT(tbl));
3994 static void
3995 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3997     // quit if run by the attr_changed listener
3998     if (g_object_get_data( tbl, "freeze" )) {
3999         return;
4000     }
4001     // in turn, prevent listener from responding
4002     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4003     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4004     prefs->setDouble("/tools/freehand/pencil/tolerance", adj->value);
4005     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4008 class PencilToleranceObserver : public Inkscape::Preferences::Observer {
4009 public:
4010     PencilToleranceObserver(Glib::ustring const &path, GObject *x) : Observer(path), _obj(x)
4011     {
4012         g_object_set_data(_obj, "prefobserver", this);
4013     }
4014     virtual ~PencilToleranceObserver() {
4015         if (g_object_get_data(_obj, "prefobserver") == this) {
4016                 g_object_set_data(_obj, "prefobserver", NULL);
4017         }
4018     }
4019     virtual void notify(Inkscape::Preferences::Entry const &val) {
4020         GObject* tbl = _obj;
4021         if (g_object_get_data( tbl, "freeze" )) {
4022             return;
4023         }
4024         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4026         GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl, "tolerance");
4028         double v = val.getDouble(adj->value);
4029         gtk_adjustment_set_value(adj, v);
4030         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4031     }
4032 private:
4033     GObject *_obj;
4034 };
4037 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4039     sp_add_freehand_mode_toggle(mainActions, holder, true);
4041     EgeAdjustmentAction* eact = 0;
4043     /* Tolerance */
4044     {
4045         gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
4046         gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
4047         eact = create_adjustment_action( "PencilToleranceAction",
4048                                          _("Smoothing:"), _("Smoothing: "),
4049                  _("How much smoothing (simplifying) is applied to the line"),
4050                                          "/tools/freehand/pencil/tolerance",
4051                                          3.0,
4052                                          GTK_WIDGET(desktop->canvas), NULL,
4053                                          holder, TRUE, "altx-pencil",
4054                                          1, 100.0, 0.5, 0,
4055                                          labels, values, G_N_ELEMENTS(labels),
4056                                          sp_pencil_tb_tolerance_value_changed,
4057                                          1, 2);
4058         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4059         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4061         PencilToleranceObserver *obs =
4062             new PencilToleranceObserver("/tools/freehand/pencil/tolerance", G_OBJECT(holder));
4063     }
4065     /* advanced shape options */
4066     freehand_add_advanced_shape_options(mainActions, holder, true);
4068     /* Reset */
4069     {
4070         InkAction* inky = ink_action_new( "PencilResetAction",
4071                                           _("Defaults"),
4072                                           _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
4073                                           GTK_STOCK_CLEAR,
4074                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4075         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
4076         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4077     }
4079     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4084 //########################
4085 //##       Tweak        ##
4086 //########################
4088 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4090     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4091     prefs->setDouble( "/tools/tweak/width", adj->value * 0.01 );
4094 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4096     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4097     prefs->setDouble( "/tools/tweak/force", adj->value * 0.01 );
4100 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4102     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4103     prefs->setBool("/tools/tweak/usepressure", gtk_toggle_action_get_active(act));
4106 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4108     int mode = ege_select_one_action_get_active( act );
4109     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4110     prefs->setInt("/tools/tweak/mode", mode);
4112     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
4113     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
4114     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
4115     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
4116     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
4117     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
4118     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
4119         if (doh) gtk_action_set_sensitive (doh, TRUE);
4120         if (dos) gtk_action_set_sensitive (dos, TRUE);
4121         if (dol) gtk_action_set_sensitive (dol, TRUE);
4122         if (doo) gtk_action_set_sensitive (doo, TRUE);
4123         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
4124         if (fid) gtk_action_set_sensitive (fid, FALSE);
4125     } else {
4126         if (doh) gtk_action_set_sensitive (doh, FALSE);
4127         if (dos) gtk_action_set_sensitive (dos, FALSE);
4128         if (dol) gtk_action_set_sensitive (dol, FALSE);
4129         if (doo) gtk_action_set_sensitive (doo, FALSE);
4130         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
4131         if (fid) gtk_action_set_sensitive (fid, TRUE);
4132     }
4135 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4137     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4138     prefs->setDouble( "/tools/tweak/fidelity", adj->value * 0.01 );
4141 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
4142     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4143     prefs->setBool("/tools/tweak/doh", gtk_toggle_action_get_active(act));
4145 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
4146     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4147     prefs->setBool("/tools/tweak/dos", gtk_toggle_action_get_active(act));
4149 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
4150     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4151     prefs->setBool("/tools/tweak/dol", gtk_toggle_action_get_active(act));
4153 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
4154     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4155     prefs->setBool("/tools/tweak/doo", gtk_toggle_action_get_active(act));
4158 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4160     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
4161     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4163     {
4164         /* Width */
4165         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
4166         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4167         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
4168                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
4169                                                               "/tools/tweak/width", 15,
4170                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
4171                                                               1, 100, 1.0, 0.0,
4172                                                               labels, values, G_N_ELEMENTS(labels),
4173                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
4174         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4175         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4176         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4177     }
4180     {
4181         /* Force */
4182         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
4183         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4184         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
4185                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
4186                                                               "/tools/tweak/force", 20,
4187                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
4188                                                               1, 100, 1.0, 0.0,
4189                                                               labels, values, G_N_ELEMENTS(labels),
4190                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
4191         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4192         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4193         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4194     }
4196     /* Mode */
4197     {
4198         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4200         GtkTreeIter iter;
4201         gtk_list_store_append( model, &iter );
4202         gtk_list_store_set( model, &iter,
4203                             0, _("Move mode"),
4204                             1, _("Move objects in any direction"),
4205                             2, "tweak_move_mode",
4206                             -1 );
4208         gtk_list_store_append( model, &iter );
4209         gtk_list_store_set( model, &iter,
4210                             0, _("Move in/out mode"),
4211                             1, _("Move objects towards cursor; with Shift from cursor"),
4212                             2, "tweak_move_mode_inout",
4213                             -1 );
4215         gtk_list_store_append( model, &iter );
4216         gtk_list_store_set( model, &iter,
4217                             0, _("Move jitter mode"),
4218                             1, _("Move objects in random directions"),
4219                             2, "tweak_move_mode_jitter",
4220                             -1 );
4222         gtk_list_store_append( model, &iter );
4223         gtk_list_store_set( model, &iter,
4224                             0, _("Scale mode"),
4225                             1, _("Scale objects, with Shift scale up"),
4226                             2, "tweak_scale_mode",
4227                             -1 );
4229         gtk_list_store_append( model, &iter );
4230         gtk_list_store_set( model, &iter,
4231                             0, _("Rotate mode"),
4232                             1, _("Rotate objects, with Shift counterclockwise"),
4233                             2, "tweak_rotate_mode",
4234                             -1 );
4236         gtk_list_store_append( model, &iter );
4237         gtk_list_store_set( model, &iter,
4238                             0, _("Duplicate/delete mode"),
4239                             1, _("Duplicate objects, with Shift delete"),
4240                             2, "tweak_moreless_mode",
4241                             -1 );
4243         gtk_list_store_append( model, &iter );
4244         gtk_list_store_set( model, &iter,
4245                             0, _("Push mode"),
4246                             1, _("Push parts of paths in any direction"),
4247                             2, "tweak_push_mode",
4248                             -1 );
4250         gtk_list_store_append( model, &iter );
4251         gtk_list_store_set( model, &iter,
4252                             0, _("Shrink/grow mode"),
4253                             1, _("Shrink (inset) parts of paths; with Shift grow (outset)"),
4254                             2, "tweak_shrink_mode",
4255                             -1 );
4257         gtk_list_store_append( model, &iter );
4258         gtk_list_store_set( model, &iter,
4259                             0, _("Attract/repel mode"),
4260                             1, _("Attract parts of paths towards cursor; with Shift from cursor"),
4261                             2, "tweak_attract_mode",
4262                             -1 );
4264         gtk_list_store_append( model, &iter );
4265         gtk_list_store_set( model, &iter,
4266                             0, _("Roughen mode"),
4267                             1, _("Roughen parts of paths"),
4268                             2, "tweak_roughen_mode",
4269                             -1 );
4271         gtk_list_store_append( model, &iter );
4272         gtk_list_store_set( model, &iter,
4273                             0, _("Color paint mode"),
4274                             1, _("Paint the tool's color upon selected objects"),
4275                             2, "tweak_colorpaint_mode",
4276                             -1 );
4278         gtk_list_store_append( model, &iter );
4279         gtk_list_store_set( model, &iter,
4280                             0, _("Color jitter mode"),
4281                             1, _("Jitter the colors of selected objects"),
4282                             2, "tweak_colorjitter_mode",
4283                             -1 );
4285         gtk_list_store_append( model, &iter );
4286         gtk_list_store_set( model, &iter,
4287                             0, _("Blur mode"),
4288                             1, _("Blur selected objects more; with Shift, blur less"),
4289                             2, "tweak_blur_mode",
4290                             -1 );
4293         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4294         g_object_set( act, "short_label", _("Mode:"), NULL );
4295         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4296         g_object_set_data( holder, "mode_action", act );
4298         ege_select_one_action_set_appearance( act, "full" );
4299         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4300         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4301         ege_select_one_action_set_icon_column( act, 2 );
4302         ege_select_one_action_set_icon_size( act, secondarySize );
4303         ege_select_one_action_set_tooltip_column( act, 1  );
4305         gint mode = prefs->getInt("/tools/tweak/mode", 0);
4306         ege_select_one_action_set_active( act, mode );
4307         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
4309         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
4310     }
4312     guint mode = prefs->getInt("/tools/tweak/mode", 0);
4314     {
4315         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
4316         ege_output_action_set_use_markup( act, TRUE );
4317         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4318         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4319             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4320         g_object_set_data( holder, "tweak_channels_label", act);
4321     }
4323     {
4324         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
4325                                                       _("Hue"),
4326                                                       _("In color mode, act on objects' hue"),
4327                                                       NULL,
4328                                                       Inkscape::ICON_SIZE_DECORATION );
4329         //TRANSLATORS:  "H" here stands for hue
4330         g_object_set( act, "short_label", _("H"), NULL );
4331         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4332         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
4333         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doh", true) );
4334         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4335             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4336         g_object_set_data( holder, "tweak_doh", act);
4337     }
4338     {
4339         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
4340                                                       _("Saturation"),
4341                                                       _("In color mode, act on objects' saturation"),
4342                                                       NULL,
4343                                                       Inkscape::ICON_SIZE_DECORATION );
4344         //TRANSLATORS: "S" here stands for Saturation
4345         g_object_set( act, "short_label", _("S"), NULL );
4346         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4347         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
4348         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dos", true) );
4349         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4350             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4351         g_object_set_data( holder, "tweak_dos", act );
4352     }
4353     {
4354         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
4355                                                       _("Lightness"),
4356                                                       _("In color mode, act on objects' lightness"),
4357                                                       NULL,
4358                                                       Inkscape::ICON_SIZE_DECORATION );
4359         //TRANSLATORS: "L" here stands for Lightness
4360         g_object_set( act, "short_label", _("L"), NULL );
4361         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4362         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
4363         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dol", true) );
4364         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4365             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4366         g_object_set_data( holder, "tweak_dol", act );
4367     }
4368     {
4369         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
4370                                                       _("Opacity"),
4371                                                       _("In color mode, act on objects' opacity"),
4372                                                       NULL,
4373                                                       Inkscape::ICON_SIZE_DECORATION );
4374         //TRANSLATORS: "O" here stands for Opacity
4375         g_object_set( act, "short_label", _("O"), NULL );
4376         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4377         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
4378         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doo", true) );
4379         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4380             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4381         g_object_set_data( holder, "tweak_doo", act );
4382     }
4384     {   /* Fidelity */
4385         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
4386         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4387         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
4388                                                               _("Fidelity"), _("Fidelity:"),
4389                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
4390                                                               "/tools/tweak/fidelity", 50,
4391                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
4392                                                               1, 100, 1.0, 10.0,
4393                                                               labels, values, G_N_ELEMENTS(labels),
4394                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
4395         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4396         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4397         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
4398             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
4399         g_object_set_data( holder, "tweak_fidelity", eact );
4400     }
4403     /* Use Pressure button */
4404     {
4405         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
4406                                                       _("Pressure"),
4407                                                       _("Use the pressure of the input device to alter the force of tweak action"),
4408                                                       "use_pressure",
4409                                                       Inkscape::ICON_SIZE_DECORATION );
4410         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4411         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
4412         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/usepressure", true) );
4413     }
4418 //########################
4419 //##     Calligraphy    ##
4420 //########################
4421 static void update_presets_list (GObject *tbl)
4423     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4424     if (g_object_get_data(tbl, "presets_blocked"))
4425         return;
4427     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4428     if (!sel) {
4429         // WTF!? This will cause a segfault if ever reached
4430         //ege_select_one_action_set_active(sel, 0);
4431         return;
4432     }
4434     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4436     int ege_index = 1;
4437     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++ege_index) {
4438         bool match = true;
4440         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(*i);
4441         for (std::vector<Inkscape::Preferences::Entry>::iterator j = preset.begin(); j != preset.end(); ++j) {
4442             Glib::ustring entry_name = j->getEntryName();
4443             if (entry_name == "id" || entry_name == "name") continue;
4445             void *widget = g_object_get_data(tbl, entry_name.data());
4446             if (widget) {
4447                 if (GTK_IS_ADJUSTMENT(widget)) {
4448                     double v = j->getDouble();
4449                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4450                     //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
4451                     if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
4452                         match = false;
4453                         break;
4454                     }
4455                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4456                     bool v = j->getBool();
4457                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4458                     //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
4459                     if ( static_cast<bool>(gtk_toggle_action_get_active(toggle)) != v ) {
4460                         match = false;
4461                         break;
4462                     }
4463                 }
4464             }
4465         }
4467         if (match) {
4468             // newly added item is at the same index as the
4469             // save command, so we need to change twice for it to take effect
4470             ege_select_one_action_set_active(sel, 0);
4471             ege_select_one_action_set_active(sel, ege_index); // one-based index
4472             return;
4473         }
4474     }
4476     // no match found
4477     ege_select_one_action_set_active(sel, 0);
4480 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
4482     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4483     prefs->setDouble( "/tools/calligraphic/mass", adj->value * 0.01 );
4484     update_presets_list(tbl);
4487 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
4489     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4490     prefs->setDouble( "/tools/calligraphic/wiggle", adj->value * 0.01 );
4491     update_presets_list(tbl);
4494 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
4496     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4497     prefs->setDouble( "/tools/calligraphic/angle", adj->value );
4498     update_presets_list(tbl);
4501 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
4503     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4504     prefs->setDouble( "/tools/calligraphic/width", adj->value * 0.01 );
4505     update_presets_list(tbl);
4508 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
4510     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4511     prefs->setDouble("/tools/calligraphic/thinning", adj->value * 0.01 );
4512     update_presets_list(tbl);
4515 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
4517     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4518     prefs->setDouble( "/tools/calligraphic/flatness", adj->value * 0.01);
4519     update_presets_list(tbl);
4522 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
4524     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4525     prefs->setDouble( "/tools/calligraphic/tremor", adj->value * 0.01 );
4526     update_presets_list(tbl);
4529 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
4531     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4532     prefs->setDouble( "/tools/calligraphic/cap_rounding", adj->value );
4533     update_presets_list(tbl);
4536 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
4538     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4539     prefs->setBool("/tools/calligraphic/usepressure", gtk_toggle_action_get_active( act ));
4540     update_presets_list(tbl);
4543 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
4545     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4546     prefs->setBool("/tools/calligraphic/tracebackground", gtk_toggle_action_get_active( act ));
4547     update_presets_list(tbl);
4550 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
4552     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4553     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
4554     prefs->setBool("/tools/calligraphic/usetilt", gtk_toggle_action_get_active( act ));
4555     update_presets_list(tbl);
4556     if (calligraphy_angle )
4557         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
4561 static gchar const *const widget_names[] = {
4562     "width",
4563     "mass",
4564     "wiggle",
4565     "angle",
4566     "thinning",
4567     "tremor",
4568     "flatness",
4569     "cap_rounding",
4570     "usepressure",
4571     "tracebackground",
4572     "usetilt"
4573 };
4576 static void sp_dcc_build_presets_list(GObject *tbl)
4578     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4580     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4581     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4582     gtk_list_store_clear (model);
4584     {
4585         GtkTreeIter iter;
4586         gtk_list_store_append( model, &iter );
4587         gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4588     }
4590     // iterate over all presets to populate the list
4591     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4592     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4593     int ii=1;
4595     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i) {
4596         GtkTreeIter iter;
4597         Glib::ustring preset_name = prefs->getString(*i + "/name");
4598         gtk_list_store_append( model, &iter );
4599         gtk_list_store_set( model, &iter, 0, preset_name.data(), 1, ii++, -1 );
4600     }
4602     {
4603         GtkTreeIter iter;
4604         gtk_list_store_append( model, &iter );
4605         gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4606         g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4607     }
4609     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4611     update_presets_list (tbl);
4614 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4616     using Inkscape::UI::Dialog::CalligraphicProfileRename;
4617     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4618     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4619     if (! desktop) return;
4621     if (g_object_get_data(tbl, "presets_blocked"))
4622         return;
4624     CalligraphicProfileRename::show(desktop);
4625     if ( !CalligraphicProfileRename::applied()) {
4626         // dialog cancelled
4627         update_presets_list (tbl);
4628         return;
4629     }
4630     Glib::ustring profile_name = CalligraphicProfileRename::getProfileName();
4632     if (profile_name.empty()) {
4633         // empty name entered
4634         update_presets_list (tbl);
4635         return;
4636     }
4638     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4640     // If there's a preset with the given name, find it and set save_path appropriately
4641     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4642     int total_presets = presets.size();
4643     int new_index = -1;
4644     Glib::ustring save_path; // profile pref path without a trailing slash
4646     int temp_index = 0;
4647     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++temp_index) {
4648         Glib::ustring name = prefs->getString(*i + "/name");
4649         if (!name.empty() && profile_name == name) {
4650             new_index = temp_index;
4651             save_path = *i;
4652             break;
4653         }
4654     }
4656     if (new_index == -1) {
4657         // no preset with this name, create
4658         new_index = total_presets + 1;
4659         gchar *profile_id = g_strdup_printf("/dcc%d", new_index);
4660         save_path = Glib::ustring("/tools/calligraphic/preset") + profile_id;
4661         g_free(profile_id);
4662     }
4664     for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4665         gchar const *const widget_name = widget_names[i];
4666         void *widget = g_object_get_data(tbl, widget_name);
4667         if (widget) {
4668             if (GTK_IS_ADJUSTMENT(widget)) {
4669                 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4670                 prefs->setDouble(save_path + "/" + widget_name, gtk_adjustment_get_value(adj));
4671                 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4672             } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4673                 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4674                 prefs->setBool(save_path + "/" + widget_name, gtk_toggle_action_get_active(toggle));
4675                 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4676             } else {
4677                 g_warning("Unknown widget type for preset: %s\n", widget_name);
4678             }
4679         } else {
4680             g_warning("Bad key when writing preset: %s\n", widget_name);
4681         }
4682     }
4683     prefs->setString(save_path + "/name", profile_name);
4685     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4686     sp_dcc_build_presets_list (tbl);
4690 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4692     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4694     gint preset_index = ege_select_one_action_get_active( act );
4695     // This is necessary because EgeSelectOneAction spams us with GObject "changed" signal calls
4696     // even when the preset is not changed. It would be good to replace it with something more
4697     // modern. Index 0 means "No preset", so we don't do anything.
4698     if (preset_index == 0) return;
4700     gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4702     if (preset_index == save_presets_index) {
4703         // this is the Save command
4704         sp_dcc_save_profile(NULL, tbl);
4705         return;
4706     }
4708     if (g_object_get_data(tbl, "presets_blocked"))
4709         return;
4711     // preset_index is one-based so we subtract 1
4712     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4713     Glib::ustring preset_path = presets.at(preset_index - 1);
4715     if (!preset_path.empty()) {
4716         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
4718         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(preset_path);
4720         // Shouldn't this be std::map?
4721         for (std::vector<Inkscape::Preferences::Entry>::iterator i = preset.begin(); i != preset.end(); ++i) {
4722             Glib::ustring entry_name = i->getEntryName();
4723             if (entry_name == "id" || entry_name == "name") continue;
4724             void *widget = g_object_get_data(tbl, entry_name.data());
4725             if (widget) {
4726                 if (GTK_IS_ADJUSTMENT(widget)) {
4727                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4728                     gtk_adjustment_set_value(adj, i->getDouble());
4729                     //std::cout << "set adj " << attr_name << " to " << v << "\n";
4730                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4731                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4732                     gtk_toggle_action_set_active(toggle, i->getBool());
4733                     //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4734                 } else {
4735                     g_warning("Unknown widget type for preset: %s\n", entry_name.data());
4736                 }
4737             } else {
4738                 g_warning("Bad key found in a preset record: %s\n", entry_name.data());
4739             }
4740         }
4741         g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4742     }
4746 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4748     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4749     {
4750         g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4752         EgeAdjustmentAction* calligraphy_angle = 0;
4754         {
4755         /* Width */
4756         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4757         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4758         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4759                                                               _("Pen Width"), _("Width:"),
4760                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
4761                                                               "/tools/calligraphic/width", 15,
4762                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4763                                                               1, 100, 1.0, 0.0,
4764                                                               labels, values, G_N_ELEMENTS(labels),
4765                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
4766         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4767         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4768         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4769         }
4771         {
4772         /* Thinning */
4773             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4774             gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4775         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4776                                                               _("Stroke Thinning"), _("Thinning:"),
4777                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4778                                                               "/tools/calligraphic/thinning", 10,
4779                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4780                                                               -100, 100, 1, 0.1,
4781                                                               labels, values, G_N_ELEMENTS(labels),
4782                                                               sp_ddc_velthin_value_changed, 0.01, 0, 100);
4783         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4784         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4785         }
4787         {
4788         /* Angle */
4789         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4790         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4791         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4792                                                               _("Pen Angle"), _("Angle:"),
4793                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4794                                                               "/tools/calligraphic/angle", 30,
4795                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4796                                                               -90.0, 90.0, 1.0, 10.0,
4797                                                               labels, values, G_N_ELEMENTS(labels),
4798                                                               sp_ddc_angle_value_changed, 1, 0 );
4799         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4800         g_object_set_data( holder, "angle_action", eact );
4801         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4802         calligraphy_angle = eact;
4803         }
4805         {
4806         /* Fixation */
4807             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4808         gdouble values[] = {0, 20, 40, 60, 90, 100};
4809         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4810                                                               _("Fixation"), _("Fixation:"),
4811                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"),
4812                                                               "/tools/calligraphic/flatness", 90,
4813                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4814                                                               0.0, 100, 1.0, 10.0,
4815                                                               labels, values, G_N_ELEMENTS(labels),
4816                                                               sp_ddc_flatness_value_changed, 0.01, 0, 100 );
4817         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4818         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4819         }
4821         {
4822         /* Cap Rounding */
4823             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4824         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4825         // TRANSLATORS: "cap" means "end" (both start and finish) here
4826         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4827                                                               _("Cap rounding"), _("Caps:"),
4828                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4829                                                               "/tools/calligraphic/cap_rounding", 0.0,
4830                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4831                                                               0.0, 5.0, 0.01, 0.1,
4832                                                               labels, values, G_N_ELEMENTS(labels),
4833                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4834         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4835         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4836         }
4838         {
4839         /* Tremor */
4840             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4841         gdouble values[] = {0, 10, 20, 40, 60, 100};
4842         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4843                                                               _("Stroke Tremor"), _("Tremor:"),
4844                                                               _("Increase to make strokes rugged and trembling"),
4845                                                               "/tools/calligraphic/tremor", 0.0,
4846                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4847                                                               0.0, 100, 1, 0.0,
4848                                                               labels, values, G_N_ELEMENTS(labels),
4849                                                               sp_ddc_tremor_value_changed, 0.01, 0, 100 );
4851         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4852         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4853         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4854         }
4856         {
4857         /* Wiggle */
4858         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4859         gdouble values[] = {0, 20, 40, 60, 100};
4860         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4861                                                               _("Pen Wiggle"), _("Wiggle:"),
4862                                                               _("Increase to make the pen waver and wiggle"),
4863                                                               "/tools/calligraphic/wiggle", 0.0,
4864                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4865                                                               0.0, 100, 1, 0.0,
4866                                                               labels, values, G_N_ELEMENTS(labels),
4867                                                               sp_ddc_wiggle_value_changed, 0.01, 0, 100 );
4868         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4869         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4870         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4871         }
4873         {
4874         /* Mass */
4875             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4876         gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4877         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4878                                                               _("Pen Mass"), _("Mass:"),
4879                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
4880                                                               "/tools/calligraphic/mass", 2.0,
4881                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4882                                                               0.0, 100, 1, 0.0,
4883                                                               labels, values, G_N_ELEMENTS(labels),
4884                                                               sp_ddc_mass_value_changed, 0.01, 0, 100 );
4885         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4886         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4887         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4888         }
4891         /* Trace Background button */
4892         {
4893             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4894                                                           _("Trace Background"),
4895                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4896                                                           "trace_background",
4897                                                           Inkscape::ICON_SIZE_DECORATION );
4898             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4899             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4900             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/tracebackground", false) );
4901             g_object_set_data( holder, "tracebackground", act );
4902         }
4904         /* Use Pressure button */
4905         {
4906             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4907                                                           _("Pressure"),
4908                                                           _("Use the pressure of the input device to alter the width of the pen"),
4909                                                           "use_pressure",
4910                                                           Inkscape::ICON_SIZE_DECORATION );
4911             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4912             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4913             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usepressure", true) );
4914             g_object_set_data( holder, "usepressure", act );
4915         }
4917         /* Use Tilt button */
4918         {
4919             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4920                                                           _("Tilt"),
4921                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
4922                                                           "use_tilt",
4923                                                           Inkscape::ICON_SIZE_DECORATION );
4924             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4925             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4926             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs->getBool("/tools/calligraphic/usetilt", true) );
4927             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usetilt", true) );
4928             g_object_set_data( holder, "usetilt", act );
4929         }
4931         /*calligraphic profile */
4932         {
4933             GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
4934             EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
4935             ege_select_one_action_set_appearance (act1, "compact");
4936             g_object_set_data (holder, "profile_selector", act1 );
4938             g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
4940             sp_dcc_build_presets_list (holder);
4942             g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
4943             gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
4944         }
4945     }
4949 //########################
4950 //##    Circle / Arc    ##
4951 //########################
4953 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4955     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4956     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4958     if (v1 == 0 && v2 == 0) {
4959         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4960             gtk_action_set_sensitive( ocb, FALSE );
4961             gtk_action_set_sensitive( make_whole, FALSE );
4962         }
4963     } else {
4964         gtk_action_set_sensitive( ocb, TRUE );
4965         gtk_action_set_sensitive( make_whole, TRUE );
4966     }
4969 static void
4970 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4972     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4974     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4975         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4976         prefs->setDouble(Glib::ustring("/tools/shapes/arc") + value_name, (adj->value * M_PI)/ 180);
4977     }
4979     // quit if run by the attr_changed listener
4980     if (g_object_get_data( tbl, "freeze" )) {
4981         return;
4982     }
4984     // in turn, prevent listener from responding
4985     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4987     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4989     bool modmade = false;
4990     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4991          items != NULL;
4992          items = items->next)
4993     {
4994         SPItem *item = SP_ITEM(items->data);
4996         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4998             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4999             SPArc *arc = SP_ARC(item);
5001             if (!strcmp(value_name, "start"))
5002                 ge->start = (adj->value * M_PI)/ 180;
5003             else
5004                 ge->end = (adj->value * M_PI)/ 180;
5006             sp_genericellipse_normalize(ge);
5007             ((SPObject *)arc)->updateRepr();
5008             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
5010             modmade = true;
5011         }
5012     }
5014     g_free(namespaced_name);
5016     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
5018     sp_arctb_sensitivize( tbl, adj->value, other->value );
5020     if (modmade) {
5021         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
5022                                    _("Arc: Change start/end"));
5023     }
5025     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5029 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
5031     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
5034 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
5036     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
5040 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
5042     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5043     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5044         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5045         prefs->setBool("/tools/shapes/arc/open", ege_select_one_action_get_active(act) != 0);
5046     }
5048     // quit if run by the attr_changed listener
5049     if (g_object_get_data( tbl, "freeze" )) {
5050         return;
5051     }
5053     // in turn, prevent listener from responding
5054     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5056     bool modmade = false;
5058     if ( ege_select_one_action_get_active(act) != 0 ) {
5059         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5060              items != NULL;
5061              items = items->next)
5062         {
5063             if (SP_IS_ARC((SPItem *) items->data)) {
5064                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5065                 repr->setAttribute("sodipodi:open", "true");
5066                 SP_OBJECT((SPItem *) items->data)->updateRepr();
5067                 modmade = true;
5068             }
5069         }
5070     } else {
5071         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5072              items != NULL;
5073              items = items->next)
5074         {
5075             if (SP_IS_ARC((SPItem *) items->data))    {
5076                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5077                 repr->setAttribute("sodipodi:open", NULL);
5078                 SP_OBJECT((SPItem *) items->data)->updateRepr();
5079                 modmade = true;
5080             }
5081         }
5082     }
5084     if (modmade) {
5085         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
5086                                    _("Arc: Change open/closed"));
5087     }
5089     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5092 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
5094     GtkAdjustment *adj;
5095     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
5096     gtk_adjustment_set_value(adj, 0.0);
5097     gtk_adjustment_value_changed(adj);
5099     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
5100     gtk_adjustment_set_value(adj, 0.0);
5101     gtk_adjustment_value_changed(adj);
5103     spinbutton_defocus( GTK_OBJECT(obj) );
5106 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
5107                                       gchar const */*old_value*/, gchar const */*new_value*/,
5108                                       bool /*is_interactive*/, gpointer data)
5110     GObject *tbl = G_OBJECT(data);
5112     // quit if run by the _changed callbacks
5113     if (g_object_get_data( tbl, "freeze" )) {
5114         return;
5115     }
5117     // in turn, prevent callbacks from responding
5118     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5120     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
5121     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
5123     GtkAdjustment *adj1,*adj2;
5124     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
5125     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
5126     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
5127     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
5129     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
5131     char const *openstr = NULL;
5132     openstr = repr->attribute("sodipodi:open");
5133     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
5135     if (openstr) {
5136         ege_select_one_action_set_active( ocb, 1 );
5137     } else {
5138         ege_select_one_action_set_active( ocb, 0 );
5139     }
5141     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5144 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
5145     NULL, /* child_added */
5146     NULL, /* child_removed */
5147     arc_tb_event_attr_changed,
5148     NULL, /* content_changed */
5149     NULL  /* order_changed */
5150 };
5153 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
5155     int n_selected = 0;
5156     Inkscape::XML::Node *repr = NULL;
5158     purge_repr_listener( tbl, tbl );
5160     for (GSList const *items = selection->itemList();
5161          items != NULL;
5162          items = items->next)
5163     {
5164         if (SP_IS_ARC((SPItem *) items->data)) {
5165             n_selected++;
5166             repr = SP_OBJECT_REPR((SPItem *) items->data);
5167         }
5168     }
5170     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
5172     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
5173     if (n_selected == 0) {
5174         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
5175     } else if (n_selected == 1) {
5176         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
5177         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5179         if (repr) {
5180             g_object_set_data( tbl, "repr", repr );
5181             Inkscape::GC::anchor(repr);
5182             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
5183             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
5184         }
5185     } else {
5186         // FIXME: implement averaging of all parameters for multiple selected
5187         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
5188         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5189         sp_arctb_sensitivize( tbl, 1, 0 );
5190     }
5194 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5196     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5198     EgeAdjustmentAction* eact = 0;
5199     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
5202     {
5203         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
5204         ege_output_action_set_use_markup( act, TRUE );
5205         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5206         g_object_set_data( holder, "mode_action", act );
5207     }
5209     /* Start */
5210     {
5211         eact = create_adjustment_action( "ArcStartAction",
5212                                          _("Start"), _("Start:"),
5213                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
5214                                          "/tools/shapes/arc/start", 0.0,
5215                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
5216                                          -360.0, 360.0, 1.0, 10.0,
5217                                          0, 0, 0,
5218                                          sp_arctb_start_value_changed);
5219         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5220     }
5222     /* End */
5223     {
5224         eact = create_adjustment_action( "ArcEndAction",
5225                                          _("End"), _("End:"),
5226                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
5227                                          "/tools/shapes/arc/end", 0.0,
5228                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
5229                                          -360.0, 360.0, 1.0, 10.0,
5230                                          0, 0, 0,
5231                                          sp_arctb_end_value_changed);
5232         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5233     }
5235     /* Segments / Pie checkbox */
5236     {
5237         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5239         GtkTreeIter iter;
5240         gtk_list_store_append( model, &iter );
5241         gtk_list_store_set( model, &iter,
5242                             0, _("Closed arc"),
5243                             1, _("Switch to segment (closed shape with two radii)"),
5244                             2, "circle_closed_arc",
5245                             -1 );
5247         gtk_list_store_append( model, &iter );
5248         gtk_list_store_set( model, &iter,
5249                             0, _("Open Arc"),
5250                             1, _("Switch to arc (unclosed shape)"),
5251                             2, "circle_open_arc",
5252                             -1 );
5254         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5255         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5256         g_object_set_data( holder, "open_action", act );
5258         ege_select_one_action_set_appearance( act, "full" );
5259         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5260         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5261         ege_select_one_action_set_icon_column( act, 2 );
5262         ege_select_one_action_set_icon_size( act, secondarySize );
5263         ege_select_one_action_set_tooltip_column( act, 1  );
5265         bool isClosed = !prefs->getBool("/tools/shapes/arc/open", false);
5266         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
5267         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
5268     }
5270     /* Make Whole */
5271     {
5272         InkAction* inky = ink_action_new( "ArcResetAction",
5273                                           _("Make whole"),
5274                                           _("Make the shape a whole ellipse, not arc or segment"),
5275                                           "reset_circle",
5276                                           secondarySize );
5277         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
5278         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5279         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
5280         g_object_set_data( holder, "make_whole", inky );
5281     }
5283     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
5284     // sensitivize make whole and open checkbox
5285     {
5286         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
5287         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
5288         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
5289     }
5292     sigc::connection *connection = new sigc::connection(
5293         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
5294         );
5295     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
5296     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
5302 // toggle button callbacks and updaters
5304 //########################
5305 //##      Dropper       ##
5306 //########################
5308 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
5309     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5310     prefs->setInt( "/tools/dropper/pick", gtk_toggle_action_get_active( act ) );
5311     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
5312     if ( set_action ) {
5313         if ( gtk_toggle_action_get_active( act ) ) {
5314             gtk_action_set_sensitive( set_action, TRUE );
5315         } else {
5316             gtk_action_set_sensitive( set_action, FALSE );
5317         }
5318     }
5320     spinbutton_defocus(GTK_OBJECT(tbl));
5323 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
5324     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5325     prefs->setBool( "/tools/dropper/setalpha", gtk_toggle_action_get_active( act ) );
5326     spinbutton_defocus(GTK_OBJECT(tbl));
5330 /**
5331  * Dropper auxiliary toolbar construction and setup.
5332  *
5333  * TODO: Would like to add swatch of current color.
5334  * TODO: Add queue of last 5 or so colors selected with new swatches so that
5335  *       can drag and drop places. Will provide a nice mixing palette.
5336  */
5337 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
5339     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5340     gint pickAlpha = prefs->getInt( "/tools/dropper/pick", 1 );
5342     {
5343         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
5344         ege_output_action_set_use_markup( act, TRUE );
5345         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5346     }
5348     {
5349         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
5350                                                       _("Pick opacity"),
5351                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
5352                                                       NULL,
5353                                                       Inkscape::ICON_SIZE_DECORATION );
5354         g_object_set( act, "short_label", _("Pick"), NULL );
5355         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5356         g_object_set_data( holder, "pick_action", act );
5357         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
5358         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
5359     }
5361     {
5362         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
5363                                                       _("Assign opacity"),
5364                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
5365                                                       NULL,
5366                                                       Inkscape::ICON_SIZE_DECORATION );
5367         g_object_set( act, "short_label", _("Assign"), NULL );
5368         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5369         g_object_set_data( holder, "set_action", act );
5370         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/dropper/setalpha", true) );
5371         // make sure it's disabled if we're not picking alpha
5372         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
5373         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
5374     }
5378 //########################
5379 //##      LPETool       ##
5380 //########################
5382 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
5384 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
5385 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
5387     using namespace Inkscape::LivePathEffect;
5389     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
5390     SPEventContext *ec = desktop->event_context;
5391     if (!SP_IS_LPETOOL_CONTEXT(ec)) {
5392         return;
5393     }
5395     // only take action if run by the attr_changed listener
5396     if (!g_object_get_data(tbl, "freeze")) {
5397         // in turn, prevent listener from responding
5398         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5400         gint mode = ege_select_one_action_get_active(act);
5401         EffectType type = lpesubtools[mode];
5403         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5404         bool success = lpetool_try_construction(lc, type);
5405         if (success) {
5406             // since the construction was already performed, we set the state back to inactive
5407             ege_select_one_action_set_active(act, 0);
5408             mode = 0;
5409         } else {
5410             // switch to the chosen subtool
5411             SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
5412         }
5414         if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5415             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5416             prefs->setInt( "/tools/lpetool/mode", mode );
5417         }
5419         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
5420     }
5423 void sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject */*tbl*/)
5425     SPEventContext *ec = selection->desktop()->event_context;
5426     if (!SP_IS_LPETOOL_CONTEXT(ec))
5427         return;
5429     lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
5432 void
5433 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
5435     using namespace Inkscape::LivePathEffect;
5436     SPEventContext *ec = selection->desktop()->event_context;
5437     if (!SP_IS_LPETOOL_CONTEXT(ec))
5438         return;
5439     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
5441     lpetool_delete_measuring_items(lc);
5442     lpetool_create_measuring_items(lc, selection);
5444     // activate line segment combo box if a single item with LPELineSegment is selected
5445     GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
5446     SPItem *item = selection->singleItem();
5447     if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
5448         SPLPEItem *lpeitem = SP_LPE_ITEM(item);
5449         Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
5450         if (lpe && lpe->effectType() == LINE_SEGMENT) {
5451             LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
5452             g_object_set_data(tbl, "currentlpe", lpe);
5453             g_object_set_data(tbl, "currentlpeitem", lpeitem);
5454             gtk_action_set_sensitive(w, TRUE);
5455             ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
5456         } else {
5457             g_object_set_data(tbl, "currentlpe", NULL);
5458             g_object_set_data(tbl, "currentlpeitem", NULL);
5459             gtk_action_set_sensitive(w, FALSE);
5460         }
5461     } else {
5462         g_object_set_data(tbl, "currentlpe", NULL);
5463         g_object_set_data(tbl, "currentlpeitem", NULL);
5464         gtk_action_set_sensitive(w, FALSE);
5465     }
5468 static void
5469 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
5470     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5471     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5473     bool show = gtk_toggle_action_get_active( act );
5474     prefs->setBool("/tools/lpetool/show_bbox",  show);
5476     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5477         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5478         lpetool_context_reset_limiting_bbox(lc);
5479     }
5482 static void
5483 lpetool_toggle_show_measuring_info (GtkToggleAction *act, GObject *tbl) {
5484     SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
5485     if (!tools_isactive(desktop, TOOLS_LPETOOL))
5486         return;
5488     GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
5489     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5490     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5491         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5492         bool show = gtk_toggle_action_get_active( act );
5493         prefs->setBool("/tools/lpetool/show_measuring_info",  show);
5494         lpetool_show_measuring_info(lc, show);
5495         gtk_action_set_sensitive(GTK_ACTION(unitact), show);
5496     }
5499 static void lpetool_unit_changed(GtkAction* /*act*/, GObject* tbl) {
5500     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
5501     SPUnit const *unit = tracker->getActiveUnit();
5502     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5503     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5505     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5506     if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
5507         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5508         lpetool_delete_measuring_items(lc);
5509         lpetool_create_measuring_items(lc);
5510     }
5513 static void
5514 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
5515     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5516     Inkscape::Selection *selection = desktop->selection;
5518     Geom::OptRect bbox = selection->bounds();
5520     if (bbox) {
5521         Geom::Point A(bbox->min());
5522         Geom::Point B(bbox->max());
5524         A *= desktop->doc2dt();
5525         B *= desktop->doc2dt();
5527         // TODO: should we provide a way to store points in prefs?
5528         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5529         prefs->setDouble("/tools/lpetool/bbox_upperleftx", A[Geom::X]);
5530         prefs->setDouble("/tools/lpetool/bbox_upperlefty", A[Geom::Y]);
5531         prefs->setDouble("/tools/lpetool/bbox_lowerrightx", B[Geom::X]);
5532         prefs->setDouble("/tools/lpetool/bbox_lowerrighty", B[Geom::Y]);
5534         lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
5535     }
5537     gtk_toggle_action_set_active(act, false);
5540 static void
5541 sp_line_segment_build_list(GObject *tbl)
5543     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
5545     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
5546     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
5547     gtk_list_store_clear (model);
5549     // TODO: we add the entries of rht combo box manually; later this should be done automatically
5550     {
5551         GtkTreeIter iter;
5552         gtk_list_store_append( model, &iter );
5553         gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
5554         gtk_list_store_append( model, &iter );
5555         gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
5556         gtk_list_store_append( model, &iter );
5557         gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
5558         gtk_list_store_append( model, &iter );
5559         gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
5560     }
5562     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5565 static void
5566 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
5567     using namespace Inkscape::LivePathEffect;
5569     // quit if run by the attr_changed listener
5570     if (g_object_get_data(tbl, "freeze")) {
5571         return;
5572     }
5574     // in turn, prevent listener from responding
5575     g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5577     LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
5578     SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5579     if (lpeitem) {
5580         SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5581         lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
5582         sp_lpe_item_update_patheffect(lpeitem, true, true);
5583     }
5585     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5588 static void
5589 lpetool_open_lpe_dialog (GtkToggleAction *act, gpointer data) {
5590     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5592     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5593         sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5594     }
5595     gtk_toggle_action_set_active(act, false);
5598 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5600     UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5601     tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5602     g_object_set_data(holder, "tracker", tracker);
5603     SPUnit const *unit = tracker->getActiveUnit();
5605     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5606     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5608     /** Automatically create a list of LPEs that get added to the toolbar **/
5609     {
5610         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5612         GtkTreeIter iter;
5614         // the first toggle button represents the state that no subtool is active (remove this when
5615         // this can be modeled by EgeSelectOneAction or some other action)
5616         gtk_list_store_append( model, &iter );
5617         gtk_list_store_set( model, &iter,
5618                             0, _("All inactive"),
5619                             1, _("No geometric tool is active"),
5620                             2, _("all_inactive"),
5621                             -1 );
5623         Inkscape::LivePathEffect::EffectType type;
5624         for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5625             type =  lpesubtools[i];
5626             gtk_list_store_append( model, &iter );
5627             gtk_list_store_set( model, &iter,
5628                                 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5629                                 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5630                                 2, Inkscape::LivePathEffect::LPETypeConverter.get_key(type).c_str(),
5631                                 -1 );
5632         }
5634         EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5635         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5636         g_object_set_data( holder, "lpetool_mode_action", act );
5638         ege_select_one_action_set_appearance( act, "full" );
5639         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5640         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5641         ege_select_one_action_set_icon_column( act, 2 );
5642         ege_select_one_action_set_tooltip_column( act, 1  );
5644         gint lpeToolMode = prefs->getInt("/tools/lpetool/mode", 0);
5645         ege_select_one_action_set_active( act, lpeToolMode );
5646         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5647     }
5649     /* Show limiting bounding box */
5650     {
5651         InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5652                                                       _("Show limiting bounding box"),
5653                                                       _("Show bounding box (used to cut infinite lines)"),
5654                                                       "lpetool_show_bbox",
5655                                                       Inkscape::ICON_SIZE_DECORATION );
5656         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5657         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5658         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_bbox", true ) );
5659     }
5661     /* Set limiting bounding box to bbox of current selection */
5662     {
5663         InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5664                                                       _("Get limiting bounding box from selection"),
5665                                                       _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5666                                                       "lpetool_set_bbox",
5667                                                       Inkscape::ICON_SIZE_DECORATION );
5668         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5669         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5670         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5671     }
5674     /* Combo box to choose line segment type */
5675     {
5676         GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5677         EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5678         ege_select_one_action_set_appearance (act, "compact");
5679         g_object_set_data (holder, "lpetool_line_segment_action", act );
5681         g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5683         sp_line_segment_build_list (holder);
5685         g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5686         gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5687         gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5688     }
5690     /* Display measuring info for selected items */
5691     {
5692         InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5693                                                       _("Display measuring info"),
5694                                                       _("Display measuring info for selected items"),
5695                                                       "lpetool_measuring_info",
5696                                                       Inkscape::ICON_SIZE_DECORATION );
5697         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5698         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
5699         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_measuring_info", true ) );
5700     }
5702     // add the units menu
5703     {
5704         GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5705         gtk_action_group_add_action( mainActions, act );
5706         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5707         g_object_set_data(holder, "lpetool_units_action", act);
5708         gtk_action_set_sensitive(act, prefs->getBool("/tools/lpetool/show_measuring_info", true));
5709     }
5711     /* Open LPE dialog (to adapt parameters numerically) */
5712     {
5713         InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
5714                                                       _("Open LPE dialog"),
5715                                                       _("Open LPE dialog (to adapt parameters numerically)"),
5716                                                       "lpetool_open_lpe_dialog",
5717                                                       Inkscape::ICON_SIZE_DECORATION );
5718         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5719         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
5720         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5721     }
5723     //watch selection
5724     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5726     sigc::connection *c_selection_modified =
5727         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5728                               (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
5729     pool->add_connection ("selection-modified", c_selection_modified);
5731     sigc::connection *c_selection_changed =
5732         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5733                               (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5734     pool->add_connection ("selection-changed", c_selection_changed);
5737 //########################
5738 //##       Eraser       ##
5739 //########################
5741 static void sp_erc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
5743     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5744     prefs->setDouble( "/tools/eraser/width", adj->value * 0.01 );
5745     update_presets_list(tbl);
5748 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5750     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5751     bool eraserMode = ege_select_one_action_get_active( act ) != 0;
5752     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5753         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5754         prefs->setBool( "/tools/eraser/mode", eraserMode );
5755     }
5757     // only take action if run by the attr_changed listener
5758     if (!g_object_get_data( tbl, "freeze" )) {
5759         // in turn, prevent listener from responding
5760         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5762         if ( eraserMode != 0 ) {
5763         } else {
5764         }
5765         // TODO finish implementation
5767         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5768     }
5771 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5773     {
5774         /* Width */
5775         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5776         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5777         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5778                                                               _("Pen Width"), _("Width:"),
5779                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
5780                                                               "/tools/eraser/width", 15,
5781                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5782                                                               1, 100, 1.0, 0.0,
5783                                                               labels, values, G_N_ELEMENTS(labels),
5784                                                               sp_erc_width_value_changed,  0.01, 0, 100 );
5785         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5786         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5787         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5788     }
5790     {
5791         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5793         GtkTreeIter iter;
5794         gtk_list_store_append( model, &iter );
5795         gtk_list_store_set( model, &iter,
5796                             0, _("Delete"),
5797                             1, _("Delete objects touched by the eraser"),
5798                             2, "delete_object",
5799                             -1 );
5801         gtk_list_store_append( model, &iter );
5802         gtk_list_store_set( model, &iter,
5803                             0, _("Cut"),
5804                             1, _("Cut out from objects"),
5805                             2, "difference",
5806                             -1 );
5808         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5809         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5810         g_object_set_data( holder, "eraser_mode_action", act );
5812         ege_select_one_action_set_appearance( act, "full" );
5813         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5814         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5815         ege_select_one_action_set_icon_column( act, 2 );
5816         ege_select_one_action_set_tooltip_column( act, 1  );
5818         /// @todo Convert to boolean?
5819         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5820         gint eraserMode = prefs->getBool("/tools/eraser/mode") ? 1 : 0;
5821         ege_select_one_action_set_active( act, eraserMode );
5822         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
5823     }
5827 //########################
5828 //##    Text Toolbox    ##
5829 //########################
5830 /*
5831 static void
5832 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
5834     //Call back for letter sizing spinbutton
5837 static void
5838 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
5840     //Call back for line height spinbutton
5843 static void
5844 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5846     //Call back for horizontal kerning spinbutton
5849 static void
5850 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5852     //Call back for vertical kerning spinbutton
5855 static void
5856 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
5858     //Call back for letter rotation spinbutton
5859 }*/
5861 namespace {
5863 bool popdown_visible = false;
5864 bool popdown_hasfocus = false;
5866 void
5867 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
5869     SPStyle *query =
5870         sp_style_new (SP_ACTIVE_DOCUMENT);
5872 //    int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5874     int result_family =
5875         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5877     int result_style =
5878         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5880     int result_numbers =
5881         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5883     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5885     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5886     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
5887         // there are no texts in selection, read from prefs
5889             sp_style_read_from_prefs(query, "/tools/text");
5891             if (g_object_get_data(tbl, "text_style_from_prefs")) {
5892             // do not reset the toolbar style from prefs if we already did it last time
5893             sp_style_unref(query);
5894             return;
5895         }
5896         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
5897     } else {
5898         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
5899     }
5901     if (query->text)
5902     {
5903         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
5904             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5905             gtk_entry_set_text (GTK_ENTRY (entry), "");
5907         } else if (query->text->font_specification.value || query->text->font_family.value) {
5909             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5911             // Get the font that corresponds
5912             Glib::ustring familyName;
5914             font_instance * font = font_factory::Default()->FaceFromStyle(query);
5915             if (font) {
5916                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
5917                 font->Unref();
5918                 font = NULL;
5919             }
5921             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
5923             Gtk::TreePath path;
5924             try {
5925                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
5926             } catch (...) {
5927                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
5928                 sp_style_unref(query);
5929                 return;
5930             }
5932             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5933             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5935             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
5937             gtk_tree_selection_select_path (tselection, path.gobj());
5938             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5940             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
5941         }
5943         //Size
5944         {
5945             GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
5946             gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
5947             g_object_set_data(tbl, "size-block", gpointer(1));
5948             gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
5949             g_object_set_data(tbl, "size-block", gpointer(0));
5950             g_free(str);
5951         }
5953         //Anchor
5954         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
5955         {
5956             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
5957             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5958             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5959             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5960         }
5961         else
5962         {
5963             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
5964             {
5965                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
5966                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5967                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5968                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5969             }
5970             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
5971             {
5972                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
5973                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5974                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5975                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5976             }
5977             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
5978             {
5979                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
5980                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5981                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5982                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5983             }
5984         }
5986         //Style
5987         {
5988             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
5990             gboolean active = gtk_toggle_button_get_active (button);
5991             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
5993             if (active != check)
5994             {
5995                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5996                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5997                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5998             }
5999         }
6001         {
6002             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
6004             gboolean active = gtk_toggle_button_get_active (button);
6005             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
6007             if (active != check)
6008             {
6009                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6010                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6011                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6012             }
6013         }
6015         //Orientation
6016         //locking both buttons, changing one affect all group (both)
6017         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
6018         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6020         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
6021         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
6023         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
6024         {
6025             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6026         }
6027         else
6028         {
6029             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
6030         }
6031         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6032         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
6033     }
6035     sp_style_unref(query);
6038 void
6039 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
6041     sp_text_toolbox_selection_changed (selection, tbl);
6044 void
6045 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
6047     sp_text_toolbox_selection_changed (NULL, tbl);
6050 void
6051 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
6052                                 GObject             *tbl)
6054     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
6055     GtkTreeModel *model = 0;
6056     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6057     GtkTreeIter   iter;
6058     char         *family = 0;
6060     gdk_pointer_ungrab (GDK_CURRENT_TIME);
6061     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
6063     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
6064         return;
6065     }
6067     gtk_tree_model_get (model, &iter, 0, &family, -1);
6069     if (g_object_get_data (G_OBJECT (selection), "block"))
6070     {
6071         gtk_entry_set_text (GTK_ENTRY (entry), family);
6072         return;
6073     }
6075     gtk_entry_set_text (GTK_ENTRY (entry), family);
6077     SPStyle *query =
6078         sp_style_new (SP_ACTIVE_DOCUMENT);
6080     int result_fontspec =
6081         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6083     //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6085     SPCSSAttr *css = sp_repr_css_attr_new ();
6088     // First try to get the font spec from the stored value
6089     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
6091     if (fontSpec.empty()) {
6092         // Construct a new font specification if it does not yet exist
6093         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6094         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6095         fontFromStyle->Unref();
6096     }
6098     if (!fontSpec.empty()) {
6099         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
6100         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
6101             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
6102             if (font) {
6103                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6105                 // Set all the these just in case they were altered when finding the best
6106                 // match for the new family and old style...
6108                 gchar c[256];
6110                 font->Family(c, 256);
6111                 sp_repr_css_set_property (css, "font-family", c);
6113                 font->Attribute( "weight", c, 256);
6114                 sp_repr_css_set_property (css, "font-weight", c);
6116                 font->Attribute("style", c, 256);
6117                 sp_repr_css_set_property (css, "font-style", c);
6119                 font->Attribute("stretch", c, 256);
6120                 sp_repr_css_set_property (css, "font-stretch", c);
6122                 font->Attribute("variant", c, 256);
6123                 sp_repr_css_set_property (css, "font-variant", c);
6125                 font->Unref();
6126             }
6127         }
6128     }
6130     // If querying returned nothing, set the default style of the tool (for new texts)
6131     if (result_fontspec == QUERY_STYLE_NOTHING)
6132     {
6133         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6134         prefs->setStyle("/tools/text/style", css);
6135         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
6136     }
6137     else
6138     {
6139         sp_desktop_set_style (desktop, css, true, true);
6140     }
6142     sp_style_unref(query);
6144     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6145                                    _("Text: Change font family"));
6146     sp_repr_css_attr_unref (css);
6147     g_free(family);
6148     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6150     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6153 /* This is where execution comes when the contents of the font family box have been completed
6154    by the press of the return key */
6155 void
6156 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
6157                                        GObject      *tbl)
6159     const char *family = gtk_entry_get_text (entry);   // Fetch the requested font family
6161 // Try to match that to a known font. If not, then leave current font alone and remain focused on text box
6162     try {
6163         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
6164         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
6165         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
6166         gtk_tree_selection_select_path (selection, path.gobj());
6167         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
6168         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6169     } catch (...) {
6170         if (family && strlen (family))
6171         {
6172             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6173         }
6174     }
6177 void
6178 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
6179                                    gpointer          data)
6181     if (g_object_get_data (G_OBJECT (button), "block")) return;
6182     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
6183     int prop = GPOINTER_TO_INT(data);
6185     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6186     SPCSSAttr *css = sp_repr_css_attr_new ();
6188     switch (prop)
6189     {
6190         case 0:
6191         {
6192             sp_repr_css_set_property (css, "text-anchor", "start");
6193             sp_repr_css_set_property (css, "text-align", "start");
6194             break;
6195         }
6196         case 1:
6197         {
6198             sp_repr_css_set_property (css, "text-anchor", "middle");
6199             sp_repr_css_set_property (css, "text-align", "center");
6200             break;
6201         }
6203         case 2:
6204         {
6205             sp_repr_css_set_property (css, "text-anchor", "end");
6206             sp_repr_css_set_property (css, "text-align", "end");
6207             break;
6208         }
6210         case 3:
6211         {
6212             sp_repr_css_set_property (css, "text-anchor", "start");
6213             sp_repr_css_set_property (css, "text-align", "justify");
6214             break;
6215         }
6216     }
6218     SPStyle *query =
6219         sp_style_new (SP_ACTIVE_DOCUMENT);
6220     int result_numbers =
6221         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6223     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6224     if (result_numbers == QUERY_STYLE_NOTHING)
6225     {
6226         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6227         prefs->setStyle("/tools/text/style", css);
6228     }
6230     sp_style_unref(query);
6232     sp_desktop_set_style (desktop, css, true, true);
6233     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6234                                    _("Text: Change alignment"));
6235     sp_repr_css_attr_unref (css);
6237     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6240 void
6241 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
6242                                gpointer          data)
6244     if (g_object_get_data (G_OBJECT (button), "block")) return;
6246     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
6247     SPCSSAttr   *css        = sp_repr_css_attr_new ();
6248     int          prop       = GPOINTER_TO_INT(data);
6249     bool         active     = gtk_toggle_button_get_active (button);
6251     SPStyle *query =
6252         sp_style_new (SP_ACTIVE_DOCUMENT);
6254     int result_fontspec =
6255         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6257     //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6258     //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6259     //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6261     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
6262     Glib::ustring newFontSpec = "";
6264     if (fontSpec.empty()) {
6265         // Construct a new font specification if it does not yet exist
6266         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6267         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6268         fontFromStyle->Unref();
6269     }
6271     switch (prop)
6272     {
6273         case 0:
6274         {
6275             if (!fontSpec.empty()) {
6276                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
6277             }
6278             if (fontSpec != newFontSpec) {
6279                 // Don't even set the bold if the font didn't exist on the system
6280                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
6281             }
6282             break;
6283         }
6285         case 1:
6286         {
6287             if (!fontSpec.empty()) {
6288                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
6289             }
6290             if (fontSpec != newFontSpec) {
6291                 // Don't even set the italic if the font didn't exist on the system
6292                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
6293             }
6294             break;
6295         }
6296     }
6298     if (!newFontSpec.empty()) {
6299         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6300     }
6302     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6303     if (result_fontspec == QUERY_STYLE_NOTHING)
6304     {
6305         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6306         prefs->setStyle("/tools/text/style", css);
6307     }
6309     sp_style_unref(query);
6311     sp_desktop_set_style (desktop, css, true, true);
6312     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6313                                    _("Text: Change font style"));
6314     sp_repr_css_attr_unref (css);
6316     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6319 void
6320 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
6321                                      gpointer         data)
6323     if (g_object_get_data (G_OBJECT (button), "block")) {
6324         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6325         return;
6326     }
6328     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
6329     SPCSSAttr   *css        = sp_repr_css_attr_new ();
6330     int          prop       = GPOINTER_TO_INT(data);
6332     switch (prop)
6333     {
6334         case 0:
6335         {
6336             sp_repr_css_set_property (css, "writing-mode", "lr");
6337             break;
6338         }
6340         case 1:
6341         {
6342             sp_repr_css_set_property (css, "writing-mode", "tb");
6343             break;
6344         }
6345     }
6347     SPStyle *query =
6348         sp_style_new (SP_ACTIVE_DOCUMENT);
6349     int result_numbers =
6350         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6352     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6353     if (result_numbers == QUERY_STYLE_NOTHING)
6354     {
6355         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6356         prefs->setStyle("/tools/text/style", css);
6357     }
6359     sp_desktop_set_style (desktop, css, true, true);
6360     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6361                                    _("Text: Change orientation"));
6362     sp_repr_css_attr_unref (css);
6364     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6367 gboolean
6368 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6370     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6371     if (!desktop) return FALSE;
6373     switch (get_group0_keyval (event)) {
6374         case GDK_Escape: // defocus
6375             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6376             sp_text_toolbox_selection_changed (NULL, tbl); // update
6377             return TRUE; // I consumed the event
6378             break;
6379     }
6380     return FALSE;
6383 gboolean
6384 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
6386     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6387     if (!desktop) return FALSE;
6389     switch (get_group0_keyval (event)) {
6390         case GDK_KP_Enter:
6391         case GDK_Return:
6392         case GDK_Escape: // defocus
6393             gtk_widget_hide (w);
6394             popdown_visible = false;
6395             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6396             return TRUE; // I consumed the event
6397             break;
6398         case GDK_w:
6399         case GDK_W:
6400             if (event->state & GDK_CONTROL_MASK) {
6401                 gtk_widget_hide (w);
6402                 popdown_visible = false;
6403                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6404                 return TRUE; // I consumed the event
6405             }
6406             break;
6407     }
6408     return FALSE;
6412 void
6413 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
6414                                GObject     *tbl)
6416     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6418     if (g_object_get_data (tbl, "size-block")) return;
6420     // If this is not from selecting a size in the list (in which case get_active will give the
6421     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
6422     // process this event. This fixes GTK's stupid insistence on sending an activate change every
6423     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
6424     if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
6425         return;
6427     gdouble value = -1;
6428     {
6429         gchar *endptr;
6430         gchar *const text = gtk_combo_box_get_active_text(cbox);
6431         if (text) {
6432             value = g_strtod(text, &endptr);
6433             if (endptr == text) {  // Conversion failed, non-numeric input.
6434                 value = -1;
6435             }
6436             g_free(text);
6437         }
6438     }
6439     if (value <= 0) {
6440         return; // could not parse value
6441     }
6443     SPCSSAttr *css = sp_repr_css_attr_new ();
6444     Inkscape::CSSOStringStream osfs;
6445     osfs << value;
6446     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
6448     SPStyle *query =
6449         sp_style_new (SP_ACTIVE_DOCUMENT);
6450     int result_numbers =
6451         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6453     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6454     if (result_numbers == QUERY_STYLE_NOTHING)
6455     {
6456         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6457         prefs->setStyle("/tools/text/style", css);
6458     }
6460     sp_style_unref(query);
6462     sp_desktop_set_style (desktop, css, true, true);
6463     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
6464                                    _("Text: Change font size"));
6465     sp_repr_css_attr_unref (css);
6467     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6470 gboolean
6471 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
6473     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6474     if (!desktop) return FALSE;
6476     if (!g_object_get_data (tbl, "esc-pressed")) {
6477         g_object_set_data (tbl, "enter-pressed", gpointer(1));
6478         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6479         sp_text_toolbox_size_changed (cbox, tbl);
6480         g_object_set_data (tbl, "enter-pressed", gpointer(0));
6481     }
6482     return FALSE; // I consumed the event
6486 gboolean
6487 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6489     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6490     if (!desktop) return FALSE;
6492     switch (get_group0_keyval (event)) {
6493         case GDK_Escape: // defocus
6494             g_object_set_data (tbl, "esc-pressed", gpointer(1));
6495             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6496             g_object_set_data (tbl, "esc-pressed", gpointer(0));
6497             return TRUE; // I consumed the event
6498             break;
6499         case GDK_Return: // defocus
6500         case GDK_KP_Enter:
6501             g_object_set_data (tbl, "enter-pressed", gpointer(1));
6502             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6503             sp_text_toolbox_size_changed (cbox, tbl);
6504             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6505             g_object_set_data (tbl, "enter-pressed", gpointer(0));
6506             return TRUE; // I consumed the event
6507             break;
6508     }
6509     return FALSE;
6512 void
6513 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
6514                                          GObject            *tbl)
6516     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
6517     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6518     int x, y;
6520     if (!popdown_visible)
6521     {
6522         gdk_window_get_origin (widget->window, &x, &y);
6523         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
6524         gtk_widget_show_all (popdown);
6525         //sp_transientize (popdown);
6527         gdk_pointer_grab (widget->window, TRUE,
6528                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
6529                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
6530                                         GDK_POINTER_MOTION_MASK),
6531                           NULL, NULL, GDK_CURRENT_TIME);
6533         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
6535         popdown_visible = true;
6536     }
6537     else
6538     {
6539         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6540         gdk_pointer_ungrab (GDK_CURRENT_TIME);
6541         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
6542         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6543         gtk_widget_hide (popdown);
6544         popdown_visible = false;
6545     }
6548 gboolean
6549 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
6550                                  GdkEventFocus    */*event*/,
6551                                  GObject          */*tbl*/)
6553     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
6554     return FALSE;
6557 gboolean
6558 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
6559                                    GdkEventFocus    */*event*/,
6560                                    GObject          */*tbl*/)
6562     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6564     if (popdown_hasfocus) {
6565         gtk_widget_hide (popdown);
6566         popdown_hasfocus = false;
6567         popdown_visible = false;
6568         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6569         return TRUE;
6570     }
6571     return FALSE;
6574 gboolean
6575 sp_text_toolbox_popdown_focus_in (GtkWidget        */*popdown*/,
6576                                    GdkEventFocus    */*event*/,
6577                                    GObject          */*tbl*/)
6579     popdown_hasfocus = true;
6580     return TRUE;
6584 void
6585 cell_data_func  (GtkTreeViewColumn */*column*/,
6586                  GtkCellRenderer   *cell,
6587                  GtkTreeModel      *tree_model,
6588                  GtkTreeIter       *iter,
6589                  gpointer           /*data*/)
6591     gchar *family;
6592     gtk_tree_model_get(tree_model, iter, 0, &family, -1);
6593     gchar *const family_escaped = g_markup_escape_text(family, -1);
6595     static char const *const sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
6596     gchar *const sample_escaped = g_markup_escape_text(sample, -1);
6598     std::stringstream markup;
6599     markup << family_escaped << "  <span foreground='darkgray' font_family='"
6600            << family_escaped << "'>" << sample_escaped << "</span>";
6601     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6603     g_free(family);
6604     g_free(family_escaped);
6605     g_free(sample_escaped);
6608 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
6609     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
6610     if (completion) {
6611         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
6612         g_object_unref (completion);
6613     }
6616 GtkWidget*
6617 sp_text_toolbox_new (SPDesktop *desktop)
6619     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
6620     GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("/toolbox/secondary", 1));
6622     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
6623     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
6625     GtkTooltips *tt = gtk_tooltips_new();
6626     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
6628     ////////////Family
6629     //Window
6630     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
6631     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
6633     //Entry
6634     GtkWidget           *entry = gtk_entry_new ();
6635     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
6636     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
6637     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
6638     gtk_entry_completion_set_text_column (completion, 0);
6639     gtk_entry_completion_set_minimum_key_length (completion, 1);
6640     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
6641     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
6642     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
6643     gtk_toolbar_append_widget( tbl, entry, "", "" );
6644     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
6646     //Button
6647     GtkWidget   *button = gtk_button_new ();
6648     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
6649     gtk_toolbar_append_widget( tbl, button, "", "");
6651     //Popdown
6652     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
6653     GtkWidget           *treeview = gtk_tree_view_new ();
6655     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
6656     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
6657     gtk_tree_view_column_pack_start (column, cell, FALSE);
6658     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
6659     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
6660     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
6662     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
6663     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
6664     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
6666     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
6668     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
6669     gtk_container_add (GTK_CONTAINER (sw), treeview);
6671     gtk_container_add (GTK_CONTAINER (window), sw);
6672     gtk_widget_set_size_request (window, 300, 450);
6674     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
6675     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
6676     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
6678     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
6680     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
6681     g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
6682     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
6684     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
6685     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6687     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
6688     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
6689     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
6690     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
6691     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
6693     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
6694     GtkWidget *box = gtk_event_box_new ();
6695     gtk_container_add (GTK_CONTAINER (box), image);
6696     gtk_toolbar_append_widget( tbl, box, "", "");
6697     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
6698     GtkTooltips *tooltips = gtk_tooltips_new ();
6699     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
6700     gtk_widget_hide (GTK_WIDGET (box));
6701     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
6703     ////////////Size
6704     gchar const *const sizes[] = {
6705         "4", "6", "8", "9", "10", "11", "12", "13", "14",
6706         "16", "18", "20", "22", "24", "28",
6707         "32", "36", "40", "48", "56", "64", "72", "144"
6708     };
6710     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
6711     for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
6712         gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
6713     }
6714     gtk_widget_set_size_request (cbox, 80, -1);
6715     gtk_toolbar_append_widget( tbl, cbox, "", "");
6716     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
6717     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
6718     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
6719     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
6721     ////////////Text anchor
6722     GtkWidget *group   = gtk_radio_button_new (NULL);
6723     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
6724     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
6726     // left
6727     GtkWidget *rbutton = group;
6728     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6729     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
6730     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6732     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6733     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
6734     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
6735     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
6737     // center
6738     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6739     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6740     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
6741     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6743     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6744     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
6745     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
6746     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
6748     // right
6749     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6750     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6751     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
6752     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6754     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6755     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
6756     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
6757     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
6759     // fill
6760     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6761     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6762     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
6763     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6765     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6766     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
6767     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
6768     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
6770     gtk_toolbar_append_widget( tbl, row, "", "");
6772     //spacer
6773     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6775     ////////////Text style
6776     row = gtk_hbox_new (FALSE, 4);
6778     // bold
6779     rbutton = gtk_toggle_button_new ();
6780     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6781     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
6782     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6783     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
6785     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6786     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
6787     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
6789     // italic
6790     rbutton = gtk_toggle_button_new ();
6791     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6792     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
6793     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6794     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
6796     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6797     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
6798     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
6800     gtk_toolbar_append_widget( tbl, row, "", "");
6802     //spacer
6803     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6805     ////////////Text orientation
6806     group   = gtk_radio_button_new (NULL);
6807     row     = gtk_hbox_new (FALSE, 4);
6808     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
6810     // horizontal
6811     rbutton = group;
6812     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6813     gtk_container_add           (GTK_CONTAINER (rbutton),
6814                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
6815     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6816     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
6818     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6819     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
6820     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
6822     // vertical
6823     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6824     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6825     gtk_container_add           (GTK_CONTAINER (rbutton),
6826                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
6827     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6828     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
6830     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6831     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
6832     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
6833     gtk_toolbar_append_widget( tbl, row, "", "" );
6836     //watch selection
6837     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
6839     sigc::connection *c_selection_changed =
6840         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
6841                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
6842     pool->add_connection ("selection-changed", c_selection_changed);
6844     sigc::connection *c_selection_modified =
6845         new sigc::connection (sp_desktop_selection (desktop)->connectModified
6846                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
6847     pool->add_connection ("selection-modified", c_selection_modified);
6849     sigc::connection *c_subselection_changed =
6850         new sigc::connection (desktop->connectToolSubselectionChanged
6851                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
6852     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
6854     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
6857     gtk_widget_show_all( GTK_WIDGET(tbl) );
6859     return GTK_WIDGET(tbl);
6860 } // end of sp_text_toolbox_new()
6862 }//<unnamed> namespace
6865 //#########################
6866 //##      Connector      ##
6867 //#########################
6869 static void sp_connector_path_set_avoid(void)
6871     cc_selection_set_avoid(true);
6875 static void sp_connector_path_set_ignore(void)
6877     cc_selection_set_avoid(false);
6882 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
6884     // quit if run by the _changed callbacks
6885     if (g_object_get_data( tbl, "freeze" )) {
6886         return;
6887     }
6889     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
6890     SPDocument *doc = sp_desktop_document(desktop);
6892     if (!sp_document_get_undo_sensitive(doc))
6893     {
6894         return;
6895     }
6897     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6899     if ( repr->attribute("inkscape:connector-spacing") ) {
6900         gdouble priorValue = gtk_adjustment_get_value(adj);
6901         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
6902         if ( priorValue == gtk_adjustment_get_value(adj) ) {
6903             return;
6904         }
6905     } else if ( adj->value == defaultConnSpacing ) {
6906         return;
6907     }
6909     // in turn, prevent callbacks from responding
6910     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6912     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
6913     SP_OBJECT(desktop->namedview)->updateRepr();
6915     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
6916     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
6917         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
6918         Geom::Matrix m = Geom::identity();
6919         avoid_item_move(&m, item);
6920     }
6922     if (items) {
6923         g_slist_free(items);
6924     }
6926     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
6927             _("Change connector spacing"));
6929     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6931     spinbutton_defocus(GTK_OBJECT(tbl));
6934 static void sp_connector_graph_layout(void)
6936     if (!SP_ACTIVE_DESKTOP) return;
6937     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6939     // hack for clones, see comment in align-and-distribute.cpp
6940     int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
6941     prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
6943     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
6945     prefs->setInt("/options/clonecompensation/value", saved_compensation);
6947     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
6950 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6952     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6953     prefs->setBool("/tools/connector/directedlayout",
6954                 gtk_toggle_action_get_active( act ));
6957 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6959     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6960     prefs->setBool("/tools/connector/avoidoverlaplayout",
6961                 gtk_toggle_action_get_active( act ));
6965 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
6967     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6968     prefs->setDouble("/tools/connector/length", adj->value);
6969     spinbutton_defocus(GTK_OBJECT(tbl));
6972 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
6973                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
6974                                             bool /*is_interactive*/, gpointer data)
6976     GtkWidget *tbl = GTK_WIDGET(data);
6978     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6979         return;
6980     }
6981     if (strcmp(name, "inkscape:connector-spacing") != 0) {
6982         return;
6983     }
6985     GtkAdjustment *adj = (GtkAdjustment*)
6986             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
6987     gdouble spacing = defaultConnSpacing;
6988     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
6990     gtk_adjustment_set_value(adj, spacing);
6994 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
6995     NULL, /* child_added */
6996     NULL, /* child_removed */
6997     connector_tb_event_attr_changed,
6998     NULL, /* content_changed */
6999     NULL  /* order_changed */
7000 };
7003 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
7005     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7006     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
7008     {
7009         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
7010                                           _("Avoid"),
7011                                           _("Make connectors avoid selected objects"),
7012                                           "connector_avoid",
7013                                           secondarySize );
7014         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
7015         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7016     }
7018     {
7019         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
7020                                           _("Ignore"),
7021                                           _("Make connectors ignore selected objects"),
7022                                           "connector_ignore",
7023                                           secondarySize );
7024         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
7025         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7026     }
7028     EgeAdjustmentAction* eact = 0;
7030     // Spacing spinbox
7031     eact = create_adjustment_action( "ConnectorSpacingAction",
7032                                      _("Connector Spacing"), _("Spacing:"),
7033                                      _("The amount of space left around objects by auto-routing connectors"),
7034                                      "/tools/connector/spacing", defaultConnSpacing,
7035                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
7036                                      0, 100, 1.0, 10.0,
7037                                      0, 0, 0,
7038                                      connector_spacing_changed, 1, 0 );
7039     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7041     // Graph (connector network) layout
7042     {
7043         InkAction* inky = ink_action_new( "ConnectorGraphAction",
7044                                           _("Graph"),
7045                                           _("Nicely arrange selected connector network"),
7046                                           "graph_layout",
7047                                           secondarySize );
7048         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
7049         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7050     }
7052     // Default connector length spinbox
7053     eact = create_adjustment_action( "ConnectorLengthAction",
7054                                      _("Connector Length"), _("Length:"),
7055                                      _("Ideal length for connectors when layout is applied"),
7056                                      "/tools/connector/length", 100,
7057                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
7058                                      10, 1000, 10.0, 100.0,
7059                                      0, 0, 0,
7060                                      connector_length_changed, 1, 0 );
7061     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7064     // Directed edges toggle button
7065     {
7066         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
7067                                                       _("Downwards"),
7068                                                       _("Make connectors with end-markers (arrows) point downwards"),
7069                                                       "directed_graph",
7070                                                       Inkscape::ICON_SIZE_DECORATION );
7071         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7073         bool tbuttonstate = prefs->getBool("/tools/connector/directedlayout");
7074         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7076         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
7077     }
7079     // Avoid overlaps toggle button
7080     {
7081         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
7082                                                       _("Remove overlaps"),
7083                                                       _("Do not allow overlapping shapes"),
7084                                                       "remove_overlaps",
7085                                                       Inkscape::ICON_SIZE_DECORATION );
7086         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7088         bool tbuttonstate = prefs->getBool("/tools/connector/avoidoverlaplayout");
7089         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), (tbuttonstate ? TRUE : FALSE ));
7091         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
7092     }
7094     // Code to watch for changes to the connector-spacing attribute in
7095     // the XML.
7096     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7097     g_assert(repr != NULL);
7099     purge_repr_listener( holder, holder );
7101     if (repr) {
7102         g_object_set_data( holder, "repr", repr );
7103         Inkscape::GC::anchor(repr);
7104         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
7105         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
7106     }
7107 } // end of sp_connector_toolbox_prep()
7110 //#########################
7111 //##     Paintbucket     ##
7112 //#########################
7114 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
7116     gint channels = ege_select_one_action_get_active( act );
7117     flood_channels_set_channels( channels );
7120 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
7122     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7123     prefs->setInt("/tools/paintbucket/threshold", (gint)adj->value);
7126 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
7128     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7129     prefs->setBool("/tools/paintbucket/autogap", ege_select_one_action_get_active( act ));
7132 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
7134     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
7135     SPUnit const *unit = tracker->getActiveUnit();
7136     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7138     prefs->setDouble("/tools/paintbucket/offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
7139     prefs->setString("/tools/paintbucket/offsetunits", sp_unit_get_abbreviation(unit));
7142 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
7144     // FIXME: make defaults settable via Inkscape Options
7145     struct KeyValue {
7146         char const *key;
7147         double value;
7148     } const key_values[] = {
7149         {"threshold", 15},
7150         {"offset", 0.0}
7151     };
7153     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
7154         KeyValue const &kv = key_values[i];
7155         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
7156         if ( adj ) {
7157             gtk_adjustment_set_value(adj, kv.value);
7158         }
7159     }
7161     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
7162     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
7163     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
7164     ege_select_one_action_set_active( autogap_action, 0 );
7167 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
7169     EgeAdjustmentAction* eact = 0;
7170     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7172     {
7173         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7175         GList* items = 0;
7176         gint count = 0;
7177         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
7178         {
7179             GtkTreeIter iter;
7180             gtk_list_store_append( model, &iter );
7181             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7182             count++;
7183         }
7184         g_list_free( items );
7185         items = 0;
7186         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
7187         g_object_set( act1, "short_label", _("Fill by:"), NULL );
7188         ege_select_one_action_set_appearance( act1, "compact" );
7189         ege_select_one_action_set_active( act1, prefs->getInt("/tools/paintbucket/channels", 0) );
7190         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
7191         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
7192         g_object_set_data( holder, "channels_action", act1 );
7193     }
7195     // Spacing spinbox
7196     {
7197         eact = create_adjustment_action(
7198             "ThresholdAction",
7199             _("Fill Threshold"), _("Threshold:"),
7200             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
7201             "/tools/paintbucket/threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
7202             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
7203             0, 0, 0,
7204             paintbucket_threshold_changed, 1, 0 );
7206         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
7207         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7208     }
7210     // Create the units menu.
7211     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
7212     Glib::ustring stored_unit = prefs->getString("/tools/paintbucket/offsetunits");
7213     if (!stored_unit.empty())
7214         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit.data()));
7215     g_object_set_data( holder, "tracker", tracker );
7216     {
7217         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
7218         gtk_action_group_add_action( mainActions, act );
7219     }
7221     // Offset spinbox
7222     {
7223         eact = create_adjustment_action(
7224             "OffsetAction",
7225             _("Grow/shrink by"), _("Grow/shrink by:"),
7226             _("The amount to grow (positive) or shrink (negative) the created fill path"),
7227             "/tools/paintbucket/offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
7228             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
7229             0, 0, 0,
7230             paintbucket_offset_changed, 1, 2);
7231         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
7233         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7234     }
7236     /* Auto Gap */
7237     {
7238         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7240         GList* items = 0;
7241         gint count = 0;
7242         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
7243         {
7244             GtkTreeIter iter;
7245             gtk_list_store_append( model, &iter );
7246             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7247             count++;
7248         }
7249         g_list_free( items );
7250         items = 0;
7251         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
7252         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
7253         ege_select_one_action_set_appearance( act2, "compact" );
7254         ege_select_one_action_set_active( act2, prefs->getBool("/tools/paintbucket/autogap") );
7255         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
7256         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
7257         g_object_set_data( holder, "autogap_action", act2 );
7258     }
7260     /* Reset */
7261     {
7262         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
7263                                           _("Defaults"),
7264                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
7265                                           GTK_STOCK_CLEAR );
7266         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
7267         gtk_action_group_add_action( mainActions, act );
7268         gtk_action_set_sensitive( act, TRUE );
7269     }
7273 /*
7274   Local Variables:
7275   mode:c++
7276   c-file-style:"stroustrup"
7277   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
7278   indent-tabs-mode:nil
7279   fill-column:99
7280   End:
7281 */
7282 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :