Code

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