Code

fix saving presets, move it to the menu; allow overwriting a preset; fix redundant...
[inkscape.git] / src / widgets / toolbox.cpp
1 /** \file
2  * Controls bars for some of Inkscape's tools
3  * (for some tools, they are in their own files)
4  */
6 /*
7 *
8 * Authors:
9 *   MenTaLguY <mental@rydia.net>
10 *   Lauris Kaplinski <lauris@kaplinski.com>
11 *   bulia byak <buliabyak@users.sf.net>
12 *   Frank Felfe <innerspace@iname.com>
13 *   John Cliff <simarilius@yahoo.com>
14 *   David Turner <novalis@gnu.org>
15 *   Josh Andler <scislac@scislac.com>
16 *   Jon A. Cruz <jon@joncruz.org>
17 *
18 * Copyright (C) 2004 David Turner
19 * Copyright (C) 2003 MenTaLguY
20 * Copyright (C) 1999-2006 authors
21 * Copyright (C) 2001-2002 Ximian, Inc.
22 *
23 * Released under GNU GPL, read the file 'COPYING' for more information
24 */
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
30 #include <cstring>
31 #include <string>
33 #include <gtkmm.h>
34 #include <gtk/gtk.h>
35 #include <iostream>
36 #include <sstream>
38 #include "widgets/button.h"
39 #include "widgets/widget-sizes.h"
40 #include "widgets/spw-utilities.h"
41 #include "widgets/spinbutton-events.h"
42 #include "dialogs/text-edit.h"
43 #include "dialogs/dialog-events.h"
45 #include "ui/widget/style-swatch.h"
47 #include "prefs-utils.h"
48 #include "verbs.h"
49 #include "sp-namedview.h"
50 #include "desktop.h"
51 #include "desktop-handles.h"
52 #include "xml/repr.h"
53 #include "xml/node-event-vector.h"
54 #include "xml/attribute-record.h"
55 #include <glibmm/i18n.h>
56 #include "helper/unit-menu.h"
57 #include "helper/units.h"
58 #include "live_effects/effect.h"
60 #include "inkscape.h"
61 #include "conn-avoid-ref.h"
64 #include "select-toolbar.h"
65 #include "gradient-toolbar.h"
67 #include "connector-context.h"
68 #include "node-context.h"
69 #include "draw-context.h"
70 #include "shape-editor.h"
71 #include "tweak-context.h"
72 #include "sp-rect.h"
73 #include "box3d.h"
74 #include "box3d-context.h"
75 #include "sp-star.h"
76 #include "sp-spiral.h"
77 #include "sp-ellipse.h"
78 #include "sp-text.h"
79 #include "sp-flowtext.h"
80 #include "sp-clippath.h"
81 #include "sp-mask.h"
82 #include "style.h"
83 #include "selection.h"
84 #include "selection-chemistry.h"
85 #include "document-private.h"
86 #include "desktop-style.h"
87 #include "../libnrtype/font-lister.h"
88 #include "../libnrtype/font-instance.h"
89 #include "../connection-pool.h"
90 #include "../prefs-utils.h"
91 #include "../inkscape-stock.h"
92 #include "icon.h"
93 #include "graphlayout/graphlayout.h"
94 #include "interface.h"
95 #include "shortcuts.h"
97 #include "mod360.h"
99 #include "toolbox.h"
101 #include "flood-context.h"
103 #include "ink-action.h"
104 #include "ege-adjustment-action.h"
105 #include "ege-output-action.h"
106 #include "ege-select-one-action.h"
107 #include "helper/unit-tracker.h"
109 #include "svg/css-ostringstream.h"
111 #include "widgets/calligraphic-profile-rename.h"
113 using Inkscape::UnitTracker;
115 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
116 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
118 static void       sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
119 static void       sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
120 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
121 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
122 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
123 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 static void       box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
126 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
127 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
128 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
129 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
130 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
131 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
132 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
133 static void       sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
135 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
138 Inkscape::IconSize prefToSize( gchar const *path, gchar const *attr, int base ) {
139     static Inkscape::IconSize sizeChoices[] = {
140         Inkscape::ICON_SIZE_LARGE_TOOLBAR,
141         Inkscape::ICON_SIZE_SMALL_TOOLBAR,
142         Inkscape::ICON_SIZE_MENU
143     };
144     int index = prefs_get_int_attribute_limited( path, attr, base, 0, G_N_ELEMENTS(sizeChoices) );
145     return sizeChoices[index];
148 static struct {
149     gchar const *type_name;
150     gchar const *data_name;
151     sp_verb_t verb;
152     sp_verb_t doubleclick_verb;
153 } const tools[] = {
154     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
155     { "SPNodeContext",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
156     { "SPTweakContext",    "tweak_tool",     SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
157     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
158     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
159     { "Box3DContext",      "3dbox_tool",     SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
160     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
161     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
162     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
163     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
164     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
165     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
166     { "SPEraserContext",   "eraser_tool",    SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
167     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
168     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
169     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
170     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
171     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
172     { NULL, NULL, 0, 0 }
173 };
175 static struct {
176     gchar const *type_name;
177     gchar const *data_name;
178     GtkWidget *(*create_func)(SPDesktop *desktop);
179     void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
180     gchar const *ui_name;
181     gint swatch_verb_id;
182     gchar const *swatch_tool;
183     gchar const *swatch_tip;
184 } const aux_toolboxes[] = {
185     { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep,            "SelectToolbar",
186       SP_VERB_INVALID, 0, 0},
187     { "SPNodeContext",   "node_toolbox",   0, sp_node_toolbox_prep,              "NodeToolbar",
188       SP_VERB_INVALID, 0, 0},
189     { "SPTweakContext",   "tweak_toolbox",   0, sp_tweak_toolbox_prep,              "TweakToolbar",
190       SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", N_("Color/opacity used for color tweaking")},
191     { "SPZoomContext",   "zoom_toolbox",   0, sp_zoom_toolbox_prep,              "ZoomToolbar",
192       SP_VERB_INVALID, 0, 0},
193     { "SPStarContext",   "star_toolbox",   0, sp_star_toolbox_prep,              "StarToolbar",
194       SP_VERB_CONTEXT_STAR_PREFS,   "tools.shapes.star",     N_("Style of new stars")},
195     { "SPRectContext",   "rect_toolbox",   0, sp_rect_toolbox_prep,              "RectToolbar",
196       SP_VERB_CONTEXT_RECT_PREFS,   "tools.shapes.rect",     N_("Style of new rectangles")},
197     { "Box3DContext",  "3dbox_toolbox",  0, box3d_toolbox_prep,             "3DBoxToolbar",
198       SP_VERB_CONTEXT_3DBOX_PREFS,  "tools.shapes.3dbox",    N_("Style of new 3D boxes")},
199     { "SPArcContext",    "arc_toolbox",    0, sp_arc_toolbox_prep,               "ArcToolbar",
200       SP_VERB_CONTEXT_ARC_PREFS,    "tools.shapes.arc",      N_("Style of new ellipses")},
201     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
202       SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral",   N_("Style of new spirals")},
203     { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep,            "PencilToolbar",
204       SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", N_("Style of new paths created by Pencil")},
205     { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep,                     "PenToolbar",
206       SP_VERB_CONTEXT_PEN_PREFS,    "tools.freehand.pen",    N_("Style of new paths created by Pen")},
207     { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
208       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", N_("Style of new calligraphic strokes")},
209     { "SPEraserContext", "eraser_toolbox", 0, sp_eraser_toolbox_prep,"EraserToolbar",
210       SP_VERB_CONTEXT_ERASER_PREFS, "tools.eraser", _("TBD")},
211     { "SPTextContext",   "text_toolbox",   sp_text_toolbox_new, 0,               0,
212       SP_VERB_INVALID, 0, 0},
213     { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep,         "DropperToolbar",
214       SP_VERB_INVALID, 0, 0},
215     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0,       0,
216       SP_VERB_INVALID, 0, 0},
217     { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep,   "ConnectorToolbar",
218       SP_VERB_INVALID, 0, 0},
219     { "SPFloodContext",  "paintbucket_toolbox",  0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
220       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", N_("Style of Paint Bucket fill objects")},
221     { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
222 };
224 #define TOOLBAR_SLIDER_HINT "full"
226 static gchar const * ui_descr =
227         "<ui>"
228         "  <toolbar name='SelectToolbar'>"
229         "    <toolitem action='EditSelectAll' />"
230         "    <toolitem action='EditSelectAllInAllLayers' />"
231         "    <toolitem action='EditDeselect' />"
232         "    <separator />"
233         "    <toolitem action='ObjectRotate90CCW' />"
234         "    <toolitem action='ObjectRotate90' />"
235         "    <toolitem action='ObjectFlipHorizontally' />"
236         "    <toolitem action='ObjectFlipVertically' />"
237         "    <separator />"
238         "    <toolitem action='SelectionToBack' />"
239         "    <toolitem action='SelectionLower' />"
240         "    <toolitem action='SelectionRaise' />"
241         "    <toolitem action='SelectionToFront' />"
242         "    <separator />"
243         "    <toolitem action='XAction' />"
244         "    <toolitem action='YAction' />"
245         "    <toolitem action='WidthAction' />"
246         "    <toolitem action='LockAction' />"
247         "    <toolitem action='HeightAction' />"
248         "    <toolitem action='UnitsAction' />"
249         "    <separator />"
250         "    <toolitem action='transform_affect_label' />"
251         "    <toolitem action='transform_stroke' />"
252         "    <toolitem action='transform_corners' />"
253         "    <toolitem action='transform_gradient' />"
254         "    <toolitem action='transform_pattern' />"
255         "  </toolbar>"
257         "  <toolbar name='NodeToolbar'>"
258         "    <toolitem action='NodeInsertAction' />"
259         "    <toolitem action='NodeDeleteAction' />"
260         "    <separator />"
261         "    <toolitem action='NodeJoinAction' />"
262         "    <toolitem action='NodeBreakAction' />"
263         "    <separator />"
264         "    <toolitem action='NodeJoinSegmentAction' />"
265         "    <toolitem action='NodeDeleteSegmentAction' />"
266         "    <separator />"
267         "    <toolitem action='NodeCuspAction' />"
268         "    <toolitem action='NodeSmoothAction' />"
269         "    <toolitem action='NodeSymmetricAction' />"
270         "    <separator />"
271         "    <toolitem action='NodeLineAction' />"
272         "    <toolitem action='NodeCurveAction' />"
273         "    <separator />"
274         "    <toolitem action='ObjectToPath' />"
275         "    <toolitem action='StrokeToPath' />"
276         "    <separator />"
277         "    <toolitem action='NodeXAction' />"
278         "    <toolitem action='NodeYAction' />"
279         "    <toolitem action='NodeUnitsAction' />"
280         "    <separator />"
281         "    <toolitem action='ObjectEditClipPathAction' />"
282         "    <toolitem action='ObjectEditMaskPathAction' />"
283         "    <toolitem action='EditNextLPEParameterAction' />"
284         "    <separator />"
285         "    <toolitem action='NodesShowHandlesAction' />"
286         "    <toolitem action='NodesShowHelperpath' />"
287         "  </toolbar>"
289         "  <toolbar name='TweakToolbar'>"
290         "    <toolitem action='TweakWidthAction' />"
291         "    <separator />"
292         "    <toolitem action='TweakForceAction' />"
293         "    <toolitem action='TweakPressureAction' />"
294         "    <separator />"
295         "    <toolitem action='TweakModeAction' />"
296         "    <separator />"
297         "    <toolitem action='TweakFidelityAction' />"
298         "    <separator />"
299         "    <toolitem action='TweakChannelsLabel' />"
300         "    <toolitem action='TweakDoH' />"
301         "    <toolitem action='TweakDoS' />"
302         "    <toolitem action='TweakDoL' />"
303         "    <toolitem action='TweakDoO' />"
304         "  </toolbar>"
306         "  <toolbar name='ZoomToolbar'>"
307         "    <toolitem action='ZoomIn' />"
308         "    <toolitem action='ZoomOut' />"
309         "    <separator />"
310         "    <toolitem action='Zoom1:0' />"
311         "    <toolitem action='Zoom1:2' />"
312         "    <toolitem action='Zoom2:1' />"
313         "    <separator />"
314         "    <toolitem action='ZoomSelection' />"
315         "    <toolitem action='ZoomDrawing' />"
316         "    <toolitem action='ZoomPage' />"
317         "    <toolitem action='ZoomPageWidth' />"
318         "    <separator />"
319         "    <toolitem action='ZoomPrev' />"
320         "    <toolitem action='ZoomNext' />"
321         "  </toolbar>"
323         "  <toolbar name='StarToolbar'>"
324         "    <separator />"
325         "    <toolitem action='StarStateAction' />"
326         "    <separator />"
327         "    <toolitem action='FlatAction' />"
328         "    <separator />"
329         "    <toolitem action='MagnitudeAction' />"
330         "    <toolitem action='SpokeAction' />"
331         "    <toolitem action='RoundednessAction' />"
332         "    <toolitem action='RandomizationAction' />"
333         "    <separator />"
334         "    <toolitem action='StarResetAction' />"
335         "  </toolbar>"
337         "  <toolbar name='RectToolbar'>"
338         "    <toolitem action='RectStateAction' />"
339         "    <toolitem action='RectWidthAction' />"
340         "    <toolitem action='RectHeightAction' />"
341         "    <toolitem action='RadiusXAction' />"
342         "    <toolitem action='RadiusYAction' />"
343         "    <toolitem action='RectUnitsAction' />"
344         "    <separator />"
345         "    <toolitem action='RectResetAction' />"
346         "  </toolbar>"
348         "  <toolbar name='3DBoxToolbar'>"
349         "    <toolitem action='3DBoxAngleXAction' />"
350         "    <toolitem action='3DBoxVPXStateAction' />"
351         "    <separator />"
352         "    <toolitem action='3DBoxAngleYAction' />"
353         "    <toolitem action='3DBoxVPYStateAction' />"
354         "    <separator />"
355         "    <toolitem action='3DBoxAngleZAction' />"
356         "    <toolitem action='3DBoxVPZStateAction' />"
357         "  </toolbar>"
359         "  <toolbar name='SpiralToolbar'>"
360         "    <toolitem action='SpiralStateAction' />"
361         "    <toolitem action='SpiralRevolutionAction' />"
362         "    <toolitem action='SpiralExpansionAction' />"
363         "    <toolitem action='SpiralT0Action' />"
364         "    <separator />"
365         "    <toolitem action='SpiralResetAction' />"
366         "  </toolbar>"
368         "  <toolbar name='PenToolbar'>"
369         "    <toolitem action='FreehandModeActionPenTemp' />"
370         "    <toolitem action='FreehandModeActionPen' />"
371         "    <separator />"
372         "    <toolitem action='SetPenShapeAction'/>"
373         "  </toolbar>"
375         "  <toolbar name='PencilToolbar'>"
376         "    <toolitem action='FreehandModeActionPencilTemp' />"
377         "    <toolitem action='FreehandModeActionPencil' />"
378         "    <separator />"
379         "    <toolitem action='PencilToleranceAction' />"
380         "    <separator />"
381         "    <toolitem action='PencilResetAction' />"
382         "    <separator />"
383         "    <toolitem action='SetPencilShapeAction'/>"
384         "  </toolbar>"
386         "  <toolbar name='CalligraphyToolbar'>"
387         "    <separator />"
388         "    <toolitem action='SetProfileAction'/>"
389         "    <separator />"
390         "    <toolitem action='CalligraphyWidthAction' />"
391         "    <toolitem action='PressureAction' />"
392         "    <toolitem action='TraceAction' />"
393         "    <toolitem action='ThinningAction' />"
394         "    <separator />"
395         "    <toolitem action='AngleAction' />"
396         "    <toolitem action='TiltAction' />"
397         "    <toolitem action='FixationAction' />"
398         "    <separator />"
399         "    <toolitem action='CapRoundingAction' />"
400         "    <separator />"
401         "    <toolitem action='TremorAction' />"
402         "    <toolitem action='WiggleAction' />"
403         "    <toolitem action='MassAction' />"
404         "    <separator />"
405         "  </toolbar>"
407         "  <toolbar name='ArcToolbar'>"
408         "    <toolitem action='ArcStateAction' />"
409         "    <separator />"
410         "    <toolitem action='ArcStartAction' />"
411         "    <toolitem action='ArcEndAction' />"
412         "    <separator />"
413         "    <toolitem action='ArcOpenAction' />"
414         "    <separator />"
415         "    <toolitem action='ArcResetAction' />"
416         "    <separator />"
417         "  </toolbar>"
419         "  <toolbar name='PaintbucketToolbar'>"
420         "    <toolitem action='ChannelsAction' />"
421         "    <separator />"
422         "    <toolitem action='ThresholdAction' />"
423         "    <separator />"
424         "    <toolitem action='OffsetAction' />"
425         "    <toolitem action='PaintbucketUnitsAction' />"
426         "    <separator />"
427         "    <toolitem action='AutoGapAction' />"
428         "    <separator />"
429         "    <toolitem action='PaintbucketResetAction' />"
430         "  </toolbar>"
432         "  <toolbar name='EraserToolbar'>"
433         "    <toolitem action='EraserWidthAction' />"
434         "    <separator />"
435         "    <toolitem action='EraserModeAction' />"
436         "  </toolbar>"
438         "  <toolbar name='DropperToolbar'>"
439         "    <toolitem action='DropperOpacityAction' />"
440         "    <toolitem action='DropperPickAlphaAction' />"
441         "    <toolitem action='DropperSetAlphaAction' />"
442         "  </toolbar>"
444         "  <toolbar name='ConnectorToolbar'>"
445         "    <toolitem action='ConnectorAvoidAction' />"
446         "    <toolitem action='ConnectorIgnoreAction' />"
447         "    <toolitem action='ConnectorSpacingAction' />"
448         "    <toolitem action='ConnectorGraphAction' />"
449         "    <toolitem action='ConnectorLengthAction' />"
450         "    <toolitem action='ConnectorDirectedAction' />"
451         "    <toolitem action='ConnectorOverlapAction' />"
452         "  </toolbar>"
454         "</ui>"
457 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
459 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
461 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
462 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
464 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
465 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
467 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
468 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
471 GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick( GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
472                                                               Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
473                                                               Inkscape::UI::View::View *view, GtkTooltips *tt);
475 class VerbAction : public Gtk::Action {
476 public:
477     static Glib::RefPtr<VerbAction> create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
479     virtual ~VerbAction();
480     virtual void set_active(bool active = true);
482 protected:
483     virtual Gtk::Widget* create_menu_item_vfunc();
484     virtual Gtk::Widget* create_tool_item_vfunc();
486     virtual void connect_proxy_vfunc(Gtk::Widget* proxy);
487     virtual void disconnect_proxy_vfunc(Gtk::Widget* proxy);
489     virtual void on_activate();
491 private:
492     Inkscape::Verb* verb;
493     Inkscape::Verb* verb2;
494     Inkscape::UI::View::View *view;
495     GtkTooltips *tooltips;
496     bool active;
498     VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
499 };
502 Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips)
504     Glib::RefPtr<VerbAction> result;
505     SPAction *action = verb->get_action(view);
506     if ( action ) {
507         //SPAction* action2 = verb2 ? verb2->get_action(view) : 0;
508         result = Glib::RefPtr<VerbAction>(new VerbAction(verb, verb2, view, tooltips));
509     }
511     return result;
514 VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips) :
515     Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(verb->get_image()), Glib::ustring(_(verb->get_name())), Glib::ustring(_(verb->get_tip()))),
516     verb(verb),
517     verb2(verb2),
518     view(view),
519     tooltips(tooltips),
520     active(false)
524 VerbAction::~VerbAction()
528 Gtk::Widget* VerbAction::create_menu_item_vfunc()
530 // First call in to get the icon rendered if present in SVG
531     Gtk::Widget *widget = sp_icon_get_icon( property_stock_id().get_value().get_string(), Inkscape::ICON_SIZE_MENU );
532     delete widget;
533     widget = 0;
535     Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
536 //     g_message("create_menu_item_vfunc() = %p  for '%s'", widg, verb->get_id());
537     return widg;
540 Gtk::Widget* VerbAction::create_tool_item_vfunc()
542 //     Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
543     Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
544     GtkWidget* toolbox = 0;
545     GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
546                                                                           SP_BUTTON_TYPE_TOGGLE,
547                                                                           verb,
548                                                                           verb2,
549                                                                           view,
550                                                                           tooltips );
551     if ( active ) {
552         sp_button_toggle_set_down( SP_BUTTON(button), active);
553     }
554     gtk_widget_show_all( button );
555     Gtk::Widget* wrapped = Glib::wrap(button);
556     Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
557     holder->add(*wrapped);
559 //     g_message("create_tool_item_vfunc() = %p  for '%s'", holder, verb->get_id());
560     return holder;
563 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
565 //     g_message("connect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
566     Gtk::Action::connect_proxy_vfunc(proxy);
569 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
571 //     g_message("disconnect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
572     Gtk::Action::disconnect_proxy_vfunc(proxy);
575 void VerbAction::set_active(bool active)
577     this->active = active;
578     Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
579     for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
580         Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
581         if (ti) {
582             // *should* have one child that is the SPButton
583             Gtk::Widget* child = ti->get_child();
584             if ( child && SP_IS_BUTTON(child->gobj()) ) {
585                 SPButton* button = SP_BUTTON(child->gobj());
586                 sp_button_toggle_set_down( button, active );
587             }
588         }
589     }
592 void VerbAction::on_activate()
594     if ( verb ) {
595         SPAction *action = verb->get_action(view);
596         if ( action ) {
597             sp_action_perform(action, 0);
598         }
599     }
602 /* Global text entry widgets necessary for update */
603 /* GtkWidget *dropper_rgb_entry,
604           *dropper_opacity_entry ; */
605 // should be made a private member once this is converted to class
607 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
608     connection->disconnect();
609     delete connection;
612 static void purge_repr_listener( GObject* obj, GObject* tbl )
614     (void)obj;
615     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
616     if (oldrepr) { // remove old listener
617         sp_repr_remove_listener_by_data(oldrepr, tbl);
618         Inkscape::GC::release(oldrepr);
619         oldrepr = 0;
620         g_object_set_data( tbl, "repr", NULL );
621     }
624 GtkWidget *
625 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
626                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
627                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
629     SPAction *action = verb->get_action(view);
630     if (!action) return NULL;
632     SPAction *doubleclick_action;
633     if (doubleclick_verb)
634         doubleclick_action = doubleclick_verb->get_action(view);
635     else
636         doubleclick_action = NULL;
638     /* fixme: Handle sensitive/unsensitive */
639     /* fixme: Implement sp_button_new_from_action */
640     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
641     gtk_widget_show(b);
644     unsigned int shortcut = sp_shortcut_get_primary(verb);
645     if (shortcut) {
646         gchar key[256];
647         sp_ui_shortcut_string(shortcut, key);
648         gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
649         if ( t ) {
650             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
651         }
652         g_free(tip);
653     } else {
654         if ( t ) {
655             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
656         }
657     }
659     return b;
663 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
665     SPAction* targetAction = SP_ACTION(user_data);
666     if ( targetAction ) {
667         sp_action_perform( targetAction, NULL );
668     }
671 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
673     if ( data ) {
674         GtkAction* act = GTK_ACTION(data);
675         gtk_action_set_sensitive( act, sensitive );
676     }
679 static SPActionEventVector action_event_vector = {
680     {NULL},
681     NULL,
682     NULL,
683     sp_action_action_set_sensitive,
684     NULL,
685     NULL
686 };
688 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
690     GtkAction* act = 0;
692     SPAction* targetAction = verb->get_action(view);
693     InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size  );
694     act = GTK_ACTION(inky);
695     gtk_action_set_sensitive( act, targetAction->sensitive );
697     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
699     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
700     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
702     return act;
705 Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
707     Inkscape::UI::View::View *view = desktop;
708     gint verbsToUse[] = {
709         // disabled until we have icons for them:
710         //find
711         //SP_VERB_EDIT_TILE,
712         //SP_VERB_EDIT_UNTILE,
713         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
714         SP_VERB_DIALOG_DISPLAY,
715         SP_VERB_DIALOG_FILL_STROKE,
716         SP_VERB_DIALOG_NAMEDVIEW,
717         SP_VERB_DIALOG_TEXT,
718         SP_VERB_DIALOG_XML_EDITOR,
719         SP_VERB_EDIT_CLONE,
720         SP_VERB_EDIT_COPY,
721         SP_VERB_EDIT_CUT,
722         SP_VERB_EDIT_DUPLICATE,
723         SP_VERB_EDIT_PASTE,
724         SP_VERB_EDIT_REDO,
725         SP_VERB_EDIT_UNDO,
726         SP_VERB_EDIT_UNLINK_CLONE,
727         SP_VERB_FILE_EXPORT,
728         SP_VERB_FILE_IMPORT,
729         SP_VERB_FILE_NEW,
730         SP_VERB_FILE_OPEN,
731         SP_VERB_FILE_PRINT,
732         SP_VERB_FILE_SAVE,
733         SP_VERB_OBJECT_TO_CURVE,
734         SP_VERB_SELECTION_GROUP,
735         SP_VERB_SELECTION_OUTLINE,
736         SP_VERB_SELECTION_UNGROUP,
737         SP_VERB_ZOOM_1_1,
738         SP_VERB_ZOOM_1_2,
739         SP_VERB_ZOOM_2_1,
740         SP_VERB_ZOOM_DRAWING,
741         SP_VERB_ZOOM_IN,
742         SP_VERB_ZOOM_NEXT,
743         SP_VERB_ZOOM_OUT,
744         SP_VERB_ZOOM_PAGE,
745         SP_VERB_ZOOM_PAGE_WIDTH,
746         SP_VERB_ZOOM_PREV,
747         SP_VERB_ZOOM_SELECTION,
748     };
750     Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
752     static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
753     Glib::RefPtr<Gtk::ActionGroup> mainActions;
754     if ( groups.find(desktop) != groups.end() ) {
755         mainActions = groups[desktop];
756     }
758     if ( !mainActions ) {
759         mainActions = Gtk::ActionGroup::create("main");
760         groups[desktop] = mainActions;
761     }
763     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
764         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
765         if ( verb ) {
766             if (!mainActions->get_action(verb->get_id())) {
767                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
768                 mainActions->add(Glib::wrap(act));
769             }
770         }
771     }
773     if ( !mainActions->get_action("ToolZoom") ) {
774         GtkTooltips *tt = gtk_tooltips_new();
775         for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
776             Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
777             if ( va ) {
778                 mainActions->add(va);
779                 if ( i == 0 ) {
780                     va->set_active(true);
781                 }
782             }
783         }
784     }
787     return mainActions;
791 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
793     gtk_widget_set_size_request( widget,
794                                  widget->allocation.width,
795                                  widget->allocation.height );
798 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
800     gtk_widget_set_size_request( widget, -1, -1 );
805 GtkWidget *
806 sp_tool_toolbox_new()
808     GtkTooltips *tt = gtk_tooltips_new();
809     GtkWidget* tb = gtk_toolbar_new();
810     gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
811     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
813     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
814     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
816     gtk_widget_set_sensitive(tb, FALSE);
818     GtkWidget *hb = gtk_handle_box_new();
819     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
820     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
821     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
823     gtk_container_add(GTK_CONTAINER(hb), tb);
824     gtk_widget_show(GTK_WIDGET(tb));
826     sigc::connection* conn = new sigc::connection;
827     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
829     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
830     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
832     return hb;
835 GtkWidget *
836 sp_aux_toolbox_new()
838     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
840     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
842     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
844     gtk_widget_set_sensitive(tb, FALSE);
846     GtkWidget *hb = gtk_handle_box_new();
847     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
848     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
849     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
851     gtk_container_add(GTK_CONTAINER(hb), tb);
852     gtk_widget_show(GTK_WIDGET(tb));
854     sigc::connection* conn = new sigc::connection;
855     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
857     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
858     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
860     return hb;
863 //####################################
864 //# Commands Bar
865 //####################################
867 GtkWidget *
868 sp_commands_toolbox_new()
870     GtkWidget *tb = gtk_toolbar_new();
872     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
873     gtk_widget_set_sensitive(tb, FALSE);
875     GtkWidget *hb = gtk_handle_box_new();
876     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
877     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
878     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
880     gtk_container_add(GTK_CONTAINER(hb), tb);
881     gtk_widget_show(GTK_WIDGET(tb));
883     sigc::connection* conn = new sigc::connection;
884     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
886     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
887     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
889     return hb;
893 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
894                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
895                                                        gchar const *path, gchar const *data, gdouble def,
896                                                        GtkWidget *focusTarget,
897                                                        GtkWidget *us,
898                                                        GObject *dataKludge,
899                                                        gboolean altx, gchar const *altx_mark,
900                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
901                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
902                                                        void (*callback)(GtkAdjustment *, GObject *),
903                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
905     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
906                                                              lower, upper, step, page, page ) );
907     if (us) {
908         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
909     }
911     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
913     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
914     if ( shortLabel ) {
915         g_object_set( act, "short_label", shortLabel, NULL );
916     }
918     if ( (descrCount > 0) && descrLabels && descrValues ) {
919         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
920     }
922     if ( focusTarget ) {
923         ege_adjustment_action_set_focuswidget( act, focusTarget );
924     }
926     if ( altx && altx_mark ) {
927         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
928     }
930     if ( dataKludge ) {
931         g_object_set_data( dataKludge, data, adj );
932     }
934     // Using a cast just to make sure we pass in the right kind of function pointer
935     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
937     return act;
941 //####################################
942 //# node editing callbacks
943 //####################################
945 /**
946  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
947  */
948 static ShapeEditor *get_current_shape_editor()
950     if (!SP_ACTIVE_DESKTOP) {
951         return NULL;
952     }
954     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
956     if (!SP_IS_NODE_CONTEXT(event_context)) {
957         return NULL;
958     }
960     return SP_NODE_CONTEXT(event_context)->shape_editor;
964 void
965 sp_node_path_edit_add(void)
967     ShapeEditor *shape_editor = get_current_shape_editor();
968     if (shape_editor) shape_editor->add_node();
971 void
972 sp_node_path_edit_delete(void)
974     ShapeEditor *shape_editor = get_current_shape_editor();
975     if (shape_editor) shape_editor->delete_nodes_preserving_shape();
978 void
979 sp_node_path_edit_delete_segment(void)
981     ShapeEditor *shape_editor = get_current_shape_editor();
982     if (shape_editor) shape_editor->delete_segment();
985 void
986 sp_node_path_edit_break(void)
988     ShapeEditor *shape_editor = get_current_shape_editor();
989     if (shape_editor) shape_editor->break_at_nodes();
992 void
993 sp_node_path_edit_join(void)
995     ShapeEditor *shape_editor = get_current_shape_editor();
996     if (shape_editor) shape_editor->join_nodes();
999 void
1000 sp_node_path_edit_join_segment(void)
1002     ShapeEditor *shape_editor = get_current_shape_editor();
1003     if (shape_editor) shape_editor->join_segments();
1006 void
1007 sp_node_path_edit_toline(void)
1009     ShapeEditor *shape_editor = get_current_shape_editor();
1010     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1013 void
1014 sp_node_path_edit_tocurve(void)
1016     ShapeEditor *shape_editor = get_current_shape_editor();
1017     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1020 void
1021 sp_node_path_edit_cusp(void)
1023     ShapeEditor *shape_editor = get_current_shape_editor();
1024     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1027 void
1028 sp_node_path_edit_smooth(void)
1030     ShapeEditor *shape_editor = get_current_shape_editor();
1031     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1034 void
1035 sp_node_path_edit_symmetrical(void)
1037     ShapeEditor *shape_editor = get_current_shape_editor();
1038     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1041 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1042     bool show = gtk_toggle_action_get_active( act );
1043     prefs_set_int_attribute ("tools.nodes", "show_handles",  show ? 1 : 0);
1044     ShapeEditor *shape_editor = get_current_shape_editor();
1045     if (shape_editor) shape_editor->show_handles(show);
1048 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1049     bool show = gtk_toggle_action_get_active( act );
1050     prefs_set_int_attribute ("tools.nodes", "show_helperpath",  show ? 1 : 0);
1051     ShapeEditor *shape_editor = get_current_shape_editor();
1052     if (shape_editor) shape_editor->show_helperpath(show);
1055 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1056     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1059 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1060     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1063 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1064     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1067 /* is called when the node selection is modified */
1068 static void
1069 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1071     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1072     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1073     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1074     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1076     // quit if run by the attr_changed listener
1077     if (g_object_get_data( tbl, "freeze" )) {
1078         return;
1079     }
1081     // in turn, prevent listener from responding
1082     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1084     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1085     SPUnit const *unit = tracker->getActiveUnit();
1087     ShapeEditor *shape_editor = get_current_shape_editor();
1088     if (shape_editor && shape_editor->has_nodepath()) {
1089         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1090         int n_selected = 0;
1091         if (nodepath) {
1092             n_selected = nodepath->numSelected();
1093         }
1095         if (n_selected == 0) {
1096             gtk_action_set_sensitive(xact, FALSE);
1097             gtk_action_set_sensitive(yact, FALSE);
1098         } else {
1099             gtk_action_set_sensitive(xact, TRUE);
1100             gtk_action_set_sensitive(yact, TRUE);
1101             NR::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1102             NR::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1104             if (n_selected == 1) {
1105                 NR::Point sel_node = nodepath->singleSelectedCoords();
1106                 if (oldx != sel_node[NR::X] || oldy != sel_node[NR::Y]) {
1107                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[NR::X], *unit));
1108                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[NR::Y], *unit));
1109                 }
1110             } else {
1111                 NR::Maybe<NR::Coord> x = sp_node_selected_common_coord(nodepath, NR::X);
1112                 NR::Maybe<NR::Coord> y = sp_node_selected_common_coord(nodepath, NR::Y);
1113                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1114                     /* Note: Currently x and y will always have a value, even if the coordinates of the
1115                        selected nodes don't coincide (in this case we use the coordinates of the center
1116                        of the bounding box). So the entries are never set to zero. */
1117                     // FIXME: Maybe we should clear the entry if several nodes are selected
1118                     //        instead of providing a kind of average value
1119                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1120                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1121                 }
1122             }
1123         }
1124     } else {
1125         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1126         gtk_action_set_sensitive(xact, FALSE);
1127         gtk_action_set_sensitive(yact, FALSE);
1128     }
1130     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1133 static void
1134 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1136     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1138     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1139     SPUnit const *unit = tracker->getActiveUnit();
1141     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1142         prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
1143     }
1145     // quit if run by the attr_changed listener
1146     if (g_object_get_data( tbl, "freeze" )) {
1147         return;
1148     }
1150     // in turn, prevent listener from responding
1151     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1153     ShapeEditor *shape_editor = get_current_shape_editor();
1154     if (shape_editor && shape_editor->has_nodepath()) {
1155         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1156         if (!strcmp(value_name, "x")) {
1157             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::X);
1158         }
1159         if (!strcmp(value_name, "y")) {
1160             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::Y);
1161         }
1162     }
1164     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1167 static void
1168 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1170     sp_node_path_value_changed(adj, tbl, "x");
1173 static void
1174 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1176     sp_node_path_value_changed(adj, tbl, "y");
1179 void
1180 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1182     {
1183     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1184     SPItem *item = selection->singleItem();
1185     if (item && SP_IS_LPE_ITEM(item)) {
1186        if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1187            gtk_action_set_sensitive(w, TRUE);
1188        } else {
1189            gtk_action_set_sensitive(w, FALSE);
1190        }
1191     } else {
1192        gtk_action_set_sensitive(w, FALSE);
1193     }
1194     }
1196     {
1197     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1198     SPItem *item = selection->singleItem();
1199     if (item && item->clip_ref && item->clip_ref->getObject()) {
1200        gtk_action_set_sensitive(w, TRUE);
1201     } else {
1202        gtk_action_set_sensitive(w, FALSE);
1203     }
1204     }
1206     {
1207     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1208     SPItem *item = selection->singleItem();
1209     if (item && item->mask_ref && item->mask_ref->getObject()) {
1210        gtk_action_set_sensitive(w, TRUE);
1211     } else {
1212        gtk_action_set_sensitive(w, FALSE);
1213     }
1214     }
1217 void
1218 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1220     sp_node_toolbox_sel_changed (selection, tbl);
1225 //################################
1226 //##    Node Editing Toolbox    ##
1227 //################################
1229 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1231     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1232     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1233     g_object_set_data( holder, "tracker", tracker );
1235     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
1237     {
1238         InkAction* inky = ink_action_new( "NodeInsertAction",
1239                                           _("Insert node"),
1240                                           _("Insert new nodes into selected segments"),
1241                                           "node_insert",
1242                                           secondarySize );
1243         g_object_set( inky, "short_label", _("Insert"), NULL );
1244         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1245         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1246     }
1248     {
1249         InkAction* inky = ink_action_new( "NodeDeleteAction",
1250                                           _("Delete node"),
1251                                           _("Delete selected nodes"),
1252                                           "node_delete",
1253                                           secondarySize );
1254         g_object_set( inky, "short_label", _("Delete"), NULL );
1255         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1256         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1257     }
1259     {
1260         InkAction* inky = ink_action_new( "NodeJoinAction",
1261                                           _("Join endnodes"),
1262                                           _("Join selected endnodes"),
1263                                           "node_join",
1264                                           secondarySize );
1265         g_object_set( inky, "short_label", _("Join"), NULL );
1266         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1267         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1268     }
1270     {
1271         InkAction* inky = ink_action_new( "NodeBreakAction",
1272                                           _("Break nodes"),
1273                                           _("Break path at selected nodes"),
1274                                           "node_break",
1275                                           secondarySize );
1276         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1277         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1278     }
1281     {
1282         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1283                                           _("Join with segment"),
1284                                           _("Join selected endnodes with a new segment"),
1285                                           "node_join_segment",
1286                                           secondarySize );
1287         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1288         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1289     }
1291     {
1292         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1293                                           _("Delete segment"),
1294                                           _("Delete segment between two non-endpoint nodes"),
1295                                           "node_delete_segment",
1296                                           secondarySize );
1297         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1298         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1299     }
1301     {
1302         InkAction* inky = ink_action_new( "NodeCuspAction",
1303                                           _("Node Cusp"),
1304                                           _("Make selected nodes corner"),
1305                                           "node_cusp",
1306                                           secondarySize );
1307         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1308         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1309     }
1311     {
1312         InkAction* inky = ink_action_new( "NodeSmoothAction",
1313                                           _("Node Smooth"),
1314                                           _("Make selected nodes smooth"),
1315                                           "node_smooth",
1316                                           secondarySize );
1317         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1318         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1319     }
1321     {
1322         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1323                                           _("Node Symmetric"),
1324                                           _("Make selected nodes symmetric"),
1325                                           "node_symmetric",
1326                                           secondarySize );
1327         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1328         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1329     }
1331     {
1332         InkAction* inky = ink_action_new( "NodeLineAction",
1333                                           _("Node Line"),
1334                                           _("Make selected segments lines"),
1335                                           "node_line",
1336                                           secondarySize );
1337         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1338         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1339     }
1341     {
1342         InkAction* inky = ink_action_new( "NodeCurveAction",
1343                                           _("Node Curve"),
1344                                           _("Make selected segments curves"),
1345                                           "node_curve",
1346                                           secondarySize );
1347         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1348         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1349     }
1351     {
1352         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1353                                                       _("Show Handles"),
1354                                                       _("Show the Bezier handles of selected nodes"),
1355                                                       "nodes_show_handles",
1356                                                       Inkscape::ICON_SIZE_DECORATION );
1357         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1358         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1359         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1360     }
1362     {
1363         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1364                                                       _("Show Outline"),
1365                                                       _("Show the outline of the path"),
1366                                                       "nodes_show_helperpath",
1367                                                       Inkscape::ICON_SIZE_DECORATION );
1368         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1369         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1370         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_helperpath", 0 ) );
1371     }
1373     {
1374         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1375                                           _("Next path effect parameter"),
1376                                           _("Show next path effect parameter for editing"),
1377                                           "edit_next_parameter",
1378                                           Inkscape::ICON_SIZE_DECORATION );
1379         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1380         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1381         g_object_set_data( holder, "nodes_lpeedit", inky);
1382     }
1384     {
1385         InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1386                                           _("Edit clipping path"),
1387                                           _("Edit the clipping path of the object"),
1388                                           "nodeedit-clippath",
1389                                           Inkscape::ICON_SIZE_DECORATION );
1390         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1391         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1392         g_object_set_data( holder, "nodes_clippathedit", inky);
1393     }
1395     {
1396         InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1397                                           _("Edit mask path"),
1398                                           _("Edit the mask of the object"),
1399                                           "nodeedit-mask",
1400                                           Inkscape::ICON_SIZE_DECORATION );
1401         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1402         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1403         g_object_set_data( holder, "nodes_maskedit", inky);
1404     }
1406     /* X coord of selected node(s) */
1407     {
1408         EgeAdjustmentAction* eact = 0;
1409         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1410         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1411         eact = create_adjustment_action( "NodeXAction",
1412                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1413                                          "tools.nodes", "Xcoord", 0,
1414                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1415                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1416                                          labels, values, G_N_ELEMENTS(labels),
1417                                          sp_node_path_x_value_changed );
1418         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1419         g_object_set_data( holder, "nodes_x_action", eact );
1420         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1421         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1422     }
1424     /* Y coord of selected node(s) */
1425     {
1426         EgeAdjustmentAction* eact = 0;
1427         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1428         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1429         eact = create_adjustment_action( "NodeYAction",
1430                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1431                                          "tools.nodes", "Ycoord", 0,
1432                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1433                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1434                                          labels, values, G_N_ELEMENTS(labels),
1435                                          sp_node_path_y_value_changed );
1436         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1437         g_object_set_data( holder, "nodes_y_action", eact );
1438         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1439         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1440     }
1442     // add the units menu
1443     {
1444         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1445         gtk_action_group_add_action( mainActions, act );
1446     }
1449     sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1451     //watch selection
1452     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1454     sigc::connection *c_selection_changed =
1455         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1456                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1457     pool->add_connection ("selection-changed", c_selection_changed);
1459     sigc::connection *c_selection_modified =
1460         new sigc::connection (sp_desktop_selection (desktop)->connectModified
1461                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1462     pool->add_connection ("selection-modified", c_selection_modified);
1464     sigc::connection *c_subselection_changed =
1465         new sigc::connection (desktop->connectToolSubselectionChanged
1466                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1467     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1469     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1471     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1472 } // end of sp_node_toolbox_prep()
1475 //########################
1476 //##    Zoom Toolbox    ##
1477 //########################
1479 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1481     // no custom GtkAction setup needed
1482 } // end of sp_zoom_toolbox_prep()
1484 void
1485 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1487     toolbox_set_desktop(toolbox,
1488                         desktop,
1489                         setup_tool_toolbox,
1490                         update_tool_toolbox,
1491                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1492                                                                          "event_context_connection")));
1496 void
1497 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1499     toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1500                         desktop,
1501                         setup_aux_toolbox,
1502                         update_aux_toolbox,
1503                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1504                                                                          "event_context_connection")));
1507 void
1508 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1510     toolbox_set_desktop(toolbox,
1511                         desktop,
1512                         setup_commands_toolbox,
1513                         update_commands_toolbox,
1514                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1515                                                                          "event_context_connection")));
1518 static void
1519 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1521     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1522     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1524     if (old_desktop) {
1525         GList *children, *iter;
1527         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1528         for ( iter = children ; iter ; iter = iter->next ) {
1529             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1530         }
1531         g_list_free(children);
1532     }
1534     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1536     if (desktop) {
1537         gtk_widget_set_sensitive(toolbox, TRUE);
1538         setup_func(toolbox, desktop);
1539         update_func(desktop, desktop->event_context, toolbox);
1540         *conn = desktop->connectEventContextChanged
1541             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1542     } else {
1543         gtk_widget_set_sensitive(toolbox, FALSE);
1544     }
1546 } // end of toolbox_set_desktop()
1549 static void
1550 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1552     gchar const * descr =
1553         "<ui>"
1554         "  <toolbar name='ToolToolbar'>"
1555         "    <toolitem action='ToolSelector' />"
1556         "    <toolitem action='ToolNode' />"
1557         "    <toolitem action='ToolTweak' />"
1558         "    <toolitem action='ToolZoom' />"
1559         "    <toolitem action='ToolRect' />"
1560         "    <toolitem action='Tool3DBox' />"
1561         "    <toolitem action='ToolArc' />"
1562         "    <toolitem action='ToolStar' />"
1563         "    <toolitem action='ToolSpiral' />"
1564         "    <toolitem action='ToolPencil' />"
1565         "    <toolitem action='ToolPen' />"
1566         "    <toolitem action='ToolCalligraphic' />"
1567         "    <toolitem action='ToolEraser' />"
1568         "    <toolitem action='ToolPaintBucket' />"
1569         "    <toolitem action='ToolText' />"
1570         "    <toolitem action='ToolConnector' />"
1571         "    <toolitem action='ToolGradient' />"
1572         "    <toolitem action='ToolDropper' />"
1573         "  </toolbar>"
1574         "</ui>";
1575     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1576     GtkUIManager* mgr = gtk_ui_manager_new();
1577     GError* errVal = 0;
1579     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1580     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1582     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" );
1583     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1584         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1585     }
1586     Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1587     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1589     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
1590     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1592     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1594     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1595     if ( child ) {
1596         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1597     }
1599     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1600 //     Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1604 static void
1605 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1607     gchar const *const tname = ( eventcontext
1608                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1609                                  : NULL );
1610     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1612     for (int i = 0 ; tools[i].type_name ; i++ ) {
1613         Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1614         if ( act ) {
1615             bool setActive = tname && !strcmp(tname, tools[i].type_name);
1616             Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1617             if ( verbAct ) {
1618                 verbAct->set_active(setActive);
1619             }
1620         }
1621     }
1624 static void
1625 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1627     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1628     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1629     GtkUIManager* mgr = gtk_ui_manager_new();
1630     GError* errVal = 0;
1631     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1632     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1634     std::map<std::string, GtkWidget*> dataHolders;
1636     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1637         if ( aux_toolboxes[i].prep_func ) {
1638             // converted to GtkActions and UIManager
1640             GtkWidget* kludge = gtk_toolbar_new();
1641             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1642             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1643             dataHolders[aux_toolboxes[i].type_name] = kludge;
1644             aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1645         } else {
1647             GtkWidget *sub_toolbox = 0;
1648             if (aux_toolboxes[i].create_func == NULL)
1649                 sub_toolbox = sp_empty_toolbox_new(desktop);
1650             else {
1651                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1652             }
1654             gtk_size_group_add_widget( grouper, sub_toolbox );
1656             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1657             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1659         }
1660     }
1662     // Second pass to create toolbars *after* all GtkActions are created
1663     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1664         if ( aux_toolboxes[i].prep_func ) {
1665             // converted to GtkActions and UIManager
1667             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1669             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1670             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1672             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1673             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1674             g_free( tmp );
1675             tmp = 0;
1677             Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1678             if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1679                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1680             }
1681             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1684             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1686             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1687                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1688                 swatch->setDesktop( desktop );
1689                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1690                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1691                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1692                 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 );
1693             }
1695             gtk_widget_show_all( holder );
1696             sp_set_font_size_smaller( holder );
1698             gtk_size_group_add_widget( grouper, holder );
1700             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1701             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1702         }
1703     }
1705     g_object_unref( G_OBJECT(grouper) );
1708 static void
1709 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1711     gchar const *tname = ( eventcontext
1712                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1713                            : NULL );
1714     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1715         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1716         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1717             gtk_widget_show_all(sub_toolbox);
1718             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1719         } else {
1720             gtk_widget_hide(sub_toolbox);
1721         }
1722     }
1725 static void
1726 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1728     gchar const * descr =
1729         "<ui>"
1730         "  <toolbar name='CommandsToolbar'>"
1731         "    <toolitem action='FileNew' />"
1732         "    <toolitem action='FileOpen' />"
1733         "    <toolitem action='FileSave' />"
1734         "    <toolitem action='FilePrint' />"
1735         "    <separator />"
1736         "    <toolitem action='FileImport' />"
1737         "    <toolitem action='FileExport' />"
1738         "    <separator />"
1739         "    <toolitem action='EditUndo' />"
1740         "    <toolitem action='EditRedo' />"
1741         "    <separator />"
1742         "    <toolitem action='EditCopy' />"
1743         "    <toolitem action='EditCut' />"
1744         "    <toolitem action='EditPaste' />"
1745         "    <separator />"
1746         "    <toolitem action='ZoomSelection' />"
1747         "    <toolitem action='ZoomDrawing' />"
1748         "    <toolitem action='ZoomPage' />"
1749         "    <separator />"
1750         "    <toolitem action='EditDuplicate' />"
1751         "    <toolitem action='EditClone' />"
1752         "    <toolitem action='EditUnlinkClone' />"
1753         "    <separator />"
1754         "    <toolitem action='SelectionGroup' />"
1755         "    <toolitem action='SelectionUnGroup' />"
1756         "    <separator />"
1757         "    <toolitem action='DialogFillStroke' />"
1758         "    <toolitem action='DialogText' />"
1759         "    <toolitem action='DialogXMLEditor' />"
1760         "    <toolitem action='DialogAlignDistribute' />"
1761         "    <separator />"
1762         "    <toolitem action='DialogPreferences' />"
1763         "    <toolitem action='DialogDocumentProperties' />"
1764         "  </toolbar>"
1765         "</ui>";
1766     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1769     GtkUIManager* mgr = gtk_ui_manager_new();
1770     GError* errVal = 0;
1772     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1773     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1775     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1776     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1777         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1778     }
1780     Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1781     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1783     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1784     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1787     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1789     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1790     if ( child ) {
1791         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1792     }
1794     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1797 static void
1798 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1802 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1804     gtk_widget_show(toolbox_toplevel);
1805     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1807     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1808     if (!shown_toolbox) {
1809         return;
1810     }
1811     gtk_widget_show(toolbox);
1813     gtk_widget_show_all(shown_toolbox);
1816 static GtkWidget *
1817 sp_empty_toolbox_new(SPDesktop *desktop)
1819     GtkWidget *tbl = gtk_toolbar_new();
1820     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1821     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1823     gtk_widget_show_all(tbl);
1824     sp_set_font_size_smaller (tbl);
1826     return tbl;
1829 #define MODE_LABEL_WIDTH 70
1831 //########################
1832 //##       Star         ##
1833 //########################
1835 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1837     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1839     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1840         // do not remember prefs if this call is initiated by an undo change, because undoing object
1841         // creation sets bogus values to its attributes before it is deleted
1842         prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1843     }
1845     // quit if run by the attr_changed listener
1846     if (g_object_get_data( dataKludge, "freeze" )) {
1847         return;
1848     }
1850     // in turn, prevent listener from responding
1851     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1853     bool modmade = false;
1855     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1856     GSList const *items = selection->itemList();
1857     for (; items != NULL; items = items->next) {
1858         if (SP_IS_STAR((SPItem *) items->data)) {
1859             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1860             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1861             sp_repr_set_svg_double(repr, "sodipodi:arg2",
1862                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1863                                     + M_PI / (gint)adj->value));
1864             SP_OBJECT((SPItem *) items->data)->updateRepr();
1865             modmade = true;
1866         }
1867     }
1868     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1869                                    _("Star: Change number of corners"));
1871     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1874 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1876     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1878     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1879         prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1880     }
1882     // quit if run by the attr_changed listener
1883     if (g_object_get_data( dataKludge, "freeze" )) {
1884         return;
1885     }
1887     // in turn, prevent listener from responding
1888     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1890     bool modmade = false;
1891     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1892     GSList const *items = selection->itemList();
1893     for (; items != NULL; items = items->next) {
1894         if (SP_IS_STAR((SPItem *) items->data)) {
1895             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1897             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1898             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1899             if (r2 < r1) {
1900                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1901             } else {
1902                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1903             }
1905             SP_OBJECT((SPItem *) items->data)->updateRepr();
1906             modmade = true;
1907         }
1908     }
1910     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1911                                    _("Star: Change spoke ratio"));
1913     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1916 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1918     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1919     bool flat = ege_select_one_action_get_active( act ) == 0;
1921     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1922         prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1923                                     flat ? "true" : "false" );
1924     }
1926     // quit if run by the attr_changed listener
1927     if (g_object_get_data( dataKludge, "freeze" )) {
1928         return;
1929     }
1931     // in turn, prevent listener from responding
1932     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1934     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1935     GSList const *items = selection->itemList();
1936     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1937     bool modmade = false;
1939     if ( prop_action ) {
1940         gtk_action_set_sensitive( prop_action, !flat );
1941     }
1943     for (; items != NULL; items = items->next) {
1944         if (SP_IS_STAR((SPItem *) items->data)) {
1945             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1946             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1947             SP_OBJECT((SPItem *) items->data)->updateRepr();
1948             modmade = true;
1949         }
1950     }
1952     if (modmade) {
1953         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1954                          flat ? _("Make polygon") : _("Make star"));
1955     }
1957     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1960 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1962     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1964     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1965         prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1966     }
1968     // quit if run by the attr_changed listener
1969     if (g_object_get_data( dataKludge, "freeze" )) {
1970         return;
1971     }
1973     // in turn, prevent listener from responding
1974     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1976     bool modmade = false;
1978     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1979     GSList const *items = selection->itemList();
1980     for (; items != NULL; items = items->next) {
1981         if (SP_IS_STAR((SPItem *) items->data)) {
1982             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1983             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1984             SP_OBJECT(items->data)->updateRepr();
1985             modmade = true;
1986         }
1987     }
1988     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1989                                    _("Star: Change rounding"));
1991     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1994 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1996     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1998     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1999         prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
2000     }
2002     // quit if run by the attr_changed listener
2003     if (g_object_get_data( dataKludge, "freeze" )) {
2004         return;
2005     }
2007     // in turn, prevent listener from responding
2008     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2010     bool modmade = false;
2012     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2013     GSList const *items = selection->itemList();
2014     for (; items != NULL; items = items->next) {
2015         if (SP_IS_STAR((SPItem *) items->data)) {
2016             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2017             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2018             SP_OBJECT(items->data)->updateRepr();
2019             modmade = true;
2020         }
2021     }
2022     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2023                                    _("Star: Change randomization"));
2025     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2029 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2030                                        gchar const */*old_value*/, gchar const */*new_value*/,
2031                                        bool /*is_interactive*/, gpointer data)
2033     GtkWidget *tbl = GTK_WIDGET(data);
2035     // quit if run by the _changed callbacks
2036     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2037         return;
2038     }
2040     // in turn, prevent callbacks from responding
2041     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2043     GtkAdjustment *adj = 0;
2045     gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2046     bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2048     if (!strcmp(name, "inkscape:randomized")) {
2049         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2050         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2051     } else if (!strcmp(name, "inkscape:rounded")) {
2052         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2053         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2054     } else if (!strcmp(name, "inkscape:flatsided")) {
2055         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2056         char const *flatsides = repr->attribute("inkscape:flatsided");
2057         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2058         if ( flatsides && !strcmp(flatsides,"false") ) {
2059             ege_select_one_action_set_active( flat_action, 1 );
2060             gtk_action_set_sensitive( prop_action, TRUE );
2061         } else {
2062             ege_select_one_action_set_active( flat_action, 0 );
2063             gtk_action_set_sensitive( prop_action, FALSE );
2064         }
2065     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2066         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2067         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2068         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2069         if (r2 < r1) {
2070             gtk_adjustment_set_value(adj, r2/r1);
2071         } else {
2072             gtk_adjustment_set_value(adj, r1/r2);
2073         }
2074     } else if (!strcmp(name, "sodipodi:sides")) {
2075         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2076         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2077     }
2079     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2083 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2085     NULL, /* child_added */
2086     NULL, /* child_removed */
2087     star_tb_event_attr_changed,
2088     NULL, /* content_changed */
2089     NULL  /* order_changed */
2090 };
2093 /**
2094  *  \param selection Should not be NULL.
2095  */
2096 static void
2097 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2099     int n_selected = 0;
2100     Inkscape::XML::Node *repr = NULL;
2102     purge_repr_listener( tbl, tbl );
2104     for (GSList const *items = selection->itemList();
2105          items != NULL;
2106          items = items->next)
2107     {
2108         if (SP_IS_STAR((SPItem *) items->data)) {
2109             n_selected++;
2110             repr = SP_OBJECT_REPR((SPItem *) items->data);
2111         }
2112     }
2114     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2116     if (n_selected == 0) {
2117         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2118     } else if (n_selected == 1) {
2119         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2121         if (repr) {
2122             g_object_set_data( tbl, "repr", repr );
2123             Inkscape::GC::anchor(repr);
2124             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2125             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2126         }
2127     } else {
2128         // FIXME: implement averaging of all parameters for multiple selected stars
2129         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2130         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2131     }
2135 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2137     // FIXME: in this and all other _default functions, set some flag telling the value_changed
2138     // callbacks to lump all the changes for all selected objects in one undo step
2140     GtkAdjustment *adj = 0;
2142     // fixme: make settable in prefs!
2143     gint mag = 5;
2144     gdouble prop = 0.5;
2145     gboolean flat = FALSE;
2146     gdouble randomized = 0;
2147     gdouble rounded = 0;
2149     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2150     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2152     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2153     gtk_action_set_sensitive( sb2, !flat );
2155     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2156     gtk_adjustment_set_value(adj, mag);
2157     gtk_adjustment_value_changed(adj);
2159     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2160     gtk_adjustment_set_value(adj, prop);
2161     gtk_adjustment_value_changed(adj);
2163     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2164     gtk_adjustment_set_value(adj, rounded);
2165     gtk_adjustment_value_changed(adj);
2167     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2168     gtk_adjustment_set_value(adj, randomized);
2169     gtk_adjustment_value_changed(adj);
2173 void
2174 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2176     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2177     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2178     GtkWidget *l = gtk_label_new(NULL);
2179     gtk_label_set_markup(GTK_LABEL(l), title);
2180     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2181     if ( GTK_IS_TOOLBAR(tbl) ) {
2182         gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2183     } else {
2184         gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2185     }
2186     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2190 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2192     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2194     {
2195         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2196         ege_output_action_set_use_markup( act, TRUE );
2197         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2198         g_object_set_data( holder, "mode_action", act );
2199     }
2201     {
2202         EgeAdjustmentAction* eact = 0;
2203         gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2204         bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2206         /* Flatsided checkbox */
2207         {
2208             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2210             GtkTreeIter iter;
2211             gtk_list_store_append( model, &iter );
2212             gtk_list_store_set( model, &iter,
2213                                 0, _("Polygon"),
2214                                 1, _("Regular polygon (with one handle) instead of a star"),
2215                                 2, "star_flat",
2216                                 -1 );
2218             gtk_list_store_append( model, &iter );
2219             gtk_list_store_set( model, &iter,
2220                                 0, _("Star"),
2221                                 1, _("Star instead of a regular polygon (with one handle)"),
2222                                 2, "star_angled",
2223                                 -1 );
2225             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2226             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2227             g_object_set_data( holder, "flat_action", act );
2229             ege_select_one_action_set_appearance( act, "full" );
2230             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2231             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2232             ege_select_one_action_set_icon_column( act, 2 );
2233             ege_select_one_action_set_icon_size( act, secondarySize );
2234             ege_select_one_action_set_tooltip_column( act, 1  );
2236             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2237             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2238         }
2240         /* Magnitude */
2241         {
2242         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2243         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2244         eact = create_adjustment_action( "MagnitudeAction",
2245                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2246                                          "tools.shapes.star", "magnitude", 3,
2247                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2248                                          3, 1024, 1, 5,
2249                                          labels, values, G_N_ELEMENTS(labels),
2250                                          sp_stb_magnitude_value_changed,
2251                                          1.0, 0 );
2252         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2253         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2254         }
2256         /* Spoke ratio */
2257         {
2258         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2259         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2260         eact = create_adjustment_action( "SpokeAction",
2261                                          _("Spoke ratio"), _("Spoke ratio:"),
2262                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2263                                          // Base radius is the same for the closest handle.
2264                                          _("Base radius to tip radius ratio"),
2265                                          "tools.shapes.star", "proportion", 0.5,
2266                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2267                                          0.01, 1.0, 0.01, 0.1,
2268                                          labels, values, G_N_ELEMENTS(labels),
2269                                          sp_stb_proportion_value_changed );
2270         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2271         g_object_set_data( holder, "prop_action", eact );
2272         }
2274         if ( !isFlatSided ) {
2275             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2276         } else {
2277             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2278         }
2280         /* Roundedness */
2281         {
2282         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2283         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2284         eact = create_adjustment_action( "RoundednessAction",
2285                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2286                                          "tools.shapes.star", "rounded", 0.0,
2287                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2288                                          -10.0, 10.0, 0.01, 0.1,
2289                                          labels, values, G_N_ELEMENTS(labels),
2290                                          sp_stb_rounded_value_changed );
2291         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2292         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2293         }
2295         /* Randomization */
2296         {
2297         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2298         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2299         eact = create_adjustment_action( "RandomizationAction",
2300                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2301                                          "tools.shapes.star", "randomized", 0.0,
2302                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2303                                          -10.0, 10.0, 0.001, 0.01,
2304                                          labels, values, G_N_ELEMENTS(labels),
2305                                          sp_stb_randomized_value_changed, 0.1, 3 );
2306         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2307         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2308         }
2309     }
2311     {
2312         /* Reset */
2313         {
2314             GtkAction* act = gtk_action_new( "StarResetAction",
2315                                              _("Defaults"),
2316                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2317                                              GTK_STOCK_CLEAR );
2318             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2319             gtk_action_group_add_action( mainActions, act );
2320             gtk_action_set_sensitive( act, TRUE );
2321         }
2322     }
2324     sigc::connection *connection = new sigc::connection(
2325         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2326         );
2327     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2328     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2332 //########################
2333 //##       Rect         ##
2334 //########################
2336 static void sp_rtb_sensitivize( GObject *tbl )
2338     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2339     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2340     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2342     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2343         gtk_action_set_sensitive( not_rounded, FALSE );
2344     } else {
2345         gtk_action_set_sensitive( not_rounded, TRUE );
2346     }
2350 static void
2351 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2352                           void (*setter)(SPRect *, gdouble))
2354     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2356     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2357     SPUnit const *unit = tracker->getActiveUnit();
2359     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2360         prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2361     }
2363     // quit if run by the attr_changed listener
2364     if (g_object_get_data( tbl, "freeze" )) {
2365         return;
2366     }
2368     // in turn, prevent listener from responding
2369     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2371     bool modmade = false;
2372     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2373     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2374         if (SP_IS_RECT(items->data)) {
2375             if (adj->value != 0) {
2376                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2377             } else {
2378                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2379             }
2380             modmade = true;
2381         }
2382     }
2384     sp_rtb_sensitivize( tbl );
2386     if (modmade) {
2387         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2388                                    _("Change rectangle"));
2389     }
2391     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2394 static void
2395 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2397     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2400 static void
2401 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2403     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2406 static void
2407 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2409     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2412 static void
2413 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2415     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2420 static void
2421 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2423     GtkAdjustment *adj = 0;
2425     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2426     gtk_adjustment_set_value(adj, 0.0);
2427     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2428     gtk_adjustment_value_changed(adj);
2430     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2431     gtk_adjustment_set_value(adj, 0.0);
2432     gtk_adjustment_value_changed(adj);
2434     sp_rtb_sensitivize( obj );
2437 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2438                                        gchar const */*old_value*/, gchar const */*new_value*/,
2439                                        bool /*is_interactive*/, gpointer data)
2441     GObject *tbl = G_OBJECT(data);
2443     // quit if run by the _changed callbacks
2444     if (g_object_get_data( tbl, "freeze" )) {
2445         return;
2446     }
2448     // in turn, prevent callbacks from responding
2449     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2451     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2452     SPUnit const *unit = tracker->getActiveUnit();
2454     gpointer item = g_object_get_data( tbl, "item" );
2455     if (item && SP_IS_RECT(item)) {
2456         {
2457             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2458             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2459             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2460         }
2462         {
2463             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2464             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2465             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2466         }
2468         {
2469             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2470             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2471             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2472         }
2474         {
2475             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2476             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2477             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2478         }
2479     }
2481     sp_rtb_sensitivize( tbl );
2483     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2487 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2488     NULL, /* child_added */
2489     NULL, /* child_removed */
2490     rect_tb_event_attr_changed,
2491     NULL, /* content_changed */
2492     NULL  /* order_changed */
2493 };
2495 /**
2496  *  \param selection should not be NULL.
2497  */
2498 static void
2499 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2501     int n_selected = 0;
2502     Inkscape::XML::Node *repr = NULL;
2503     SPItem *item = NULL;
2505     if ( g_object_get_data( tbl, "repr" ) ) {
2506         g_object_set_data( tbl, "item", NULL );
2507     }
2508     purge_repr_listener( tbl, tbl );
2510     for (GSList const *items = selection->itemList();
2511          items != NULL;
2512          items = items->next) {
2513         if (SP_IS_RECT((SPItem *) items->data)) {
2514             n_selected++;
2515             item = (SPItem *) items->data;
2516             repr = SP_OBJECT_REPR(item);
2517         }
2518     }
2520     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2522     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2524     if (n_selected == 0) {
2525         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2527         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2528         gtk_action_set_sensitive(w, FALSE);
2529         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2530         gtk_action_set_sensitive(h, FALSE);
2532     } else if (n_selected == 1) {
2533         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2534         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2536         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2537         gtk_action_set_sensitive(w, TRUE);
2538         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2539         gtk_action_set_sensitive(h, TRUE);
2541         if (repr) {
2542             g_object_set_data( tbl, "repr", repr );
2543             g_object_set_data( tbl, "item", item );
2544             Inkscape::GC::anchor(repr);
2545             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2546             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2547         }
2548     } else {
2549         // FIXME: implement averaging of all parameters for multiple selected
2550         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2551         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2552         sp_rtb_sensitivize( tbl );
2553     }
2557 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2559     EgeAdjustmentAction* eact = 0;
2560     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2562     {
2563         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2564         ege_output_action_set_use_markup( act, TRUE );
2565         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2566         g_object_set_data( holder, "mode_action", act );
2567     }
2569     // rx/ry units menu: create
2570     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2571     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2572     // fixme: add % meaning per cent of the width/height
2573     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2574     g_object_set_data( holder, "tracker", tracker );
2576     /* W */
2577     {
2578         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2579         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2580         eact = create_adjustment_action( "RectWidthAction",
2581                                          _("Width"), _("W:"), _("Width of rectangle"),
2582                                          "tools.shapes.rect", "width", 0,
2583                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2584                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2585                                          labels, values, G_N_ELEMENTS(labels),
2586                                          sp_rtb_width_value_changed );
2587         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2588         g_object_set_data( holder, "width_action", eact );
2589         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2590         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2591     }
2593     /* H */
2594     {
2595         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2596         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2597         eact = create_adjustment_action( "RectHeightAction",
2598                                          _("Height"), _("H:"), _("Height of rectangle"),
2599                                          "tools.shapes.rect", "height", 0,
2600                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2601                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2602                                          labels, values, G_N_ELEMENTS(labels),
2603                                          sp_rtb_height_value_changed );
2604         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2605         g_object_set_data( holder, "height_action", eact );
2606         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2607         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2608     }
2610     /* rx */
2611     {
2612         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2613         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2614         eact = create_adjustment_action( "RadiusXAction",
2615                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2616                                          "tools.shapes.rect", "rx", 0,
2617                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2618                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2619                                          labels, values, G_N_ELEMENTS(labels),
2620                                          sp_rtb_rx_value_changed);
2621         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2622         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2623     }
2625     /* ry */
2626     {
2627         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2628         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2629         eact = create_adjustment_action( "RadiusYAction",
2630                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2631                                          "tools.shapes.rect", "ry", 0,
2632                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2633                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2634                                          labels, values, G_N_ELEMENTS(labels),
2635                                          sp_rtb_ry_value_changed);
2636         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2637         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2638     }
2640     // add the units menu
2641     {
2642         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2643         gtk_action_group_add_action( mainActions, act );
2644     }
2646     /* Reset */
2647     {
2648         InkAction* inky = ink_action_new( "RectResetAction",
2649                                           _("Not rounded"),
2650                                           _("Make corners sharp"),
2651                                           "squared_corner",
2652                                           secondarySize );
2653         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2654         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2655         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2656         g_object_set_data( holder, "not_rounded", inky );
2657     }
2659     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2660     sp_rtb_sensitivize( holder );
2662     sigc::connection *connection = new sigc::connection(
2663         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2664         );
2665     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2666     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2669 //########################
2670 //##       3D Box       ##
2671 //########################
2673 // normalize angle so that it lies in the interval [0,360]
2674 static double box3d_normalize_angle (double a) {
2675     double angle = a + ((int) (a/360.0))*360;
2676     if (angle < 0) {
2677         angle += 360.0;
2678     }
2679     return angle;
2682 static void
2683 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2684                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2685     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2686     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2687     //       are reset).
2688     bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2690     if (is_infinite) {
2691         gtk_toggle_action_set_active(tact, TRUE);
2692         gtk_action_set_sensitive(act, TRUE);
2694         double angle = persp3d_get_infinite_angle(persp, axis);
2695         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2696             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2697         }
2698     } else {
2699         gtk_toggle_action_set_active(tact, FALSE);
2700         gtk_action_set_sensitive(act, FALSE);
2701     }
2704 static void
2705 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2706     if (!persp_repr) {
2707         g_print ("No perspective given to box3d_resync_toolbar().\n");
2708         return;
2709     }
2711     GtkWidget *tbl = GTK_WIDGET(data);
2712     GtkAdjustment *adj = 0;
2713     GtkAction *act = 0;
2714     GtkToggleAction *tact = 0;
2715     Persp3D *persp = persp3d_get_from_repr(persp_repr);
2716     {
2717         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2718         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2719         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2721         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2722     }
2723     {
2724         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2725         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2726         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2728         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2729     }
2730     {
2731         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2732         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2733         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2735         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2736     }
2739 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2740                                                   gchar const */*old_value*/, gchar const */*new_value*/,
2741                                                   bool /*is_interactive*/, gpointer data)
2743     GtkWidget *tbl = GTK_WIDGET(data);
2745     // quit if run by the attr_changed listener
2746     // note: it used to work without the differently called freeze_ attributes (here and in
2747     //       box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2748     if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2749         return;
2750     }
2752     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2753     // sp_document_maybe_done() when the document is undo insensitive)
2754     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2756     // TODO: Only update the appropriate part of the toolbar
2757 //    if (!strcmp(name, "inkscape:vp_z")) {
2758         box3d_resync_toolbar(repr, G_OBJECT(tbl));
2759 //    }
2761     Persp3D *persp = persp3d_get_from_repr(repr);
2762     persp3d_update_box_reprs(persp);
2764     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2767 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2769     NULL, /* child_added */
2770     NULL, /* child_removed */
2771     box3d_persp_tb_event_attr_changed,
2772     NULL, /* content_changed */
2773     NULL  /* order_changed */
2774 };
2776 /**
2777  *  \param selection Should not be NULL.
2778  */
2779 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2780 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2781 static void
2782 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2784     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2785     // disable the angle entry fields for this direction (otherwise entering a value in them should only
2786     // update the perspectives with infinite VPs and leave the other ones untouched).
2788     Inkscape::XML::Node *persp_repr = NULL;
2789     purge_repr_listener(tbl, tbl);
2791     SPItem *item = selection->singleItem();
2792     if (item && SP_IS_BOX3D(item)) {
2793         // FIXME: Also deal with multiple selected boxes
2794         SPBox3D *box = SP_BOX3D(item);
2795         Persp3D *persp = box3d_get_perspective(box);
2796         persp_repr = SP_OBJECT_REPR(persp);
2797         if (persp_repr) {
2798             g_object_set_data(tbl, "repr", persp_repr);
2799             Inkscape::GC::anchor(persp_repr);
2800             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2801             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2802         }
2804         inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2805         prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2807         box3d_resync_toolbar(persp_repr, tbl);
2808     }
2811 static void
2812 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2814     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2815     SPDocument *document = sp_desktop_document(desktop);
2817     // quit if run by the attr_changed listener
2818     // note: it used to work without the differently called freeze_ attributes (here and in
2819     //       box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2820     if (g_object_get_data( dataKludge, "freeze_attr" )) {
2821         return;
2822     }
2824     // in turn, prevent listener from responding
2825     g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2827     //Persp3D *persp = document->current_persp3d;
2828     std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
2829     if (sel_persps.empty()) {
2830         // this can happen when the document is created; we silently ignore it
2831         return;
2832     }
2833     Persp3D *persp = sel_persps.front();
2835     persp->tmat.set_infinite_direction (axis, adj->value);
2836     SP_OBJECT(persp)->updateRepr();
2838     // TODO: use the correct axis here, too
2839     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2841     g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2845 static void
2846 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2848     box3d_angle_value_changed(adj, dataKludge, Proj::X);
2851 static void
2852 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2854     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2857 static void
2858 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2860     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2864 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2866     // TODO: Take all selected perspectives into account
2867     std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
2868     if (sel_persps.empty()) {
2869         // this can happen when the document is created; we silently ignore it
2870         return;
2871     }
2872     Persp3D *persp = sel_persps.front();
2874     bool set_infinite = gtk_toggle_action_get_active(act);
2875     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2878 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2880     box3d_vp_state_changed(act, box3d_angle, Proj::X);
2883 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2885     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2888 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2890     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2893 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2895     EgeAdjustmentAction* eact = 0;
2896     SPDocument *document = sp_desktop_document (desktop);
2897     Persp3D *persp = document->current_persp3d;
2899     EgeAdjustmentAction* box3d_angle_x = 0;
2900     EgeAdjustmentAction* box3d_angle_y = 0;
2901     EgeAdjustmentAction* box3d_angle_z = 0;
2903     /* Angle X */
2904     {
2905         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2906         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2907         eact = create_adjustment_action( "3DBoxAngleXAction",
2908                                          _("Angle in X direction"), _("Angle X:"),
2909                                          // Translators: PL is short for 'perspective line'
2910                                          _("Angle of PLs in X direction"),
2911                                          "tools.shapes.3dbox", "box3d_angle_x", 30,
2912                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2913                                          -360.0, 360.0, 1.0, 10.0,
2914                                          labels, values, G_N_ELEMENTS(labels),
2915                                          box3d_angle_x_value_changed );
2916         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2917         g_object_set_data( holder, "box3d_angle_x_action", eact );
2918         box3d_angle_x = eact;
2919     }
2921     if (!persp3d_VP_is_finite(persp, Proj::X)) {
2922         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2923     } else {
2924         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2925     }
2928     /* VP X state */
2929     {
2930         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2931                                                       // Translators: VP is short for 'vanishing point'
2932                                                       _("State of VP in X direction"),
2933                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2934                                                       "toggle_vp_x",
2935                                                       Inkscape::ICON_SIZE_DECORATION );
2936         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2937         g_object_set_data( holder, "box3d_vp_x_state_action", act );
2938         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2939         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2940         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2941     }
2943     /* Angle Y */
2944     {
2945         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2946         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2947         eact = create_adjustment_action( "3DBoxAngleYAction",
2948                                          _("Angle in Y direction"), _("Angle Y:"),
2949                                          // Translators: PL is short for 'perspective line'
2950                                          _("Angle of PLs in Y direction"),
2951                                          "tools.shapes.3dbox", "box3d_angle_y", 30,
2952                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2953                                          -360.0, 360.0, 1.0, 10.0,
2954                                          labels, values, G_N_ELEMENTS(labels),
2955                                          box3d_angle_y_value_changed );
2956         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2957         g_object_set_data( holder, "box3d_angle_y_action", eact );
2958         box3d_angle_y = eact;
2959     }
2961     if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2962         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2963     } else {
2964         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2965     }
2967     /* VP Y state */
2968     {
2969         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2970                                                       // Translators: VP is short for 'vanishing point'
2971                                                       _("State of VP in Y direction"),
2972                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2973                                                       "toggle_vp_y",
2974                                                       Inkscape::ICON_SIZE_DECORATION );
2975         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2976         g_object_set_data( holder, "box3d_vp_y_state_action", act );
2977         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2978         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2979         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2980     }
2982     /* Angle Z */
2983     {
2984         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2985         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2986         eact = create_adjustment_action( "3DBoxAngleZAction",
2987                                          _("Angle in Z direction"), _("Angle Z:"),
2988                                          // Translators: PL is short for 'perspective line'
2989                                          _("Angle of PLs in Z direction"),
2990                                          "tools.shapes.3dbox", "box3d_angle_z", 30,
2991                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2992                                          -360.0, 360.0, 1.0, 10.0,
2993                                          labels, values, G_N_ELEMENTS(labels),
2994                                          box3d_angle_z_value_changed );
2995         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2996         g_object_set_data( holder, "box3d_angle_z_action", eact );
2997         box3d_angle_z = eact;
2998     }
3000     if (!persp3d_VP_is_finite(persp, Proj::Z)) {
3001         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3002     } else {
3003         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3004     }
3006     /* VP Z state */
3007     {
3008         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3009                                                       // Translators: VP is short for 'vanishing point'
3010                                                       _("State of VP in Z direction"),
3011                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3012                                                       "toggle_vp_z",
3013                                                       Inkscape::ICON_SIZE_DECORATION );
3014         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3015         g_object_set_data( holder, "box3d_vp_z_state_action", act );
3016         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3017         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3018         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3019     }
3021     sigc::connection *connection = new sigc::connection(
3022         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3023        );
3024     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3025     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3028 //########################
3029 //##       Spiral       ##
3030 //########################
3032 static void
3033 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
3035     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3037     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3038         prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
3039     }
3041     // quit if run by the attr_changed listener
3042     if (g_object_get_data( tbl, "freeze" )) {
3043         return;
3044     }
3046     // in turn, prevent listener from responding
3047     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3049     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3051     bool modmade = false;
3052     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3053          items != NULL;
3054          items = items->next)
3055     {
3056         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3057             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3058             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3059             SP_OBJECT((SPItem *) items->data)->updateRepr();
3060             modmade = true;
3061         }
3062     }
3064     g_free(namespaced_name);
3066     if (modmade) {
3067         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3068                                    _("Change spiral"));
3069     }
3071     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3074 static void
3075 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3077     sp_spl_tb_value_changed(adj, tbl, "revolution");
3080 static void
3081 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3083     sp_spl_tb_value_changed(adj, tbl, "expansion");
3086 static void
3087 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3089     sp_spl_tb_value_changed(adj, tbl, "t0");
3092 static void
3093 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3095     GtkWidget *tbl = GTK_WIDGET(obj);
3097     GtkAdjustment *adj;
3099     // fixme: make settable
3100     gdouble rev = 5;
3101     gdouble exp = 1.0;
3102     gdouble t0 = 0.0;
3104     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3105     gtk_adjustment_set_value(adj, rev);
3106     gtk_adjustment_value_changed(adj);
3108     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3109     gtk_adjustment_set_value(adj, exp);
3110     gtk_adjustment_value_changed(adj);
3112     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3113     gtk_adjustment_set_value(adj, t0);
3114     gtk_adjustment_value_changed(adj);
3116     spinbutton_defocus(GTK_OBJECT(tbl));
3120 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3121                                          gchar const */*old_value*/, gchar const */*new_value*/,
3122                                          bool /*is_interactive*/, gpointer data)
3124     GtkWidget *tbl = GTK_WIDGET(data);
3126     // quit if run by the _changed callbacks
3127     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3128         return;
3129     }
3131     // in turn, prevent callbacks from responding
3132     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3134     GtkAdjustment *adj;
3135     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3136     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3138     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3139     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3141     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3142     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3144     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3148 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3149     NULL, /* child_added */
3150     NULL, /* child_removed */
3151     spiral_tb_event_attr_changed,
3152     NULL, /* content_changed */
3153     NULL  /* order_changed */
3154 };
3156 static void
3157 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3159     int n_selected = 0;
3160     Inkscape::XML::Node *repr = NULL;
3162     purge_repr_listener( tbl, tbl );
3164     for (GSList const *items = selection->itemList();
3165          items != NULL;
3166          items = items->next)
3167     {
3168         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3169             n_selected++;
3170             repr = SP_OBJECT_REPR((SPItem *) items->data);
3171         }
3172     }
3174     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3176     if (n_selected == 0) {
3177         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3178     } else if (n_selected == 1) {
3179         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3181         if (repr) {
3182             g_object_set_data( tbl, "repr", repr );
3183             Inkscape::GC::anchor(repr);
3184             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3185             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3186         }
3187     } else {
3188         // FIXME: implement averaging of all parameters for multiple selected
3189         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3190         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3191     }
3195 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3197     EgeAdjustmentAction* eact = 0;
3198     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3200     {
3201         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3202         ege_output_action_set_use_markup( act, TRUE );
3203         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3204         g_object_set_data( holder, "mode_action", act );
3205     }
3207     /* Revolution */
3208     {
3209         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3210         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3211         eact = create_adjustment_action( "SpiralRevolutionAction",
3212                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3213                                          "tools.shapes.spiral", "revolution", 3.0,
3214                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3215                                          0.01, 1024.0, 0.1, 1.0,
3216                                          labels, values, G_N_ELEMENTS(labels),
3217                                          sp_spl_tb_revolution_value_changed, 1, 2);
3218         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3219     }
3221     /* Expansion */
3222     {
3223         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3224         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3225         eact = create_adjustment_action( "SpiralExpansionAction",
3226                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3227                                          "tools.shapes.spiral", "expansion", 1.0,
3228                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3229                                          0.0, 1000.0, 0.01, 1.0,
3230                                          labels, values, G_N_ELEMENTS(labels),
3231                                          sp_spl_tb_expansion_value_changed);
3232         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3233     }
3235     /* T0 */
3236     {
3237         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3238         gdouble values[] = {0, 0.5, 0.9};
3239         eact = create_adjustment_action( "SpiralT0Action",
3240                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3241                                          "tools.shapes.spiral", "t0", 0.0,
3242                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3243                                          0.0, 0.999, 0.01, 1.0,
3244                                          labels, values, G_N_ELEMENTS(labels),
3245                                          sp_spl_tb_t0_value_changed);
3246         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3247     }
3249     /* Reset */
3250     {
3251         InkAction* inky = ink_action_new( "SpiralResetAction",
3252                                           _("Defaults"),
3253                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3254                                           GTK_STOCK_CLEAR,
3255                                           secondarySize );
3256         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3257         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3258     }
3261     sigc::connection *connection = new sigc::connection(
3262         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3263         );
3264     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3265     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3268 //########################
3269 //##     Pen/Pencil    ##
3270 //########################
3272 static void sp_pc_spiro_spline_mode_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
3274     prefs_set_int_attribute("tools.freehand", "spiro-spline-mode", ege_select_one_action_get_active(act));
3277 static void sp_add_spiro_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3279     // FIXME: No action is needed, we only want a simple label. But sp_toolbox_add_label() always
3280     //        adds the label at the end of the toolbar, whence this workarund. How to avoid this?
3281     {
3282         EgeOutputAction* act = ege_output_action_new(
3283             tool_is_pencil ?
3284             "FreehandModeActionPencilTemp" :
3285             "FreehandModeActionPenTemp",
3286             _("<b>Mode:</b>"), "", 0 );
3287         ege_output_action_set_use_markup( act, TRUE );
3288         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3289         g_object_set_data( holder, "freehand_mode_action", act );
3290     }
3292     /* Freehand mode toggle buttons */
3293     {
3294         //gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
3295         //bool isSpiroMode = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
3296         guint spiroMode = prefs_get_int_attribute("tools.freehand", "spiro-spline-mode", 0);
3297         Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3299         {
3300             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3302             GtkTreeIter iter;
3303             gtk_list_store_append( model, &iter );
3304             gtk_list_store_set( model, &iter,
3305                                 0, _("Bézier"),
3306                                 1, _("Regular Bézier mode"),
3307                                 2, "bezier_mode",
3308                                 -1 );
3310             gtk_list_store_append( model, &iter );
3311             gtk_list_store_set( model, &iter,
3312                                 0, _("Spiro"),
3313                                 1, _("Spiro splines mode"),
3314                                 2, "spiro_splines_mode",
3315                                 -1 );
3317             EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3318                                                                 "FreehandModeActionPencil" :
3319                                                                 "FreehandModeActionPen",
3320                                                                 (""), (""), NULL, GTK_TREE_MODEL(model) );
3321             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3322             g_object_set_data( holder, "freehande_mode_action", act );
3324             ege_select_one_action_set_appearance( act, "full" );
3325             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3326             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3327             ege_select_one_action_set_icon_column( act, 2 );
3328             ege_select_one_action_set_icon_size( act, secondarySize );
3329             ege_select_one_action_set_tooltip_column( act, 1  );
3331             ege_select_one_action_set_active( act, spiroMode);
3332             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_pc_spiro_spline_mode_changed), holder);
3333         }
3334     }
3337 static void sp_freehand_change_shape(EgeSelectOneAction* act, GObject */*dataKludge*/) {
3338     gint shape = ege_select_one_action_get_active( act );
3339     prefs_set_int_attribute("tools.freehand", "shape", shape);
3342 /**
3343  * \brief Generate the list of freehand advanced shape option entries.
3344  */
3345 GList * freehand_shape_dropdown_items_list() {
3346     GList *glist = NULL;
3348     glist = g_list_append (glist, _("None"));
3349     glist = g_list_append (glist, _("Clipboard"));
3350     glist = g_list_append (glist, _("Crescendo"));
3351     glist = g_list_append (glist, _("Decrescendo"));
3353     return glist;
3356 static void
3357 sp_freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3358     /*advanced shape options */
3359     {
3360         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3362         GList* items = 0;
3363         gint count = 0;
3364         for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3365         {
3366             GtkTreeIter iter;
3367             gtk_list_store_append( model, &iter );
3368             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3369             count++;
3370         }
3371         g_list_free( items );
3372         items = 0;
3373         EgeSelectOneAction* act1 = ege_select_one_action_new(
3374             tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3375             _("Shape:"), (""), NULL, GTK_TREE_MODEL(model));
3376         g_object_set( act1, "short_label", _("Shape:"), NULL );
3377         ege_select_one_action_set_appearance( act1, "compact" );
3378         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.freehand", "shape", 0) );
3379         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(sp_freehand_change_shape), holder );
3380         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3381         g_object_set_data( holder, "shape_action", act1 );
3382     }
3385 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3387     sp_add_spiro_toggle(mainActions, holder, false);
3388     sp_freehand_add_advanced_shape_options(mainActions, holder, false);
3392 static void
3393 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3395     GtkWidget *tbl = GTK_WIDGET(obj);
3397     GtkAdjustment *adj;
3399     // fixme: make settable
3400     gdouble tolerance = 4;
3402     adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3403     gtk_adjustment_set_value(adj, tolerance);
3404     gtk_adjustment_value_changed(adj);
3406     spinbutton_defocus(GTK_OBJECT(tbl));
3409 static void
3410 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3413     // quit if run by the attr_changed listener
3414     if (g_object_get_data( tbl, "freeze" )) {
3415         return;
3416     }
3417     // in turn, prevent listener from responding
3418     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3419     prefs_set_double_attribute("tools.freehand.pencil",
3420                                "tolerance", adj->value);
3421     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3427 static void
3428 sp_pencil_tb_tolerance_value_changed_external(Inkscape::XML::Node */*repr*/,
3429                                               const gchar */*key*/,
3430                                               const gchar */*oldval*/,
3431                                               const gchar */*newval*/,
3432                                               bool /*is_interactive*/,
3433                                               void * data)
3435     GObject* tbl = G_OBJECT(data);
3436     if (g_object_get_data( tbl, "freeze" )) {
3437         return;
3438     }
3440     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3442     GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl,
3443                                                             "tolerance");
3445     double v = prefs_get_double_attribute("tools.freehand.pencil",
3446                                             "tolerance", adj->value);
3447     gtk_adjustment_set_value(adj, v);
3448     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3452 static Inkscape::XML::NodeEventVector pencil_node_events =
3454     NULL,
3455     NULL,
3456     sp_pencil_tb_tolerance_value_changed_external,
3457     NULL,
3458     NULL,
3459 };
3462 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3464     sp_add_spiro_toggle(mainActions, holder, true);
3466     EgeAdjustmentAction* eact = 0;
3468     /* Tolerance */
3469     {
3470         gchar const* labels[] = {_("(many nodes, rough)"), ("(default)"), 0, 0, 0, 0, ("(few nodes, smooth)")};
3471         gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
3472         eact = create_adjustment_action( "PencilToleranceAction",
3473                                          _("Smoothing:"), _("Smoothing"),
3474                  _("How much smoothing (simplifying) is applied to the line"),
3475                                          "tools.freehand.pencil", "tolerance",
3476                                          3.0,
3477                                          GTK_WIDGET(desktop->canvas), NULL,
3478                                          holder, TRUE, "altx-pencil",
3479                                          1, 100.0, 0.5, 0,
3480                                          labels, values, G_N_ELEMENTS(labels),
3481                                          sp_pencil_tb_tolerance_value_changed,
3482                                          1, 2);
3483         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3484         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3486         Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE,
3487                                                       "tools.freehand.pencil");
3488         repr->addListener(&pencil_node_events, G_OBJECT(holder));
3489         g_object_set_data(G_OBJECT(holder), "repr", repr);
3491     }
3493     /* advanced shape options */
3494     sp_freehand_add_advanced_shape_options(mainActions, holder, true);
3496     /* Reset */
3497     {
3498         InkAction* inky = ink_action_new( "PencilResetAction",
3499                                           _("Defaults"),
3500                                           _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3501                                           GTK_STOCK_CLEAR,
3502                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3503         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
3504         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3505     }
3507     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3512 //########################
3513 //##       Tweak        ##
3514 //########################
3516 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3518     prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3521 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3523     prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3526 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3528     prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3531 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3533     int mode = ege_select_one_action_get_active( act );
3534     prefs_set_int_attribute("tools.tweak", "mode", mode);
3536     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3537     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3538     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3539     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3540     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3541     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3542     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3543         if (doh) gtk_action_set_sensitive (doh, TRUE);
3544         if (dos) gtk_action_set_sensitive (dos, TRUE);
3545         if (dol) gtk_action_set_sensitive (dol, TRUE);
3546         if (doo) gtk_action_set_sensitive (doo, TRUE);
3547         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3548         if (fid) gtk_action_set_sensitive (fid, FALSE);
3549     } else {
3550         if (doh) gtk_action_set_sensitive (doh, FALSE);
3551         if (dos) gtk_action_set_sensitive (dos, FALSE);
3552         if (dol) gtk_action_set_sensitive (dol, FALSE);
3553         if (doo) gtk_action_set_sensitive (doo, FALSE);
3554         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3555         if (fid) gtk_action_set_sensitive (fid, TRUE);
3556     }
3559 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3561     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3564 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3565     bool show = gtk_toggle_action_get_active( act );
3566     prefs_set_int_attribute ("tools.tweak", "doh",  show ? 1 : 0);
3568 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3569     bool show = gtk_toggle_action_get_active( act );
3570     prefs_set_int_attribute ("tools.tweak", "dos",  show ? 1 : 0);
3572 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3573     bool show = gtk_toggle_action_get_active( act );
3574     prefs_set_int_attribute ("tools.tweak", "dol",  show ? 1 : 0);
3576 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3577     bool show = gtk_toggle_action_get_active( act );
3578     prefs_set_int_attribute ("tools.tweak", "doo",  show ? 1 : 0);
3581 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3583     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3585     {
3586         /* Width */
3587         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3588         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3589         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3590                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3591                                                               "tools.tweak", "width", 15,
3592                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3593                                                               1, 100, 1.0, 0.0,
3594                                                               labels, values, G_N_ELEMENTS(labels),
3595                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
3596         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3597         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3598         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3599     }
3602     {
3603         /* Force */
3604         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3605         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3606         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3607                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
3608                                                               "tools.tweak", "force", 20,
3609                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3610                                                               1, 100, 1.0, 0.0,
3611                                                               labels, values, G_N_ELEMENTS(labels),
3612                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
3613         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3614         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3615         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3616     }
3618     /* Mode */
3619     {
3620         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3622         GtkTreeIter iter;
3623         gtk_list_store_append( model, &iter );
3624         gtk_list_store_set( model, &iter,
3625                             0, _("Push mode"),
3626                             1, _("Push parts of paths in any direction"),
3627                             2, "tweak_push_mode",
3628                             -1 );
3630         gtk_list_store_append( model, &iter );
3631         gtk_list_store_set( model, &iter,
3632                             0, _("Shrink mode"),
3633                             1, _("Shrink (inset) parts of paths"),
3634                             2, "tweak_shrink_mode",
3635                             -1 );
3637         gtk_list_store_append( model, &iter );
3638         gtk_list_store_set( model, &iter,
3639                             0, _("Grow mode"),
3640                             1, _("Grow (outset) parts of paths"),
3641                             2, "tweak_grow_mode",
3642                             -1 );
3644         gtk_list_store_append( model, &iter );
3645         gtk_list_store_set( model, &iter,
3646                             0, _("Attract mode"),
3647                             1, _("Attract parts of paths towards cursor"),
3648                             2, "tweak_attract_mode",
3649                             -1 );
3651         gtk_list_store_append( model, &iter );
3652         gtk_list_store_set( model, &iter,
3653                             0, _("Repel mode"),
3654                             1, _("Repel parts of paths from cursor"),
3655                             2, "tweak_repel_mode",
3656                             -1 );
3658         gtk_list_store_append( model, &iter );
3659         gtk_list_store_set( model, &iter,
3660                             0, _("Roughen mode"),
3661                             1, _("Roughen parts of paths"),
3662                             2, "tweak_roughen_mode",
3663                             -1 );
3665         gtk_list_store_append( model, &iter );
3666         gtk_list_store_set( model, &iter,
3667                             0, _("Color paint mode"),
3668                             1, _("Paint the tool's color upon selected objects"),
3669                             2, "tweak_colorpaint_mode",
3670                             -1 );
3672         gtk_list_store_append( model, &iter );
3673         gtk_list_store_set( model, &iter,
3674                             0, _("Color jitter mode"),
3675                             1, _("Jitter the colors of selected objects"),
3676                             2, "tweak_colorjitter_mode",
3677                             -1 );
3679         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3680         g_object_set( act, "short_label", _("Mode:"), NULL );
3681         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3682         g_object_set_data( holder, "mode_action", act );
3684         ege_select_one_action_set_appearance( act, "full" );
3685         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3686         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3687         ege_select_one_action_set_icon_column( act, 2 );
3688         ege_select_one_action_set_icon_size( act, secondarySize );
3689         ege_select_one_action_set_tooltip_column( act, 1  );
3691         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3692         ege_select_one_action_set_active( act, mode );
3693         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3695         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3696     }
3698     guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3700     {
3701         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3702         ege_output_action_set_use_markup( act, TRUE );
3703         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3704         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3705             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3706         g_object_set_data( holder, "tweak_channels_label", act);
3707     }
3709     {
3710         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3711                                                       _("Hue"),
3712                                                       _("In color mode, act on objects' hue"),
3713                                                       NULL,
3714                                                       Inkscape::ICON_SIZE_DECORATION );
3715         //TRANSLATORS:  "H" here stands for hue
3716         g_object_set( act, "short_label", _("H"), NULL );
3717         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3718         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3719         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3720         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3721             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3722         g_object_set_data( holder, "tweak_doh", act);
3723     }
3724     {
3725         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3726                                                       _("Saturation"),
3727                                                       _("In color mode, act on objects' saturation"),
3728                                                       NULL,
3729                                                       Inkscape::ICON_SIZE_DECORATION );
3730         //TRANSLATORS: "S" here stands for Saturation
3731         g_object_set( act, "short_label", _("S"), NULL );
3732         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3733         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3734         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3735         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3736             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3737         g_object_set_data( holder, "tweak_dos", act );
3738     }
3739     {
3740         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3741                                                       _("Lightness"),
3742                                                       _("In color mode, act on objects' lightness"),
3743                                                       NULL,
3744                                                       Inkscape::ICON_SIZE_DECORATION );
3745         //TRANSLATORS: "L" here stands for Lightness
3746         g_object_set( act, "short_label", _("L"), NULL );
3747         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3748         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3749         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3750         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3751             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3752         g_object_set_data( holder, "tweak_dol", act );
3753     }
3754     {
3755         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3756                                                       _("Opacity"),
3757                                                       _("In color mode, act on objects' opacity"),
3758                                                       NULL,
3759                                                       Inkscape::ICON_SIZE_DECORATION );
3760         //TRANSLATORS: "O" here stands for Opacity
3761         g_object_set( act, "short_label", _("O"), NULL );
3762         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3763         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3764         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3765         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3766             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3767         g_object_set_data( holder, "tweak_doo", act );
3768     }
3770     {   /* Fidelity */
3771         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3772         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3773         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3774                                                               _("Fidelity"), _("Fidelity:"),
3775                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3776                                                               "tools.tweak", "fidelity", 50,
3777                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3778                                                               1, 100, 1.0, 10.0,
3779                                                               labels, values, G_N_ELEMENTS(labels),
3780                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
3781         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3782         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3783         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3784             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3785         g_object_set_data( holder, "tweak_fidelity", eact );
3786     }
3789     /* Use Pressure button */
3790     {
3791         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3792                                                       _("Pressure"),
3793                                                       _("Use the pressure of the input device to alter the force of tweak action"),
3794                                                       "use_pressure",
3795                                                       Inkscape::ICON_SIZE_DECORATION );
3796         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3797         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3798         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3799     }
3804 //########################
3805 //##     Calligraphy    ##
3806 //########################
3807 static void update_presets_list (GObject *tbl){
3809     if (g_object_get_data(tbl, "presets_blocked")) 
3810         return;
3812     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3813     if (sel) {
3814         ege_select_one_action_set_active(sel, 0);
3815     }
3818 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3820     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value * 0.01 );
3821     update_presets_list(tbl);
3824 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3826     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value * 0.01 );
3827     update_presets_list(tbl);
3830 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3832     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3833     update_presets_list(tbl);
3836 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3838     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3839     update_presets_list(tbl);
3842 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3844     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value * 0.01 );
3845     update_presets_list(tbl);
3848 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3850     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value * 0.01);
3851     update_presets_list(tbl);
3854 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3856     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value * 0.01 );
3857     update_presets_list(tbl);
3860 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3862     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3863     update_presets_list(tbl);
3866 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
3868     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3869     update_presets_list(tbl);
3872 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
3874     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3875     update_presets_list(tbl);
3878 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
3880     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
3881     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3882     update_presets_list(tbl);
3883     if (calligraphy_angle )
3884         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3888 #define NUMBER_OF_PRESET_PARAMS 11
3889 static gchar * widget_names[NUMBER_OF_PRESET_PARAMS] = {
3890     "width",
3891     "mass",
3892     "wiggle",
3893     "angle",
3894     "thinning",
3895     "tremor",
3896     "flatness",
3897     "cap_rounding",
3898     "usepressure",
3899     "tracebackground",
3900     "usetilt"
3901 };
3904 static void sp_dcc_build_presets_list(GObject *tbl) 
3906     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3907     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
3908     gtk_list_store_clear (model);
3910     {
3911         GtkTreeIter iter;
3912         gtk_list_store_append( model, &iter );
3913         gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
3914     }
3916     //TODO: switch back to prefs API
3917     Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
3918     Inkscape::XML::Node *child_repr = sp_repr_children(repr);
3919     int ii=1;
3920     while (child_repr) {
3921         GtkTreeIter iter;
3922         char *preset_name = (char *) child_repr->attribute("name");
3923         gtk_list_store_append( model, &iter );
3924         gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );
3925         child_repr = sp_repr_next(child_repr);
3926     }
3928     {
3929         GtkTreeIter iter;
3930         gtk_list_store_append( model, &iter );
3931         gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
3932         g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
3933     }
3935     update_presets_list (tbl);
3938 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
3940     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
3941     if (! desktop) return;
3943     if (g_object_get_data(tbl, "presets_blocked")) 
3944         return;
3946     Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
3947     if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) {
3948         // dialog cancelled
3949         update_presets_list (tbl);
3950         return;
3951     }
3952     Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
3954     if (!profile_name.c_str() || *profile_name.c_str() == 0) {
3955         // empty name entered
3956         update_presets_list (tbl);
3957         return;
3958     }
3960     int new_index = -1;
3961     gchar *pref_path = NULL;
3962     int total_prefs = pref_path_number_of_children("tools.calligraphic.preset");
3964     for (int i = 1; i <= total_prefs; i++) {
3965         gchar *path = get_pref_nth_child("tools.calligraphic.preset", i);
3966         const gchar *name = prefs_get_string_attribute(path, "name");
3967         if (name && !strcmp(name, profile_name.c_str())) {
3968             // we already have preset with this name, replace its values
3969             new_index = i;
3970             pref_path = g_strdup(path);
3971             break;
3972         }
3973     }
3975     if (new_index == -1) {
3976         // no preset with this name, create 
3977         new_index = total_prefs + 1;
3978         gchar *profile_id = g_strdup_printf("dcc%d", new_index);
3979         pref_path = create_pref("tools.calligraphic.preset", profile_id);
3980         free(profile_id);
3981     }
3983     for (unsigned i = 0; i < NUMBER_OF_PRESET_PARAMS; ++i) {
3984         gchar *widget_name = widget_names[i];
3985         void *widget = g_object_get_data(tbl, widget_name);
3986         if (widget) {
3987             if (GTK_IS_ADJUSTMENT(widget)) {
3988                 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
3989                 double v = gtk_adjustment_get_value(adj);
3990                 prefs_set_double_attribute(pref_path, widget_name, v);
3991                 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
3992             } else if (GTK_IS_TOGGLE_ACTION(widget)) {
3993                 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
3994                 int v = gtk_toggle_action_get_active(toggle);
3995                 prefs_set_int_attribute(pref_path, widget_name, v);
3996                 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
3997             } else {
3998                g_warning("Unknown widget type for preset: %s\n", widget_name);
3999             }
4000         } else {
4001             g_warning("Bad key when writing preset: %s\n", widget_name);
4002         }
4003     }
4004     prefs_set_string_attribute(pref_path, "name", profile_name.c_str());
4006     sp_dcc_build_presets_list (tbl);
4008     free (pref_path);
4012 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4014     gint preset_index = ege_select_one_action_get_active( act );
4015     gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4017     if (preset_index == save_presets_index) {
4018         // this is the Save command
4019         sp_dcc_save_profile(NULL, tbl);
4020         return;
4021     }
4023     gchar *preset_path = get_pref_nth_child("tools.calligraphic.preset", preset_index);
4025     if (preset_path) {
4026         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
4028         Inkscape::XML::Node *preset_repr = inkscape_get_repr(INKSCAPE, preset_path);
4030         for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
4031               iter; 
4032               ++iter ) {
4033             const gchar *attr_name = g_quark_to_string(iter->key);
4034             if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
4035                 continue;
4036             void *widget = g_object_get_data(tbl, attr_name);
4037             if (widget) {
4038                 if (GTK_IS_ADJUSTMENT(widget)) {
4039                     double v = prefs_get_double_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4040                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4041                     gtk_adjustment_set_value(adj, v);
4042                     //std::cout << "set adj " << attr_name << " to " << v << "\n";
4043                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4044                     int v = prefs_get_int_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4045                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4046                     gtk_toggle_action_set_active(toggle, v);
4047                     //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4048                 } else {
4049                     g_warning("Unknown widget type for preset: %s\n", attr_name);
4050                 }
4051             } else {
4052                 g_warning("Bad key found in a preset record: %s\n", attr_name);
4053             }
4054         }
4055         free(preset_path);
4056         g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4057     }
4062 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4064     {
4065         EgeAdjustmentAction* calligraphy_angle = 0;
4067         {
4068         /* Width */
4069         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4070         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4071         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4072                                                               _("Pen Width"), _("Width:"),
4073                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
4074                                                               "tools.calligraphic", "width", 15,
4075                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4076                                                               1, 100, 1.0, 0.0,
4077                                                               labels, values, G_N_ELEMENTS(labels),
4078                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
4079         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4080         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4081         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4082         }
4084         {
4085         /* Thinning */
4086             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4087             gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4088         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4089                                                               _("Stroke Thinning"), _("Thinning:"),
4090                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4091                                                               "tools.calligraphic", "thinning", 10,
4092                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4093                                                               -100, 100, 1, 0.1,
4094                                                               labels, values, G_N_ELEMENTS(labels),
4095                                                               sp_ddc_velthin_value_changed, 0.01, 0, 100);
4096         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4097         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4098         }
4100         {
4101         /* Angle */
4102         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4103         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4104         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4105                                                               _("Pen Angle"), _("Angle:"),
4106                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4107                                                               "tools.calligraphic", "angle", 30,
4108                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4109                                                               -90.0, 90.0, 1.0, 10.0,
4110                                                               labels, values, G_N_ELEMENTS(labels),
4111                                                               sp_ddc_angle_value_changed, 1, 0 );
4112         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4113         g_object_set_data( holder, "angle_action", eact );
4114         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4115         calligraphy_angle = eact;
4116         }
4118         {
4119         /* Fixation */
4120             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4121         gdouble values[] = {0, 20, 40, 60, 90, 100};
4122         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4123                                                               _("Fixation"), _("Fixation:"),
4124                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
4125                                                               "tools.calligraphic", "flatness", 90,
4126                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4127                                                               0.0, 100, 1.0, 10.0,
4128                                                               labels, values, G_N_ELEMENTS(labels),
4129                                                               sp_ddc_flatness_value_changed, 0.01, 0, 100 );
4130         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4131         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4132         }
4134         {
4135         /* Cap Rounding */
4136             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4137         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4138         // TRANSLATORS: "cap" means "end" (both start and finish) here
4139         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4140                                                               _("Cap rounding"), _("Caps:"),
4141                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4142                                                               "tools.calligraphic", "cap_rounding", 0.0,
4143                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4144                                                               0.0, 5.0, 0.01, 0.1,
4145                                                               labels, values, G_N_ELEMENTS(labels),
4146                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4147         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4148         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4149         }
4151         {
4152         /* Tremor */
4153             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4154         gdouble values[] = {0, 10, 20, 40, 60, 100};
4155         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4156                                                               _("Stroke Tremor"), _("Tremor:"),
4157                                                               _("Increase to make strokes rugged and trembling"),
4158                                                               "tools.calligraphic", "tremor", 0.0,
4159                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4160                                                               0.0, 100, 1, 0.0,
4161                                                               labels, values, G_N_ELEMENTS(labels),
4162                                                               sp_ddc_tremor_value_changed, 0.01, 0, 100 );
4164         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4165         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4166         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4167         }
4169         {
4170         /* Wiggle */
4171         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4172         gdouble values[] = {0, 20, 40, 60, 100};
4173         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4174                                                               _("Pen Wiggle"), _("Wiggle:"),
4175                                                               _("Increase to make the pen waver and wiggle"),
4176                                                               "tools.calligraphic", "wiggle", 0.0,
4177                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4178                                                               0.0, 100, 1, 0.0,
4179                                                               labels, values, G_N_ELEMENTS(labels),
4180                                                               sp_ddc_wiggle_value_changed, 0.01, 0, 100 );
4181         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4182         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4183         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4184         }
4186         {
4187         /* Mass */
4188             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4189         gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4190         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4191                                                               _("Pen Mass"), _("Mass:"),
4192                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
4193                                                               "tools.calligraphic", "mass", 2.0,
4194                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4195                                                               0.0, 100, 1, 0.0,
4196                                                               labels, values, G_N_ELEMENTS(labels),
4197                                                               sp_ddc_mass_value_changed, 0.01, 0, 100 );
4198         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4199         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4200         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4201         }
4204         /* Trace Background button */
4205         {
4206             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4207                                                           _("Trace Background"),
4208                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4209                                                           "trace_background",
4210                                                           Inkscape::ICON_SIZE_DECORATION );
4211             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4212             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4213             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
4214             g_object_set_data( holder, "tracebackground", act );
4215         }
4217         /* Use Pressure button */
4218         {
4219             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4220                                                           _("Pressure"),
4221                                                           _("Use the pressure of the input device to alter the width of the pen"),
4222                                                           "use_pressure",
4223                                                           Inkscape::ICON_SIZE_DECORATION );
4224             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4225             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4226             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
4227             g_object_set_data( holder, "usepressure", act );
4228         }
4230         /* Use Tilt button */
4231         {
4232             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4233                                                           _("Tilt"),
4234                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
4235                                                           "use_tilt",
4236                                                           Inkscape::ICON_SIZE_DECORATION );
4237             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4238             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4239             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4240             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4241             g_object_set_data( holder, "usetilt", act );
4242         }
4244         /*calligraphic profile */
4245         {
4246             GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
4247             EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Change calligraphic profile")), NULL, GTK_TREE_MODEL(model));
4248             ege_select_one_action_set_appearance (act1, "compact");
4249             g_object_set_data (holder, "profile_selector", act1 );
4251             sp_dcc_build_presets_list (holder);
4253             g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
4254             gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
4255         }
4256     }
4260 //########################
4261 //##    Circle / Arc    ##
4262 //########################
4264 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4266     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4267     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4269     if (v1 == 0 && v2 == 0) {
4270         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4271             gtk_action_set_sensitive( ocb, FALSE );
4272             gtk_action_set_sensitive( make_whole, FALSE );
4273         }
4274     } else {
4275         gtk_action_set_sensitive( ocb, TRUE );
4276         gtk_action_set_sensitive( make_whole, TRUE );
4277     }
4280 static void
4281 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4283     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4285     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4286         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
4287     }
4289     // quit if run by the attr_changed listener
4290     if (g_object_get_data( tbl, "freeze" )) {
4291         return;
4292     }
4294     // in turn, prevent listener from responding
4295     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4297     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4299     bool modmade = false;
4300     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4301          items != NULL;
4302          items = items->next)
4303     {
4304         SPItem *item = SP_ITEM(items->data);
4306         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4308             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4309             SPArc *arc = SP_ARC(item);
4311             if (!strcmp(value_name, "start"))
4312                 ge->start = (adj->value * M_PI)/ 180;
4313             else
4314                 ge->end = (adj->value * M_PI)/ 180;
4316             sp_genericellipse_normalize(ge);
4317             ((SPObject *)arc)->updateRepr();
4318             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4320             modmade = true;
4321         }
4322     }
4324     g_free(namespaced_name);
4326     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4328     sp_arctb_sensitivize( tbl, adj->value, other->value );
4330     if (modmade) {
4331         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4332                                    _("Arc: Change start/end"));
4333     }
4335     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4339 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
4341     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
4344 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4346     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
4350 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4352     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4353     gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
4354     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4355         prefs_set_int_attribute( "tools.eraser", "mode", eraserMode );
4356     }
4358     // only take action if run by the attr_changed listener
4359     if (!g_object_get_data( tbl, "freeze" )) {
4360         // in turn, prevent listener from responding
4361         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4363         if ( eraserMode != 0 ) {
4364         } else {
4365         }
4366         // TODO finish implementation
4368         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4369     }
4372 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4374     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4375     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4376         if ( ege_select_one_action_get_active( act ) != 0 ) {
4377             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
4378         } else {
4379             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
4380         }
4381     }
4383     // quit if run by the attr_changed listener
4384     if (g_object_get_data( tbl, "freeze" )) {
4385         return;
4386     }
4388     // in turn, prevent listener from responding
4389     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4391     bool modmade = false;
4393     if ( ege_select_one_action_get_active(act) != 0 ) {
4394         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4395              items != NULL;
4396              items = items->next)
4397         {
4398             if (SP_IS_ARC((SPItem *) items->data)) {
4399                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4400                 repr->setAttribute("sodipodi:open", "true");
4401                 SP_OBJECT((SPItem *) items->data)->updateRepr();
4402                 modmade = true;
4403             }
4404         }
4405     } else {
4406         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4407              items != NULL;
4408              items = items->next)
4409         {
4410             if (SP_IS_ARC((SPItem *) items->data))    {
4411                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4412                 repr->setAttribute("sodipodi:open", NULL);
4413                 SP_OBJECT((SPItem *) items->data)->updateRepr();
4414                 modmade = true;
4415             }
4416         }
4417     }
4419     if (modmade) {
4420         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4421                                    _("Arc: Change open/closed"));
4422     }
4424     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4427 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4429     GtkAdjustment *adj;
4430     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
4431     gtk_adjustment_set_value(adj, 0.0);
4432     gtk_adjustment_value_changed(adj);
4434     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
4435     gtk_adjustment_set_value(adj, 0.0);
4436     gtk_adjustment_value_changed(adj);
4438     spinbutton_defocus( GTK_OBJECT(obj) );
4441 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
4442                                       gchar const */*old_value*/, gchar const */*new_value*/,
4443                                       bool /*is_interactive*/, gpointer data)
4445     GObject *tbl = G_OBJECT(data);
4447     // quit if run by the _changed callbacks
4448     if (g_object_get_data( tbl, "freeze" )) {
4449         return;
4450     }
4452     // in turn, prevent callbacks from responding
4453     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4455     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4456     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4458     GtkAdjustment *adj1,*adj2;
4459     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4460     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4461     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4462     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4464     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4466     char const *openstr = NULL;
4467     openstr = repr->attribute("sodipodi:open");
4468     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4470     if (openstr) {
4471         ege_select_one_action_set_active( ocb, 1 );
4472     } else {
4473         ege_select_one_action_set_active( ocb, 0 );
4474     }
4476     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4479 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4480     NULL, /* child_added */
4481     NULL, /* child_removed */
4482     arc_tb_event_attr_changed,
4483     NULL, /* content_changed */
4484     NULL  /* order_changed */
4485 };
4488 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4490     int n_selected = 0;
4491     Inkscape::XML::Node *repr = NULL;
4493     purge_repr_listener( tbl, tbl );
4495     for (GSList const *items = selection->itemList();
4496          items != NULL;
4497          items = items->next)
4498     {
4499         if (SP_IS_ARC((SPItem *) items->data)) {
4500             n_selected++;
4501             repr = SP_OBJECT_REPR((SPItem *) items->data);
4502         }
4503     }
4505     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4507     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4508     if (n_selected == 0) {
4509         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4510     } else if (n_selected == 1) {
4511         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4512         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4514         if (repr) {
4515             g_object_set_data( tbl, "repr", repr );
4516             Inkscape::GC::anchor(repr);
4517             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4518             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4519         }
4520     } else {
4521         // FIXME: implement averaging of all parameters for multiple selected
4522         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4523         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4524         sp_arctb_sensitivize( tbl, 1, 0 );
4525     }
4529 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4531     EgeAdjustmentAction* eact = 0;
4532     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
4535     {
4536         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4537         ege_output_action_set_use_markup( act, TRUE );
4538         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4539         g_object_set_data( holder, "mode_action", act );
4540     }
4542     /* Start */
4543     {
4544         eact = create_adjustment_action( "ArcStartAction",
4545                                          _("Start"), _("Start:"),
4546                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
4547                                          "tools.shapes.arc", "start", 0.0,
4548                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4549                                          -360.0, 360.0, 1.0, 10.0,
4550                                          0, 0, 0,
4551                                          sp_arctb_start_value_changed);
4552         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4553     }
4555     /* End */
4556     {
4557         eact = create_adjustment_action( "ArcEndAction",
4558                                          _("End"), _("End:"),
4559                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
4560                                          "tools.shapes.arc", "end", 0.0,
4561                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4562                                          -360.0, 360.0, 1.0, 10.0,
4563                                          0, 0, 0,
4564                                          sp_arctb_end_value_changed);
4565         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4566     }
4568     /* Segments / Pie checkbox */
4569     {
4570         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4572         GtkTreeIter iter;
4573         gtk_list_store_append( model, &iter );
4574         gtk_list_store_set( model, &iter,
4575                             0, _("Closed arc"),
4576                             1, _("Switch to segment (closed shape with two radii)"),
4577                             2, "circle_closed_arc",
4578                             -1 );
4580         gtk_list_store_append( model, &iter );
4581         gtk_list_store_set( model, &iter,
4582                             0, _("Open Arc"),
4583                             1, _("Switch to arc (unclosed shape)"),
4584                             2, "circle_open_arc",
4585                             -1 );
4587         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4588         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4589         g_object_set_data( holder, "open_action", act );
4591         ege_select_one_action_set_appearance( act, "full" );
4592         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4593         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4594         ege_select_one_action_set_icon_column( act, 2 );
4595         ege_select_one_action_set_icon_size( act, secondarySize );
4596         ege_select_one_action_set_tooltip_column( act, 1  );
4598         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4599         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4600         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4601         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4602     }
4604     /* Make Whole */
4605     {
4606         InkAction* inky = ink_action_new( "ArcResetAction",
4607                                           _("Make whole"),
4608                                           _("Make the shape a whole ellipse, not arc or segment"),
4609                                           "reset_circle",
4610                                           secondarySize );
4611         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4612         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4613         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4614         g_object_set_data( holder, "make_whole", inky );
4615     }
4617     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4618     // sensitivize make whole and open checkbox
4619     {
4620         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4621         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4622         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4623     }
4626     sigc::connection *connection = new sigc::connection(
4627         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4628         );
4629     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4630     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4636 // toggle button callbacks and updaters
4638 //########################
4639 //##      Dropper       ##
4640 //########################
4642 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4643     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4644     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4645     if ( set_action ) {
4646         if ( gtk_toggle_action_get_active( act ) ) {
4647             gtk_action_set_sensitive( set_action, TRUE );
4648         } else {
4649             gtk_action_set_sensitive( set_action, FALSE );
4650         }
4651     }
4653     spinbutton_defocus(GTK_OBJECT(tbl));
4656 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4657     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4658     spinbutton_defocus(GTK_OBJECT(tbl));
4662 /**
4663  * Dropper auxiliary toolbar construction and setup.
4664  *
4665  * TODO: Would like to add swatch of current color.
4666  * TODO: Add queue of last 5 or so colors selected with new swatches so that
4667  *       can drag and drop places. Will provide a nice mixing palette.
4668  */
4669 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4671     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4673     {
4674         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4675         ege_output_action_set_use_markup( act, TRUE );
4676         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4677     }
4679     {
4680         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4681                                                       _("Pick opacity"),
4682                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4683                                                       NULL,
4684                                                       Inkscape::ICON_SIZE_DECORATION );
4685         g_object_set( act, "short_label", _("Pick"), NULL );
4686         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4687         g_object_set_data( holder, "pick_action", act );
4688         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4689         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4690     }
4692     {
4693         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4694                                                       _("Assign opacity"),
4695                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4696                                                       NULL,
4697                                                       Inkscape::ICON_SIZE_DECORATION );
4698         g_object_set( act, "short_label", _("Assign"), NULL );
4699         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4700         g_object_set_data( holder, "set_action", act );
4701         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4702         // make sure it's disabled if we're not picking alpha
4703         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4704         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4705     }
4710 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4712     {
4713         /* Width */
4714         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4715         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4716         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
4717                                                               _("Pen Width"), _("Width:"),
4718                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
4719                                                               "tools.eraser", "width", 15,
4720                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
4721                                                               1, 100, 1.0, 0.0,
4722                                                               labels, values, G_N_ELEMENTS(labels),
4723                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
4724         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4725         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4726         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4727     }
4729     {
4730         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4732         GtkTreeIter iter;
4733         gtk_list_store_append( model, &iter );
4734         gtk_list_store_set( model, &iter,
4735                             0, _("Delete"),
4736                             1, _("Delete objects touched by the eraser"),
4737                             2, "delete_object",
4738                             -1 );
4740         gtk_list_store_append( model, &iter );
4741         gtk_list_store_set( model, &iter,
4742                             0, _("Cut"),
4743                             1, _("Cut out from objects"),
4744                             2, "difference",
4745                             -1 );
4747         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4748         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4749         g_object_set_data( holder, "eraser_mode_action", act );
4751         ege_select_one_action_set_appearance( act, "full" );
4752         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4753         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4754         ege_select_one_action_set_icon_column( act, 2 );
4755         ege_select_one_action_set_tooltip_column( act, 1  );
4757         gint eraserMode = (prefs_get_int_attribute("tools.eraser", "mode", 0) != 0) ? 1 : 0;
4758         ege_select_one_action_set_active( act, eraserMode );
4759         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
4760     }
4764 //########################
4765 //##    Text Toolbox    ##
4766 //########################
4767 /*
4768 static void
4769 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
4771     //Call back for letter sizing spinbutton
4774 static void
4775 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
4777     //Call back for line height spinbutton
4780 static void
4781 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4783     //Call back for horizontal kerning spinbutton
4786 static void
4787 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4789     //Call back for vertical kerning spinbutton
4792 static void
4793 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
4795     //Call back for letter rotation spinbutton
4796 }*/
4798 namespace {
4800 bool popdown_visible = false;
4801 bool popdown_hasfocus = false;
4803 void
4804 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
4806     SPStyle *query =
4807         sp_style_new (SP_ACTIVE_DOCUMENT);
4809 //    int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4811     int result_family =
4812         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4814     int result_style =
4815         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4817     int result_numbers =
4818         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4820     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4822     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4823     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
4824         // there are no texts in selection, read from prefs
4825  
4826         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
4827         if (repr) {
4828             sp_style_read_from_repr (query, repr);
4829             if (g_object_get_data(tbl, "text_style_from_prefs")) {
4830                 // do not reset the toolbar style from prefs if we already did it last time
4831                 sp_style_unref(query);
4832                 return;
4833             }
4834             g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
4835         } else {
4836             sp_style_unref(query);
4837             return;
4838         }
4839     } else {
4840         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
4841     }
4843     if (query->text)
4844     {
4845         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4846             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4847             gtk_entry_set_text (GTK_ENTRY (entry), "");
4849         } else if (query->text->font_specification.value || query->text->font_family.value) {
4851             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4853             // Get the font that corresponds
4854             Glib::ustring familyName;
4856             font_instance * font = font_factory::Default()->FaceFromStyle(query);
4857             if (font) {
4858                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4859                 font->Unref();
4860                 font = NULL;
4861             }
4863             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4865             Gtk::TreePath path;
4866             try {
4867                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4868             } catch (...) {
4869                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4870                 sp_style_unref(query);
4871                 return;
4872             }
4874             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4875             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4877             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4879             gtk_tree_selection_select_path (tselection, path.gobj());
4880             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4882             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4883         }
4885         //Size
4886         GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4887         char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4888         g_object_set_data (tbl, "size-block", gpointer(1));
4889         gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4890         g_object_set_data (tbl, "size-block", gpointer(0));
4891         free (str);
4893         //Anchor
4894         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4895         {
4896             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4897             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4898             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4899             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4900         }
4901         else
4902         {
4903             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4904             {
4905                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4906                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4907                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4908                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4909             }
4910             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4911             {
4912                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4913                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4914                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4915                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4916             }
4917             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4918             {
4919                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4920                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4921                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4922                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4923             }
4924         }
4926         //Style
4927         {
4928             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4930             gboolean active = gtk_toggle_button_get_active (button);
4931             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4933             if (active != check)
4934             {
4935                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4936                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4937                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4938             }
4939         }
4941         {
4942             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4944             gboolean active = gtk_toggle_button_get_active (button);
4945             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4947             if (active != check)
4948             {
4949                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4950                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4951                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4952             }
4953         }
4955         //Orientation
4956         //locking both buttons, changing one affect all group (both)
4957         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4958         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4960         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4961         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4963         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4964         {
4965             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4966         }
4967         else
4968         {
4969             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4970         }
4971         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4972         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4973     }
4975     sp_style_unref(query);
4978 void
4979 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4981     sp_text_toolbox_selection_changed (selection, tbl);
4984 void
4985 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4987     sp_text_toolbox_selection_changed (NULL, tbl);
4990 void
4991 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
4992                                 GObject             *tbl)
4994     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
4995     GtkTreeModel *model = 0;
4996     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4997     GtkTreeIter   iter;
4998     char         *family = 0;
5000     gdk_pointer_ungrab (GDK_CURRENT_TIME);
5001     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5003     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
5004         return;
5005     }
5007     gtk_tree_model_get (model, &iter, 0, &family, -1);
5009     if (g_object_get_data (G_OBJECT (selection), "block"))
5010     {
5011         gtk_entry_set_text (GTK_ENTRY (entry), family);
5012         return;
5013     }
5015     gtk_entry_set_text (GTK_ENTRY (entry), family);
5017     SPStyle *query =
5018         sp_style_new (SP_ACTIVE_DOCUMENT);
5020     int result_fontspec =
5021         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5023     //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5025     SPCSSAttr *css = sp_repr_css_attr_new ();
5028     // First try to get the font spec from the stored value
5029     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
5031     if (fontSpec.empty()) {
5032         // Construct a new font specification if it does not yet exist
5033         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5034         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5035         fontFromStyle->Unref();
5036     }
5038     if (!fontSpec.empty()) {
5039         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
5040         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
5041             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
5042             if (font) {
5043                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5045                 // Set all the these just in case they were altered when finding the best
5046                 // match for the new family and old style...
5048                 gchar c[256];
5050                 font->Family(c, 256);
5051                 sp_repr_css_set_property (css, "font-family", c);
5053                 font->Attribute( "weight", c, 256);
5054                 sp_repr_css_set_property (css, "font-weight", c);
5056                 font->Attribute("style", c, 256);
5057                 sp_repr_css_set_property (css, "font-style", c);
5059                 font->Attribute("stretch", c, 256);
5060                 sp_repr_css_set_property (css, "font-stretch", c);
5062                 font->Attribute("variant", c, 256);
5063                 sp_repr_css_set_property (css, "font-variant", c);
5065                 font->Unref();
5066             }
5067         }
5068     }
5070     // If querying returned nothing, set the default style of the tool (for new texts)
5071     if (result_fontspec == QUERY_STYLE_NOTHING)
5072     {
5073         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5074         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
5075     }
5076     else
5077     {
5078         sp_desktop_set_style (desktop, css, true, true);
5079     }
5081     sp_style_unref(query);
5083     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5084                                    _("Text: Change font family"));
5085     sp_repr_css_attr_unref (css);
5086     free (family);
5087     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5089     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5092 void
5093 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
5094                                        GObject      *tbl)
5096     const char *family = gtk_entry_get_text (entry);
5098     try {
5099         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
5100         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5101         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5102         gtk_tree_selection_select_path (selection, path.gobj());
5103         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5104         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5105     } catch (...) {
5106         if (family && strlen (family))
5107         {
5108             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5109         }
5110     }
5113 void
5114 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
5115                                    gpointer          data)
5117     if (g_object_get_data (G_OBJECT (button), "block")) return;
5118     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
5119     int prop = GPOINTER_TO_INT(data);
5121     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5122     SPCSSAttr *css = sp_repr_css_attr_new ();
5124     switch (prop)
5125     {
5126         case 0:
5127         {
5128             sp_repr_css_set_property (css, "text-anchor", "start");
5129             sp_repr_css_set_property (css, "text-align", "start");
5130             break;
5131         }
5132         case 1:
5133         {
5134             sp_repr_css_set_property (css, "text-anchor", "middle");
5135             sp_repr_css_set_property (css, "text-align", "center");
5136             break;
5137         }
5139         case 2:
5140         {
5141             sp_repr_css_set_property (css, "text-anchor", "end");
5142             sp_repr_css_set_property (css, "text-align", "end");
5143             break;
5144         }
5146         case 3:
5147         {
5148             sp_repr_css_set_property (css, "text-anchor", "start");
5149             sp_repr_css_set_property (css, "text-align", "justify");
5150             break;
5151         }
5152     }
5154     SPStyle *query =
5155         sp_style_new (SP_ACTIVE_DOCUMENT);
5156     int result_numbers =
5157         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5159     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5160     if (result_numbers == QUERY_STYLE_NOTHING)
5161     {
5162         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5163     }
5165     sp_style_unref(query);
5167     sp_desktop_set_style (desktop, css, true, true);
5168     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5169                                    _("Text: Change alignment"));
5170     sp_repr_css_attr_unref (css);
5172     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5175 void
5176 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
5177                                gpointer          data)
5179     if (g_object_get_data (G_OBJECT (button), "block")) return;
5181     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5182     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5183     int          prop       = GPOINTER_TO_INT(data);
5184     bool         active     = gtk_toggle_button_get_active (button);
5186     SPStyle *query =
5187         sp_style_new (SP_ACTIVE_DOCUMENT);
5189     int result_fontspec =
5190         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5192     //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5193     //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5194     //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5196     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
5197     Glib::ustring newFontSpec = "";
5199     if (fontSpec.empty()) {
5200         // Construct a new font specification if it does not yet exist
5201         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5202         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5203         fontFromStyle->Unref();
5204     }
5206     switch (prop)
5207     {
5208         case 0:
5209         {
5210             if (!fontSpec.empty()) {
5211                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
5212             }
5213             if (fontSpec != newFontSpec) {
5214                 // Don't even set the bold if the font didn't exist on the system
5215                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
5216             }
5217             break;
5218         }
5220         case 1:
5221         {
5222             if (!fontSpec.empty()) {
5223                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
5224             }
5225             if (fontSpec != newFontSpec) {
5226                 // Don't even set the italic if the font didn't exist on the system
5227                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
5228             }
5229             break;
5230         }
5231     }
5233     if (!newFontSpec.empty()) {
5234         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5235     }
5237     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5238     if (result_fontspec == QUERY_STYLE_NOTHING)
5239     {
5240         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5241     }
5243     sp_style_unref(query);
5245     sp_desktop_set_style (desktop, css, true, true);
5246     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5247                                    _("Text: Change font style"));
5248     sp_repr_css_attr_unref (css);
5250     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5253 void
5254 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
5255                                      gpointer         data)
5257     if (g_object_get_data (G_OBJECT (button), "block")) {
5258         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5259         return;
5260     }
5262     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5263     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5264     int          prop       = GPOINTER_TO_INT(data);
5266     switch (prop)
5267     {
5268         case 0:
5269         {
5270             sp_repr_css_set_property (css, "writing-mode", "lr");
5271             break;
5272         }
5274         case 1:
5275         {
5276             sp_repr_css_set_property (css, "writing-mode", "tb");
5277             break;
5278         }
5279     }
5281     SPStyle *query =
5282         sp_style_new (SP_ACTIVE_DOCUMENT);
5283     int result_numbers =
5284         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5286     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5287     if (result_numbers == QUERY_STYLE_NOTHING)
5288     {
5289         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5290     }
5292     sp_desktop_set_style (desktop, css, true, true);
5293     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5294                                    _("Text: Change orientation"));
5295     sp_repr_css_attr_unref (css);
5297     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5300 gboolean
5301 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5303     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5304     if (!desktop) return FALSE;
5306     switch (get_group0_keyval (event)) {
5307         case GDK_Escape: // defocus
5308             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5309             sp_text_toolbox_selection_changed (NULL, tbl); // update
5310             return TRUE; // I consumed the event
5311             break;
5312     }
5313     return FALSE;
5316 gboolean
5317 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5319     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5320     if (!desktop) return FALSE;
5322     switch (get_group0_keyval (event)) {
5323         case GDK_KP_Enter:
5324         case GDK_Return:
5325         case GDK_Escape: // defocus
5326             gtk_widget_hide (w);
5327             popdown_visible = false;
5328             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5329             return TRUE; // I consumed the event
5330             break;
5331         case GDK_w:
5332         case GDK_W:
5333             if (event->state & GDK_CONTROL_MASK) {
5334                 gtk_widget_hide (w);
5335                 popdown_visible = false;
5336                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5337                 return TRUE; // I consumed the event
5338             }
5339             break;
5340     }
5341     return FALSE;
5345 void
5346 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
5347                                GObject     *tbl)
5349     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5351     if (g_object_get_data (tbl, "size-block")) return;
5353     // If this is not from selecting a size in the list (in which case get_active will give the
5354     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5355     // process this event. This fixes GTK's stupid insistence on sending an activate change every
5356     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5357     if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5358         return;
5360     gchar *endptr;
5361     gdouble value = -1;
5362     char *text = gtk_combo_box_get_active_text (cbox);
5363     if (text) {
5364         value = g_strtod (text, &endptr);
5365         if (endptr == text) // conversion failed, non-numeric input
5366             value = -1;
5367         free (text);
5368     }
5369     if (value <= 0) {
5370         return; // could not parse value
5371     }
5373     SPCSSAttr *css = sp_repr_css_attr_new ();
5374     Inkscape::CSSOStringStream osfs;
5375     osfs << value;
5376     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5378     SPStyle *query =
5379         sp_style_new (SP_ACTIVE_DOCUMENT);
5380     int result_numbers =
5381         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5383     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5384     if (result_numbers == QUERY_STYLE_NOTHING)
5385     {
5386         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5387     }
5389     sp_style_unref(query);
5391     sp_desktop_set_style (desktop, css, true, true);
5392     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5393                                    _("Text: Change font size"));
5394     sp_repr_css_attr_unref (css);
5396     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5399 gboolean
5400 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5402     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5403     if (!desktop) return FALSE;
5405     if (!g_object_get_data (tbl, "esc-pressed")) {
5406         g_object_set_data (tbl, "enter-pressed", gpointer(1));
5407         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5408         sp_text_toolbox_size_changed (cbox, tbl);
5409         g_object_set_data (tbl, "enter-pressed", gpointer(0));
5410     }
5411     return FALSE; // I consumed the event
5415 gboolean
5416 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5418     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5419     if (!desktop) return FALSE;
5421     switch (get_group0_keyval (event)) {
5422         case GDK_Escape: // defocus
5423             g_object_set_data (tbl, "esc-pressed", gpointer(1));
5424             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5425             g_object_set_data (tbl, "esc-pressed", gpointer(0));
5426             return TRUE; // I consumed the event
5427             break;
5428         case GDK_Return: // defocus
5429         case GDK_KP_Enter:
5430             g_object_set_data (tbl, "enter-pressed", gpointer(1));
5431             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5432             sp_text_toolbox_size_changed (cbox, tbl);
5433             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5434             g_object_set_data (tbl, "enter-pressed", gpointer(0));
5435             return TRUE; // I consumed the event
5436             break;
5437     }
5438     return FALSE;
5441 void
5442 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
5443                                          GObject            *tbl)
5445     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
5446     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5447     int x, y;
5449     if (!popdown_visible)
5450     {
5451         gdk_window_get_origin (widget->window, &x, &y);
5452         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
5453         gtk_widget_show_all (popdown);
5454         //sp_transientize (popdown);
5456         gdk_pointer_grab (widget->window, TRUE,
5457                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
5458                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
5459                                         GDK_POINTER_MOTION_MASK),
5460                           NULL, NULL, GDK_CURRENT_TIME);
5462         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
5464         popdown_visible = true;
5465     }
5466     else
5467     {
5468         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5469         gdk_pointer_ungrab (GDK_CURRENT_TIME);
5470         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5471         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5472         gtk_widget_hide (popdown);
5473         popdown_visible = false;
5474     }
5477 gboolean
5478 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
5479                                  GdkEventFocus    */*event*/,
5480                                  GObject          */*tbl*/)
5482     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
5483     return FALSE;
5486 gboolean
5487 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
5488                                    GdkEventFocus    */*event*/,
5489                                    GObject          */*tbl*/)
5491     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5493     if (popdown_hasfocus) {
5494         gtk_widget_hide (popdown);
5495         popdown_hasfocus = false;
5496         popdown_visible = false;
5497         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5498         return TRUE;
5499     }
5500     return FALSE;
5503 gboolean
5504 sp_text_toolbox_popdown_focus_in (GtkWidget        */*popdown*/,
5505                                    GdkEventFocus    */*event*/,
5506                                    GObject          */*tbl*/)
5508     popdown_hasfocus = true;
5509     return TRUE;
5513 void
5514 cell_data_func  (GtkTreeViewColumn */*column*/,
5515                  GtkCellRenderer   *cell,
5516                  GtkTreeModel      *tree_model,
5517                  GtkTreeIter       *iter,
5518                  gpointer           /*data*/)
5520     char        *family,
5521         *family_escaped,
5522         *sample_escaped;
5524     static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
5526     gtk_tree_model_get (tree_model, iter, 0, &family, -1);
5528     family_escaped = g_markup_escape_text (family, -1);
5529     sample_escaped = g_markup_escape_text (sample, -1);
5531     std::stringstream markup;
5532     markup << family_escaped << "  <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
5533     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
5535     free (family);
5536     free (family_escaped);
5537     free (sample_escaped);
5540 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
5541     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
5542     if (completion) {
5543         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
5544         g_object_unref (completion);
5545     }
5548 GtkWidget*
5549 sp_text_toolbox_new (SPDesktop *desktop)
5551     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
5552     GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
5554     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
5555     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
5557     GtkTooltips *tt = gtk_tooltips_new();
5558     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
5560     ////////////Family
5561     //Window
5562     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5563     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
5565     //Entry
5566     GtkWidget           *entry = gtk_entry_new ();
5567     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
5568     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
5569     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
5570     gtk_entry_completion_set_text_column (completion, 0);
5571     gtk_entry_completion_set_minimum_key_length (completion, 1);
5572     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
5573     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
5574     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
5575     gtk_toolbar_append_widget( tbl, entry, "", "" );
5576     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
5578     //Button
5579     GtkWidget   *button = gtk_button_new ();
5580     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
5581     gtk_toolbar_append_widget( tbl, button, "", "");
5583     //Popdown
5584     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
5585     GtkWidget           *treeview = gtk_tree_view_new ();
5587     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
5588     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
5589     gtk_tree_view_column_pack_start (column, cell, FALSE);
5590     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
5591     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5592     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5594     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5595     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5596     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5598     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5600     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5601     gtk_container_add (GTK_CONTAINER (sw), treeview);
5603     gtk_container_add (GTK_CONTAINER (window), sw);
5604     gtk_widget_set_size_request (window, 300, 450);
5606     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5607     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5608     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5610     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5612     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5613     g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5614     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5616     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5617     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5619     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5620     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5621     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5622     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5623     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5625     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
5626     GtkWidget *box = gtk_event_box_new ();
5627     gtk_container_add (GTK_CONTAINER (box), image);
5628     gtk_toolbar_append_widget( tbl, box, "", "");
5629     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5630     GtkTooltips *tooltips = gtk_tooltips_new ();
5631     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5632     gtk_widget_hide (GTK_WIDGET (box));
5633     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
5635     ////////////Size
5636     const char *sizes[] = {
5637         "4", "6", "8", "9", "10", "11", "12", "13", "14",
5638         "16", "18", "20", "22", "24", "28",
5639         "32", "36", "40", "48", "56", "64", "72", "144"
5640     };
5642     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
5643     for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
5644     gtk_widget_set_size_request (cbox, 80, -1);
5645     gtk_toolbar_append_widget( tbl, cbox, "", "");
5646     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
5647     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
5648     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
5649     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
5651     ////////////Text anchor
5652     GtkWidget *group   = gtk_radio_button_new (NULL);
5653     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
5654     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
5656     // left
5657     GtkWidget *rbutton = group;
5658     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5659     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
5660     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5662     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5663     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
5664     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
5665     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
5667     // center
5668     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5669     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5670     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
5671     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5673     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5674     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
5675     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5676     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5678     // right
5679     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5680     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5681     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
5682     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5684     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5685     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
5686     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5687     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5689     // fill
5690     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5691     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5692     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
5693     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5695     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5696     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
5697     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
5698     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
5700     gtk_toolbar_append_widget( tbl, row, "", "");
5702     //spacer
5703     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5705     ////////////Text style
5706     row = gtk_hbox_new (FALSE, 4);
5708     // bold
5709     rbutton = gtk_toggle_button_new ();
5710     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5711     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
5712     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5713     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
5715     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5716     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
5717     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
5719     // italic
5720     rbutton = gtk_toggle_button_new ();
5721     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5722     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
5723     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5724     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
5726     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5727     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
5728     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
5730     gtk_toolbar_append_widget( tbl, row, "", "");
5732     //spacer
5733     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5735     ////////////Text orientation
5736     group   = gtk_radio_button_new (NULL);
5737     row     = gtk_hbox_new (FALSE, 4);
5738     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
5740     // horizontal
5741     rbutton = group;
5742     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5743     gtk_container_add           (GTK_CONTAINER (rbutton),
5744                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
5745     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5746     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
5748     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5749     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
5750     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
5752     // vertical
5753     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5754     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5755     gtk_container_add           (GTK_CONTAINER (rbutton),
5756                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
5757     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5758     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
5760     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5761     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
5762     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
5763     gtk_toolbar_append_widget( tbl, row, "", "" );
5766     //watch selection
5767     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
5769     sigc::connection *c_selection_changed =
5770         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5771                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
5772     pool->add_connection ("selection-changed", c_selection_changed);
5774     sigc::connection *c_selection_modified =
5775         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5776                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
5777     pool->add_connection ("selection-modified", c_selection_modified);
5779     sigc::connection *c_subselection_changed =
5780         new sigc::connection (desktop->connectToolSubselectionChanged
5781                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
5782     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
5784     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
5787     gtk_widget_show_all( GTK_WIDGET(tbl) );
5789     return GTK_WIDGET(tbl);
5790 } // end of sp_text_toolbox_new()
5792 }//<unnamed> namespace
5795 //#########################
5796 //##      Connector      ##
5797 //#########################
5799 static void sp_connector_path_set_avoid(void)
5801     cc_selection_set_avoid(true);
5805 static void sp_connector_path_set_ignore(void)
5807     cc_selection_set_avoid(false);
5812 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
5814     // quit if run by the _changed callbacks
5815     if (g_object_get_data( tbl, "freeze" )) {
5816         return;
5817     }
5819     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5820     SPDocument *doc = sp_desktop_document(desktop);
5822     if (!sp_document_get_undo_sensitive(doc))
5823     {
5824         return;
5825     }
5827     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5829     if ( repr->attribute("inkscape:connector-spacing") ) {
5830         gdouble priorValue = gtk_adjustment_get_value(adj);
5831         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
5832         if ( priorValue == gtk_adjustment_get_value(adj) ) {
5833             return;
5834         }
5835     } else if ( adj->value == defaultConnSpacing ) {
5836         return;
5837     }
5839     // in turn, prevent callbacks from responding
5840     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5842     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
5843     SP_OBJECT(desktop->namedview)->updateRepr();
5845     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
5846     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
5847         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
5848         NR::Matrix m = NR::identity();
5849         avoid_item_move(&m, item);
5850     }
5852     if (items) {
5853         g_slist_free(items);
5854     }
5856     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
5857             _("Change connector spacing"));
5859     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5861     spinbutton_defocus(GTK_OBJECT(tbl));
5864 static void sp_connector_graph_layout(void)
5866     if (!SP_ACTIVE_DESKTOP) return;
5868     // hack for clones, see comment in align-and-distribute.cpp
5869     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5870     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5872     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5874     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5876     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5879 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5881     if ( gtk_toggle_action_get_active( act ) ) {
5882         prefs_set_string_attribute("tools.connector", "directedlayout",
5883                 "true");
5884     } else {
5885         prefs_set_string_attribute("tools.connector", "directedlayout",
5886                 "false");
5887     }
5890 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5892     if ( gtk_toggle_action_get_active( act ) ) {
5893         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5894                 "true");
5895     } else {
5896         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5897                 "false");
5898     }
5902 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5904     prefs_set_double_attribute("tools.connector", "length", adj->value);
5905     spinbutton_defocus(GTK_OBJECT(tbl));
5908 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5909                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5910                                             bool /*is_interactive*/, gpointer data)
5912     GtkWidget *tbl = GTK_WIDGET(data);
5914     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5915         return;
5916     }
5917     if (strcmp(name, "inkscape:connector-spacing") != 0) {
5918         return;
5919     }
5921     GtkAdjustment *adj = (GtkAdjustment*)
5922             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5923     gdouble spacing = defaultConnSpacing;
5924     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5926     gtk_adjustment_set_value(adj, spacing);
5930 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5931     NULL, /* child_added */
5932     NULL, /* child_removed */
5933     connector_tb_event_attr_changed,
5934     NULL, /* content_changed */
5935     NULL  /* order_changed */
5936 };
5939 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5941     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
5943     {
5944         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5945                                           _("Avoid"),
5946                                           _("Make connectors avoid selected objects"),
5947                                           "connector_avoid",
5948                                           secondarySize );
5949         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5950         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5951     }
5953     {
5954         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5955                                           _("Ignore"),
5956                                           _("Make connectors ignore selected objects"),
5957                                           "connector_ignore",
5958                                           secondarySize );
5959         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5960         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5961     }
5963     EgeAdjustmentAction* eact = 0;
5965     // Spacing spinbox
5966     eact = create_adjustment_action( "ConnectorSpacingAction",
5967                                      _("Connector Spacing"), _("Spacing:"),
5968                                      _("The amount of space left around objects by auto-routing connectors"),
5969                                      "tools.connector", "spacing", defaultConnSpacing,
5970                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5971                                      0, 100, 1.0, 10.0,
5972                                      0, 0, 0,
5973                                      connector_spacing_changed, 1, 0 );
5974     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5976     // Graph (connector network) layout
5977     {
5978         InkAction* inky = ink_action_new( "ConnectorGraphAction",
5979                                           _("Graph"),
5980                                           _("Nicely arrange selected connector network"),
5981                                           "graph_layout",
5982                                           secondarySize );
5983         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5984         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5985     }
5987     // Default connector length spinbox
5988     eact = create_adjustment_action( "ConnectorLengthAction",
5989                                      _("Connector Length"), _("Length:"),
5990                                      _("Ideal length for connectors when layout is applied"),
5991                                      "tools.connector", "length", 100,
5992                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5993                                      10, 1000, 10.0, 100.0,
5994                                      0, 0, 0,
5995                                      connector_length_changed, 1, 0 );
5996     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5999     // Directed edges toggle button
6000     {
6001         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
6002                                                       _("Downwards"),
6003                                                       _("Make connectors with end-markers (arrows) point downwards"),
6004                                                       "directed_graph",
6005                                                       Inkscape::ICON_SIZE_DECORATION );
6006         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6008         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
6009         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6010                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6012         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
6013     }
6015     // Avoid overlaps toggle button
6016     {
6017         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
6018                                                       _("Remove overlaps"),
6019                                                       _("Do not allow overlapping shapes"),
6020                                                       "remove_overlaps",
6021                                                       Inkscape::ICON_SIZE_DECORATION );
6022         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6024         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
6025         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6026                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6028         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
6029     }
6031     // Code to watch for changes to the connector-spacing attribute in
6032     // the XML.
6033     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6034     g_assert(repr != NULL);
6036     purge_repr_listener( holder, holder );
6038     if (repr) {
6039         g_object_set_data( holder, "repr", repr );
6040         Inkscape::GC::anchor(repr);
6041         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
6042         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
6043     }
6044 } // end of sp_connector_toolbox_prep()
6047 //#########################
6048 //##     Paintbucket     ##
6049 //#########################
6051 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
6053     gint channels = ege_select_one_action_get_active( act );
6054     flood_channels_set_channels( channels );
6057 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
6059     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
6062 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
6064     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
6067 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
6069     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
6070     SPUnit const *unit = tracker->getActiveUnit();
6072     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
6074     prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
6077 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
6079     // FIXME: make defaults settable via Inkscape Options
6080     struct KeyValue {
6081         char const *key;
6082         double value;
6083     } const key_values[] = {
6084         {"threshold", 15},
6085         {"offset", 0.0}
6086     };
6088     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
6089         KeyValue const &kv = key_values[i];
6090         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
6091         if ( adj ) {
6092             gtk_adjustment_set_value(adj, kv.value);
6093         }
6094     }
6096     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
6097     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
6098     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
6099     ege_select_one_action_set_active( autogap_action, 0 );
6102 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6104     EgeAdjustmentAction* eact = 0;
6106     {
6107         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6109         GList* items = 0;
6110         gint count = 0;
6111         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
6112         {
6113             GtkTreeIter iter;
6114             gtk_list_store_append( model, &iter );
6115             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6116             count++;
6117         }
6118         g_list_free( items );
6119         items = 0;
6120         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
6121         g_object_set( act1, "short_label", _("Fill by:"), NULL );
6122         ege_select_one_action_set_appearance( act1, "compact" );
6123         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
6124         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
6125         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
6126         g_object_set_data( holder, "channels_action", act1 );
6127     }
6129     // Spacing spinbox
6130     {
6131         eact = create_adjustment_action(
6132             "ThresholdAction",
6133             _("Fill Threshold"), _("Threshold:"),
6134             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
6135             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
6136             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
6137             0, 0, 0,
6138             paintbucket_threshold_changed, 1, 0 );
6140         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6141         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6142     }
6144     // Create the units menu.
6145     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
6146     const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
6147     if (stored_unit)
6148         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
6149     g_object_set_data( holder, "tracker", tracker );
6150     {
6151         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
6152         gtk_action_group_add_action( mainActions, act );
6153     }
6155     // Offset spinbox
6156     {
6157         eact = create_adjustment_action(
6158             "OffsetAction",
6159             _("Grow/shrink by"), _("Grow/shrink by:"),
6160             _("The amount to grow (positive) or shrink (negative) the created fill path"),
6161             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
6162             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
6163             0, 0, 0,
6164             paintbucket_offset_changed, 1, 2);
6165         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
6167         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6168     }
6170     /* Auto Gap */
6171     {
6172         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6174         GList* items = 0;
6175         gint count = 0;
6176         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
6177         {
6178             GtkTreeIter iter;
6179             gtk_list_store_append( model, &iter );
6180             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6181             count++;
6182         }
6183         g_list_free( items );
6184         items = 0;
6185         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
6186         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
6187         ege_select_one_action_set_appearance( act2, "compact" );
6188         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
6189         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
6190         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
6191         g_object_set_data( holder, "autogap_action", act2 );
6192     }
6194     /* Reset */
6195     {
6196         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
6197                                           _("Defaults"),
6198                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
6199                                           GTK_STOCK_CLEAR );
6200         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
6201         gtk_action_group_add_action( mainActions, act );
6202         gtk_action_set_sensitive( act, TRUE );
6203     }
6207 /*
6208   Local Variables:
6209   mode:c++
6210   c-file-style:"stroustrup"
6211   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
6212   indent-tabs-mode:nil
6213   fill-column:99
6214   End:
6215 */
6216 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :