Code

comments
[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 "pen-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 "tools-switch.h"
84 #include "selection.h"
85 #include "selection-chemistry.h"
86 #include "document-private.h"
87 #include "desktop-style.h"
88 #include "../libnrtype/font-lister.h"
89 #include "../libnrtype/font-instance.h"
90 #include "../connection-pool.h"
91 #include "../prefs-utils.h"
92 #include "../inkscape-stock.h"
93 #include "icon.h"
94 #include "graphlayout/graphlayout.h"
95 #include "interface.h"
96 #include "shortcuts.h"
98 #include "mod360.h"
100 #include "toolbox.h"
102 #include "flood-context.h"
104 #include "ink-action.h"
105 #include "ege-adjustment-action.h"
106 #include "ege-output-action.h"
107 #include "ege-select-one-action.h"
108 #include "helper/unit-tracker.h"
110 #include "svg/css-ostringstream.h"
112 #include "widgets/calligraphic-profile-rename.h"
114 using Inkscape::UnitTracker;
116 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
117 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
119 static void       sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
120 static void       sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
121 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
122 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
123 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 static void       box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
126 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
127 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
128 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
129 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
130 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
131 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
132 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
133 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
134 static void       sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
136 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
139 Inkscape::IconSize prefToSize( gchar const *path, gchar const *attr, int base ) {
140     static Inkscape::IconSize sizeChoices[] = {
141         Inkscape::ICON_SIZE_LARGE_TOOLBAR,
142         Inkscape::ICON_SIZE_SMALL_TOOLBAR,
143         Inkscape::ICON_SIZE_MENU
144     };
145     int index = prefs_get_int_attribute_limited( path, attr, base, 0, G_N_ELEMENTS(sizeChoices) );
146     return sizeChoices[index];
149 static struct {
150     gchar const *type_name;
151     gchar const *data_name;
152     sp_verb_t verb;
153     sp_verb_t doubleclick_verb;
154 } const tools[] = {
155     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
156     { "SPNodeContext",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
157     { "SPTweakContext",    "tweak_tool",     SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
158     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
159     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
160     { "Box3DContext",      "3dbox_tool",     SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
161     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
162     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
163     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
164     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
165     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
166     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
167     { "SPEraserContext",   "eraser_tool",    SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
168     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
169     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
170     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
171     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
172     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
173     { NULL, NULL, 0, 0 }
174 };
176 static struct {
177     gchar const *type_name;
178     gchar const *data_name;
179     GtkWidget *(*create_func)(SPDesktop *desktop);
180     void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
181     gchar const *ui_name;
182     gint swatch_verb_id;
183     gchar const *swatch_tool;
184     gchar const *swatch_tip;
185 } const aux_toolboxes[] = {
186     { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep,            "SelectToolbar",
187       SP_VERB_INVALID, 0, 0},
188     { "SPNodeContext",   "node_toolbox",   0, sp_node_toolbox_prep,              "NodeToolbar",
189       SP_VERB_INVALID, 0, 0},
190     { "SPTweakContext",   "tweak_toolbox",   0, sp_tweak_toolbox_prep,              "TweakToolbar",
191       SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", N_("Color/opacity used for color tweaking")},
192     { "SPZoomContext",   "zoom_toolbox",   0, sp_zoom_toolbox_prep,              "ZoomToolbar",
193       SP_VERB_INVALID, 0, 0},
194     { "SPStarContext",   "star_toolbox",   0, sp_star_toolbox_prep,              "StarToolbar",
195       SP_VERB_CONTEXT_STAR_PREFS,   "tools.shapes.star",     N_("Style of new stars")},
196     { "SPRectContext",   "rect_toolbox",   0, sp_rect_toolbox_prep,              "RectToolbar",
197       SP_VERB_CONTEXT_RECT_PREFS,   "tools.shapes.rect",     N_("Style of new rectangles")},
198     { "Box3DContext",  "3dbox_toolbox",  0, box3d_toolbox_prep,             "3DBoxToolbar",
199       SP_VERB_CONTEXT_3DBOX_PREFS,  "tools.shapes.3dbox",    N_("Style of new 3D boxes")},
200     { "SPArcContext",    "arc_toolbox",    0, sp_arc_toolbox_prep,               "ArcToolbar",
201       SP_VERB_CONTEXT_ARC_PREFS,    "tools.shapes.arc",      N_("Style of new ellipses")},
202     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
203       SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral",   N_("Style of new spirals")},
204     { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep,            "PencilToolbar",
205       SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", N_("Style of new paths created by Pencil")},
206     { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep,                     "PenToolbar",
207       SP_VERB_CONTEXT_PEN_PREFS,    "tools.freehand.pen",    N_("Style of new paths created by Pen")},
208     { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
209       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", N_("Style of new calligraphic strokes")},
210     { "SPEraserContext", "eraser_toolbox", 0, sp_eraser_toolbox_prep,"EraserToolbar",
211       SP_VERB_CONTEXT_ERASER_PREFS, "tools.eraser", _("TBD")},
212     { "SPTextContext",   "text_toolbox",   sp_text_toolbox_new, 0,               0,
213       SP_VERB_INVALID, 0, 0},
214     { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep,         "DropperToolbar",
215       SP_VERB_INVALID, 0, 0},
216     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0,       0,
217       SP_VERB_INVALID, 0, 0},
218     { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep,   "ConnectorToolbar",
219       SP_VERB_INVALID, 0, 0},
220     { "SPFloodContext",  "paintbucket_toolbox",  0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
221       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", N_("Style of Paint Bucket fill objects")},
222     { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
223 };
225 #define TOOLBAR_SLIDER_HINT "full"
227 static gchar const * ui_descr =
228         "<ui>"
229         "  <toolbar name='SelectToolbar'>"
230         "    <toolitem action='EditSelectAll' />"
231         "    <toolitem action='EditSelectAllInAllLayers' />"
232         "    <toolitem action='EditDeselect' />"
233         "    <separator />"
234         "    <toolitem action='ObjectRotate90CCW' />"
235         "    <toolitem action='ObjectRotate90' />"
236         "    <toolitem action='ObjectFlipHorizontally' />"
237         "    <toolitem action='ObjectFlipVertically' />"
238         "    <separator />"
239         "    <toolitem action='SelectionToBack' />"
240         "    <toolitem action='SelectionLower' />"
241         "    <toolitem action='SelectionRaise' />"
242         "    <toolitem action='SelectionToFront' />"
243         "    <separator />"
244         "    <toolitem action='XAction' />"
245         "    <toolitem action='YAction' />"
246         "    <toolitem action='WidthAction' />"
247         "    <toolitem action='LockAction' />"
248         "    <toolitem action='HeightAction' />"
249         "    <toolitem action='UnitsAction' />"
250         "    <separator />"
251         "    <toolitem action='transform_affect_label' />"
252         "    <toolitem action='transform_stroke' />"
253         "    <toolitem action='transform_corners' />"
254         "    <toolitem action='transform_gradient' />"
255         "    <toolitem action='transform_pattern' />"
256         "  </toolbar>"
258         "  <toolbar name='NodeToolbar'>"
259         "    <toolitem action='NodeInsertAction' />"
260         "    <toolitem action='NodeDeleteAction' />"
261         "    <separator />"
262         "    <toolitem action='NodeJoinAction' />"
263         "    <toolitem action='NodeBreakAction' />"
264         "    <separator />"
265         "    <toolitem action='NodeJoinSegmentAction' />"
266         "    <toolitem action='NodeDeleteSegmentAction' />"
267         "    <separator />"
268         "    <toolitem action='NodeCuspAction' />"
269         "    <toolitem action='NodeSmoothAction' />"
270         "    <toolitem action='NodeSymmetricAction' />"
271         "    <separator />"
272         "    <toolitem action='NodeLineAction' />"
273         "    <toolitem action='NodeCurveAction' />"
274         "    <separator />"
275         "    <toolitem action='ObjectToPath' />"
276         "    <toolitem action='StrokeToPath' />"
277         "    <separator />"
278         "    <toolitem action='NodeXAction' />"
279         "    <toolitem action='NodeYAction' />"
280         "    <toolitem action='NodeUnitsAction' />"
281         "    <separator />"
282         "    <toolitem action='ObjectEditClipPathAction' />"
283         "    <toolitem action='ObjectEditMaskPathAction' />"
284         "    <toolitem action='EditNextLPEParameterAction' />"
285         "    <separator />"
286         "    <toolitem action='NodesShowHandlesAction' />"
287         "    <toolitem action='NodesShowHelperpath' />"
288         "  </toolbar>"
290         "  <toolbar name='TweakToolbar'>"
291         "    <toolitem action='TweakWidthAction' />"
292         "    <separator />"
293         "    <toolitem action='TweakForceAction' />"
294         "    <toolitem action='TweakPressureAction' />"
295         "    <separator />"
296         "    <toolitem action='TweakModeAction' />"
297         "    <separator />"
298         "    <toolitem action='TweakFidelityAction' />"
299         "    <separator />"
300         "    <toolitem action='TweakChannelsLabel' />"
301         "    <toolitem action='TweakDoH' />"
302         "    <toolitem action='TweakDoS' />"
303         "    <toolitem action='TweakDoL' />"
304         "    <toolitem action='TweakDoO' />"
305         "  </toolbar>"
307         "  <toolbar name='ZoomToolbar'>"
308         "    <toolitem action='ZoomIn' />"
309         "    <toolitem action='ZoomOut' />"
310         "    <separator />"
311         "    <toolitem action='Zoom1:0' />"
312         "    <toolitem action='Zoom1:2' />"
313         "    <toolitem action='Zoom2:1' />"
314         "    <separator />"
315         "    <toolitem action='ZoomSelection' />"
316         "    <toolitem action='ZoomDrawing' />"
317         "    <toolitem action='ZoomPage' />"
318         "    <toolitem action='ZoomPageWidth' />"
319         "    <separator />"
320         "    <toolitem action='ZoomPrev' />"
321         "    <toolitem action='ZoomNext' />"
322         "  </toolbar>"
324         "  <toolbar name='StarToolbar'>"
325         "    <separator />"
326         "    <toolitem action='StarStateAction' />"
327         "    <separator />"
328         "    <toolitem action='FlatAction' />"
329         "    <separator />"
330         "    <toolitem action='MagnitudeAction' />"
331         "    <toolitem action='SpokeAction' />"
332         "    <toolitem action='RoundednessAction' />"
333         "    <toolitem action='RandomizationAction' />"
334         "    <separator />"
335         "    <toolitem action='StarResetAction' />"
336         "  </toolbar>"
338         "  <toolbar name='RectToolbar'>"
339         "    <toolitem action='RectStateAction' />"
340         "    <toolitem action='RectWidthAction' />"
341         "    <toolitem action='RectHeightAction' />"
342         "    <toolitem action='RadiusXAction' />"
343         "    <toolitem action='RadiusYAction' />"
344         "    <toolitem action='RectUnitsAction' />"
345         "    <separator />"
346         "    <toolitem action='RectResetAction' />"
347         "  </toolbar>"
349         "  <toolbar name='3DBoxToolbar'>"
350         "    <toolitem action='3DBoxAngleXAction' />"
351         "    <toolitem action='3DBoxVPXStateAction' />"
352         "    <separator />"
353         "    <toolitem action='3DBoxAngleYAction' />"
354         "    <toolitem action='3DBoxVPYStateAction' />"
355         "    <separator />"
356         "    <toolitem action='3DBoxAngleZAction' />"
357         "    <toolitem action='3DBoxVPZStateAction' />"
358         "  </toolbar>"
360         "  <toolbar name='SpiralToolbar'>"
361         "    <toolitem action='SpiralStateAction' />"
362         "    <toolitem action='SpiralRevolutionAction' />"
363         "    <toolitem action='SpiralExpansionAction' />"
364         "    <toolitem action='SpiralT0Action' />"
365         "    <separator />"
366         "    <toolitem action='SpiralResetAction' />"
367         "  </toolbar>"
369         "  <toolbar name='PenToolbar'>"
370         "    <toolitem action='FreehandModeActionPen' />"
371         "    <separator />"
372         "    <toolitem action='SetPenShapeAction'/>"
373         "  </toolbar>"
375         "  <toolbar name='PencilToolbar'>"
376         "    <toolitem action='FreehandModeActionPencil' />"
377         "    <separator />"
378         "    <toolitem action='PencilToleranceAction' />"
379         "    <separator />"
380         "    <toolitem action='PencilResetAction' />"
381         "    <separator />"
382         "    <toolitem action='SetPencilShapeAction'/>"
383         "  </toolbar>"
385         "  <toolbar name='CalligraphyToolbar'>"
386         "    <separator />"
387         "    <toolitem action='SetProfileAction'/>"
388         "    <separator />"
389         "    <toolitem action='CalligraphyWidthAction' />"
390         "    <toolitem action='PressureAction' />"
391         "    <toolitem action='TraceAction' />"
392         "    <toolitem action='ThinningAction' />"
393         "    <separator />"
394         "    <toolitem action='AngleAction' />"
395         "    <toolitem action='TiltAction' />"
396         "    <toolitem action='FixationAction' />"
397         "    <separator />"
398         "    <toolitem action='CapRoundingAction' />"
399         "    <separator />"
400         "    <toolitem action='TremorAction' />"
401         "    <toolitem action='WiggleAction' />"
402         "    <toolitem action='MassAction' />"
403         "    <separator />"
404         "  </toolbar>"
406         "  <toolbar name='ArcToolbar'>"
407         "    <toolitem action='ArcStateAction' />"
408         "    <separator />"
409         "    <toolitem action='ArcStartAction' />"
410         "    <toolitem action='ArcEndAction' />"
411         "    <separator />"
412         "    <toolitem action='ArcOpenAction' />"
413         "    <separator />"
414         "    <toolitem action='ArcResetAction' />"
415         "    <separator />"
416         "  </toolbar>"
418         "  <toolbar name='PaintbucketToolbar'>"
419         "    <toolitem action='ChannelsAction' />"
420         "    <separator />"
421         "    <toolitem action='ThresholdAction' />"
422         "    <separator />"
423         "    <toolitem action='OffsetAction' />"
424         "    <toolitem action='PaintbucketUnitsAction' />"
425         "    <separator />"
426         "    <toolitem action='AutoGapAction' />"
427         "    <separator />"
428         "    <toolitem action='PaintbucketResetAction' />"
429         "  </toolbar>"
431         "  <toolbar name='EraserToolbar'>"
432         "    <toolitem action='EraserWidthAction' />"
433         "    <separator />"
434         "    <toolitem action='EraserModeAction' />"
435         "  </toolbar>"
437         "  <toolbar name='DropperToolbar'>"
438         "    <toolitem action='DropperOpacityAction' />"
439         "    <toolitem action='DropperPickAlphaAction' />"
440         "    <toolitem action='DropperSetAlphaAction' />"
441         "  </toolbar>"
443         "  <toolbar name='ConnectorToolbar'>"
444         "    <toolitem action='ConnectorAvoidAction' />"
445         "    <toolitem action='ConnectorIgnoreAction' />"
446         "    <toolitem action='ConnectorSpacingAction' />"
447         "    <toolitem action='ConnectorGraphAction' />"
448         "    <toolitem action='ConnectorLengthAction' />"
449         "    <toolitem action='ConnectorDirectedAction' />"
450         "    <toolitem action='ConnectorOverlapAction' />"
451         "  </toolbar>"
453         "</ui>"
456 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
458 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
460 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
461 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
463 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
464 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
466 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
467 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
470 GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick( GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
471                                                               Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
472                                                               Inkscape::UI::View::View *view, GtkTooltips *tt);
474 class VerbAction : public Gtk::Action {
475 public:
476     static Glib::RefPtr<VerbAction> create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
478     virtual ~VerbAction();
479     virtual void set_active(bool active = true);
481 protected:
482     virtual Gtk::Widget* create_menu_item_vfunc();
483     virtual Gtk::Widget* create_tool_item_vfunc();
485     virtual void connect_proxy_vfunc(Gtk::Widget* proxy);
486     virtual void disconnect_proxy_vfunc(Gtk::Widget* proxy);
488     virtual void on_activate();
490 private:
491     Inkscape::Verb* verb;
492     Inkscape::Verb* verb2;
493     Inkscape::UI::View::View *view;
494     GtkTooltips *tooltips;
495     bool active;
497     VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
498 };
501 Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips)
503     Glib::RefPtr<VerbAction> result;
504     SPAction *action = verb->get_action(view);
505     if ( action ) {
506         //SPAction* action2 = verb2 ? verb2->get_action(view) : 0;
507         result = Glib::RefPtr<VerbAction>(new VerbAction(verb, verb2, view, tooltips));
508     }
510     return result;
513 VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips) :
514     Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(verb->get_image()), Glib::ustring(_(verb->get_name())), Glib::ustring(_(verb->get_tip()))),
515     verb(verb),
516     verb2(verb2),
517     view(view),
518     tooltips(tooltips),
519     active(false)
523 VerbAction::~VerbAction()
527 Gtk::Widget* VerbAction::create_menu_item_vfunc()
529 // First call in to get the icon rendered if present in SVG
530     Gtk::Widget *widget = sp_icon_get_icon( property_stock_id().get_value().get_string(), Inkscape::ICON_SIZE_MENU );
531     delete widget;
532     widget = 0;
534     Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
535 //     g_message("create_menu_item_vfunc() = %p  for '%s'", widg, verb->get_id());
536     return widg;
539 Gtk::Widget* VerbAction::create_tool_item_vfunc()
541 //     Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
542     Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
543     GtkWidget* toolbox = 0;
544     GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
545                                                                           SP_BUTTON_TYPE_TOGGLE,
546                                                                           verb,
547                                                                           verb2,
548                                                                           view,
549                                                                           tooltips );
550     if ( active ) {
551         sp_button_toggle_set_down( SP_BUTTON(button), active);
552     }
553     gtk_widget_show_all( button );
554     Gtk::Widget* wrapped = Glib::wrap(button);
555     Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
556     holder->add(*wrapped);
558 //     g_message("create_tool_item_vfunc() = %p  for '%s'", holder, verb->get_id());
559     return holder;
562 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
564 //     g_message("connect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
565     Gtk::Action::connect_proxy_vfunc(proxy);
568 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
570 //     g_message("disconnect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
571     Gtk::Action::disconnect_proxy_vfunc(proxy);
574 void VerbAction::set_active(bool active)
576     this->active = active;
577     Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
578     for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
579         Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
580         if (ti) {
581             // *should* have one child that is the SPButton
582             Gtk::Widget* child = ti->get_child();
583             if ( child && SP_IS_BUTTON(child->gobj()) ) {
584                 SPButton* button = SP_BUTTON(child->gobj());
585                 sp_button_toggle_set_down( button, active );
586             }
587         }
588     }
591 void VerbAction::on_activate()
593     if ( verb ) {
594         SPAction *action = verb->get_action(view);
595         if ( action ) {
596             sp_action_perform(action, 0);
597         }
598     }
601 /* Global text entry widgets necessary for update */
602 /* GtkWidget *dropper_rgb_entry,
603           *dropper_opacity_entry ; */
604 // should be made a private member once this is converted to class
606 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
607     connection->disconnect();
608     delete connection;
611 static void purge_repr_listener( GObject* obj, GObject* tbl )
613     (void)obj;
614     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
615     if (oldrepr) { // remove old listener
616         sp_repr_remove_listener_by_data(oldrepr, tbl);
617         Inkscape::GC::release(oldrepr);
618         oldrepr = 0;
619         g_object_set_data( tbl, "repr", NULL );
620     }
623 GtkWidget *
624 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
625                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
626                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
628     SPAction *action = verb->get_action(view);
629     if (!action) return NULL;
631     SPAction *doubleclick_action;
632     if (doubleclick_verb)
633         doubleclick_action = doubleclick_verb->get_action(view);
634     else
635         doubleclick_action = NULL;
637     /* fixme: Handle sensitive/unsensitive */
638     /* fixme: Implement sp_button_new_from_action */
639     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
640     gtk_widget_show(b);
643     unsigned int shortcut = sp_shortcut_get_primary(verb);
644     if (shortcut) {
645         gchar key[256];
646         sp_ui_shortcut_string(shortcut, key);
647         gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
648         if ( t ) {
649             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
650         }
651         g_free(tip);
652     } else {
653         if ( t ) {
654             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
655         }
656     }
658     return b;
662 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
664     SPAction* targetAction = SP_ACTION(user_data);
665     if ( targetAction ) {
666         sp_action_perform( targetAction, NULL );
667     }
670 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
672     if ( data ) {
673         GtkAction* act = GTK_ACTION(data);
674         gtk_action_set_sensitive( act, sensitive );
675     }
678 static SPActionEventVector action_event_vector = {
679     {NULL},
680     NULL,
681     NULL,
682     sp_action_action_set_sensitive,
683     NULL,
684     NULL
685 };
687 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
689     GtkAction* act = 0;
691     SPAction* targetAction = verb->get_action(view);
692     InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size  );
693     act = GTK_ACTION(inky);
694     gtk_action_set_sensitive( act, targetAction->sensitive );
696     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
698     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
699     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
701     return act;
704 Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
706     Inkscape::UI::View::View *view = desktop;
707     gint verbsToUse[] = {
708         // disabled until we have icons for them:
709         //find
710         //SP_VERB_EDIT_TILE,
711         //SP_VERB_EDIT_UNTILE,
712         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
713         SP_VERB_DIALOG_DISPLAY,
714         SP_VERB_DIALOG_FILL_STROKE,
715         SP_VERB_DIALOG_NAMEDVIEW,
716         SP_VERB_DIALOG_TEXT,
717         SP_VERB_DIALOG_XML_EDITOR,
718         SP_VERB_EDIT_CLONE,
719         SP_VERB_EDIT_COPY,
720         SP_VERB_EDIT_CUT,
721         SP_VERB_EDIT_DUPLICATE,
722         SP_VERB_EDIT_PASTE,
723         SP_VERB_EDIT_REDO,
724         SP_VERB_EDIT_UNDO,
725         SP_VERB_EDIT_UNLINK_CLONE,
726         SP_VERB_FILE_EXPORT,
727         SP_VERB_FILE_IMPORT,
728         SP_VERB_FILE_NEW,
729         SP_VERB_FILE_OPEN,
730         SP_VERB_FILE_PRINT,
731         SP_VERB_FILE_SAVE,
732         SP_VERB_OBJECT_TO_CURVE,
733         SP_VERB_SELECTION_GROUP,
734         SP_VERB_SELECTION_OUTLINE,
735         SP_VERB_SELECTION_UNGROUP,
736         SP_VERB_ZOOM_1_1,
737         SP_VERB_ZOOM_1_2,
738         SP_VERB_ZOOM_2_1,
739         SP_VERB_ZOOM_DRAWING,
740         SP_VERB_ZOOM_IN,
741         SP_VERB_ZOOM_NEXT,
742         SP_VERB_ZOOM_OUT,
743         SP_VERB_ZOOM_PAGE,
744         SP_VERB_ZOOM_PAGE_WIDTH,
745         SP_VERB_ZOOM_PREV,
746         SP_VERB_ZOOM_SELECTION,
747     };
749     Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
751     static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
752     Glib::RefPtr<Gtk::ActionGroup> mainActions;
753     if ( groups.find(desktop) != groups.end() ) {
754         mainActions = groups[desktop];
755     }
757     if ( !mainActions ) {
758         mainActions = Gtk::ActionGroup::create("main");
759         groups[desktop] = mainActions;
760     }
762     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
763         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
764         if ( verb ) {
765             if (!mainActions->get_action(verb->get_id())) {
766                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
767                 mainActions->add(Glib::wrap(act));
768             }
769         }
770     }
772     if ( !mainActions->get_action("ToolZoom") ) {
773         GtkTooltips *tt = gtk_tooltips_new();
774         for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
775             Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
776             if ( va ) {
777                 mainActions->add(va);
778                 if ( i == 0 ) {
779                     va->set_active(true);
780                 }
781             }
782         }
783     }
786     return mainActions;
790 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
792     gtk_widget_set_size_request( widget,
793                                  widget->allocation.width,
794                                  widget->allocation.height );
797 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
799     gtk_widget_set_size_request( widget, -1, -1 );
804 GtkWidget *
805 sp_tool_toolbox_new()
807     GtkTooltips *tt = gtk_tooltips_new();
808     GtkWidget* tb = gtk_toolbar_new();
809     gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
810     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
812     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
813     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
815     gtk_widget_set_sensitive(tb, FALSE);
817     GtkWidget *hb = gtk_handle_box_new();
818     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
819     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
820     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
822     gtk_container_add(GTK_CONTAINER(hb), tb);
823     gtk_widget_show(GTK_WIDGET(tb));
825     sigc::connection* conn = new sigc::connection;
826     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
828     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
829     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
831     return hb;
834 GtkWidget *
835 sp_aux_toolbox_new()
837     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
839     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
841     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
843     gtk_widget_set_sensitive(tb, FALSE);
845     GtkWidget *hb = gtk_handle_box_new();
846     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
847     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
848     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
850     gtk_container_add(GTK_CONTAINER(hb), tb);
851     gtk_widget_show(GTK_WIDGET(tb));
853     sigc::connection* conn = new sigc::connection;
854     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
856     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
857     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
859     return hb;
862 //####################################
863 //# Commands Bar
864 //####################################
866 GtkWidget *
867 sp_commands_toolbox_new()
869     GtkWidget *tb = gtk_toolbar_new();
871     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
872     gtk_widget_set_sensitive(tb, FALSE);
874     GtkWidget *hb = gtk_handle_box_new();
875     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
876     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
877     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
879     gtk_container_add(GTK_CONTAINER(hb), tb);
880     gtk_widget_show(GTK_WIDGET(tb));
882     sigc::connection* conn = new sigc::connection;
883     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
885     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
886     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
888     return hb;
892 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
893                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
894                                                        gchar const *path, gchar const *data, gdouble def,
895                                                        GtkWidget *focusTarget,
896                                                        GtkWidget *us,
897                                                        GObject *dataKludge,
898                                                        gboolean altx, gchar const *altx_mark,
899                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
900                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
901                                                        void (*callback)(GtkAdjustment *, GObject *),
902                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
904     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
905                                                              lower, upper, step, page, page ) );
906     if (us) {
907         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
908     }
910     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
912     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
913     if ( shortLabel ) {
914         g_object_set( act, "short_label", shortLabel, NULL );
915     }
917     if ( (descrCount > 0) && descrLabels && descrValues ) {
918         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
919     }
921     if ( focusTarget ) {
922         ege_adjustment_action_set_focuswidget( act, focusTarget );
923     }
925     if ( altx && altx_mark ) {
926         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
927     }
929     if ( dataKludge ) {
930         g_object_set_data( dataKludge, data, adj );
931     }
933     // Using a cast just to make sure we pass in the right kind of function pointer
934     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
936     return act;
940 //####################################
941 //# node editing callbacks
942 //####################################
944 /**
945  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
946  */
947 static ShapeEditor *get_current_shape_editor()
949     if (!SP_ACTIVE_DESKTOP) {
950         return NULL;
951     }
953     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
955     if (!SP_IS_NODE_CONTEXT(event_context)) {
956         return NULL;
957     }
959     return SP_NODE_CONTEXT(event_context)->shape_editor;
963 void
964 sp_node_path_edit_add(void)
966     ShapeEditor *shape_editor = get_current_shape_editor();
967     if (shape_editor) shape_editor->add_node();
970 void
971 sp_node_path_edit_delete(void)
973     ShapeEditor *shape_editor = get_current_shape_editor();
974     if (shape_editor) shape_editor->delete_nodes_preserving_shape();
977 void
978 sp_node_path_edit_delete_segment(void)
980     ShapeEditor *shape_editor = get_current_shape_editor();
981     if (shape_editor) shape_editor->delete_segment();
984 void
985 sp_node_path_edit_break(void)
987     ShapeEditor *shape_editor = get_current_shape_editor();
988     if (shape_editor) shape_editor->break_at_nodes();
991 void
992 sp_node_path_edit_join(void)
994     ShapeEditor *shape_editor = get_current_shape_editor();
995     if (shape_editor) shape_editor->join_nodes();
998 void
999 sp_node_path_edit_join_segment(void)
1001     ShapeEditor *shape_editor = get_current_shape_editor();
1002     if (shape_editor) shape_editor->join_segments();
1005 void
1006 sp_node_path_edit_toline(void)
1008     ShapeEditor *shape_editor = get_current_shape_editor();
1009     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1012 void
1013 sp_node_path_edit_tocurve(void)
1015     ShapeEditor *shape_editor = get_current_shape_editor();
1016     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1019 void
1020 sp_node_path_edit_cusp(void)
1022     ShapeEditor *shape_editor = get_current_shape_editor();
1023     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1026 void
1027 sp_node_path_edit_smooth(void)
1029     ShapeEditor *shape_editor = get_current_shape_editor();
1030     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1033 void
1034 sp_node_path_edit_symmetrical(void)
1036     ShapeEditor *shape_editor = get_current_shape_editor();
1037     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1040 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1041     bool show = gtk_toggle_action_get_active( act );
1042     prefs_set_int_attribute ("tools.nodes", "show_handles",  show ? 1 : 0);
1043     ShapeEditor *shape_editor = get_current_shape_editor();
1044     if (shape_editor) shape_editor->show_handles(show);
1047 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1048     bool show = gtk_toggle_action_get_active( act );
1049     prefs_set_int_attribute ("tools.nodes", "show_helperpath",  show ? 1 : 0);
1050     ShapeEditor *shape_editor = get_current_shape_editor();
1051     if (shape_editor) shape_editor->show_helperpath(show);
1054 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1055     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1058 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1059     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1062 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1063     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1066 /* is called when the node selection is modified */
1067 static void
1068 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1070     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1071     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1072     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1073     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1075     // quit if run by the attr_changed listener
1076     if (g_object_get_data( tbl, "freeze" )) {
1077         return;
1078     }
1080     // in turn, prevent listener from responding
1081     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1083     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1084     SPUnit const *unit = tracker->getActiveUnit();
1086     ShapeEditor *shape_editor = get_current_shape_editor();
1087     if (shape_editor && shape_editor->has_nodepath()) {
1088         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1089         int n_selected = 0;
1090         if (nodepath) {
1091             n_selected = nodepath->numSelected();
1092         }
1094         if (n_selected == 0) {
1095             gtk_action_set_sensitive(xact, FALSE);
1096             gtk_action_set_sensitive(yact, FALSE);
1097         } else {
1098             gtk_action_set_sensitive(xact, TRUE);
1099             gtk_action_set_sensitive(yact, TRUE);
1100             NR::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1101             NR::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1103             if (n_selected == 1) {
1104                 NR::Point sel_node = nodepath->singleSelectedCoords();
1105                 if (oldx != sel_node[NR::X] || oldy != sel_node[NR::Y]) {
1106                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[NR::X], *unit));
1107                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[NR::Y], *unit));
1108                 }
1109             } else {
1110                 NR::Maybe<NR::Coord> x = sp_node_selected_common_coord(nodepath, NR::X);
1111                 NR::Maybe<NR::Coord> y = sp_node_selected_common_coord(nodepath, NR::Y);
1112                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1113                     /* Note: Currently x and y will always have a value, even if the coordinates of the
1114                        selected nodes don't coincide (in this case we use the coordinates of the center
1115                        of the bounding box). So the entries are never set to zero. */
1116                     // FIXME: Maybe we should clear the entry if several nodes are selected
1117                     //        instead of providing a kind of average value
1118                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1119                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1120                 }
1121             }
1122         }
1123     } else {
1124         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1125         gtk_action_set_sensitive(xact, FALSE);
1126         gtk_action_set_sensitive(yact, FALSE);
1127     }
1129     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1132 static void
1133 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1135     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1137     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1138     SPUnit const *unit = tracker->getActiveUnit();
1140     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1141         prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
1142     }
1144     // quit if run by the attr_changed listener
1145     if (g_object_get_data( tbl, "freeze" )) {
1146         return;
1147     }
1149     // in turn, prevent listener from responding
1150     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1152     ShapeEditor *shape_editor = get_current_shape_editor();
1153     if (shape_editor && shape_editor->has_nodepath()) {
1154         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1155         if (!strcmp(value_name, "x")) {
1156             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::X);
1157         }
1158         if (!strcmp(value_name, "y")) {
1159             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::Y);
1160         }
1161     }
1163     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1166 static void
1167 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1169     sp_node_path_value_changed(adj, tbl, "x");
1172 static void
1173 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1175     sp_node_path_value_changed(adj, tbl, "y");
1178 void
1179 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1181     {
1182     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1183     SPItem *item = selection->singleItem();
1184     if (item && SP_IS_LPE_ITEM(item)) {
1185        if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1186            gtk_action_set_sensitive(w, TRUE);
1187        } else {
1188            gtk_action_set_sensitive(w, FALSE);
1189        }
1190     } else {
1191        gtk_action_set_sensitive(w, FALSE);
1192     }
1193     }
1195     {
1196     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1197     SPItem *item = selection->singleItem();
1198     if (item && item->clip_ref && item->clip_ref->getObject()) {
1199        gtk_action_set_sensitive(w, TRUE);
1200     } else {
1201        gtk_action_set_sensitive(w, FALSE);
1202     }
1203     }
1205     {
1206     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1207     SPItem *item = selection->singleItem();
1208     if (item && item->mask_ref && item->mask_ref->getObject()) {
1209        gtk_action_set_sensitive(w, TRUE);
1210     } else {
1211        gtk_action_set_sensitive(w, FALSE);
1212     }
1213     }
1216 void
1217 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1219     sp_node_toolbox_sel_changed (selection, tbl);
1224 //################################
1225 //##    Node Editing Toolbox    ##
1226 //################################
1228 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1230     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1231     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1232     g_object_set_data( holder, "tracker", tracker );
1234     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
1236     {
1237         InkAction* inky = ink_action_new( "NodeInsertAction",
1238                                           _("Insert node"),
1239                                           _("Insert new nodes into selected segments"),
1240                                           "node_insert",
1241                                           secondarySize );
1242         g_object_set( inky, "short_label", _("Insert"), NULL );
1243         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1244         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1245     }
1247     {
1248         InkAction* inky = ink_action_new( "NodeDeleteAction",
1249                                           _("Delete node"),
1250                                           _("Delete selected nodes"),
1251                                           "node_delete",
1252                                           secondarySize );
1253         g_object_set( inky, "short_label", _("Delete"), NULL );
1254         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1255         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1256     }
1258     {
1259         InkAction* inky = ink_action_new( "NodeJoinAction",
1260                                           _("Join endnodes"),
1261                                           _("Join selected endnodes"),
1262                                           "node_join",
1263                                           secondarySize );
1264         g_object_set( inky, "short_label", _("Join"), NULL );
1265         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1266         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1267     }
1269     {
1270         InkAction* inky = ink_action_new( "NodeBreakAction",
1271                                           _("Break nodes"),
1272                                           _("Break path at selected nodes"),
1273                                           "node_break",
1274                                           secondarySize );
1275         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1276         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1277     }
1280     {
1281         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1282                                           _("Join with segment"),
1283                                           _("Join selected endnodes with a new segment"),
1284                                           "node_join_segment",
1285                                           secondarySize );
1286         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1287         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1288     }
1290     {
1291         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1292                                           _("Delete segment"),
1293                                           _("Delete segment between two non-endpoint nodes"),
1294                                           "node_delete_segment",
1295                                           secondarySize );
1296         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1297         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1298     }
1300     {
1301         InkAction* inky = ink_action_new( "NodeCuspAction",
1302                                           _("Node Cusp"),
1303                                           _("Make selected nodes corner"),
1304                                           "node_cusp",
1305                                           secondarySize );
1306         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1307         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1308     }
1310     {
1311         InkAction* inky = ink_action_new( "NodeSmoothAction",
1312                                           _("Node Smooth"),
1313                                           _("Make selected nodes smooth"),
1314                                           "node_smooth",
1315                                           secondarySize );
1316         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1317         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1318     }
1320     {
1321         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1322                                           _("Node Symmetric"),
1323                                           _("Make selected nodes symmetric"),
1324                                           "node_symmetric",
1325                                           secondarySize );
1326         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1327         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1328     }
1330     {
1331         InkAction* inky = ink_action_new( "NodeLineAction",
1332                                           _("Node Line"),
1333                                           _("Make selected segments lines"),
1334                                           "node_line",
1335                                           secondarySize );
1336         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1337         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1338     }
1340     {
1341         InkAction* inky = ink_action_new( "NodeCurveAction",
1342                                           _("Node Curve"),
1343                                           _("Make selected segments curves"),
1344                                           "node_curve",
1345                                           secondarySize );
1346         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1347         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1348     }
1350     {
1351         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1352                                                       _("Show Handles"),
1353                                                       _("Show the Bezier handles of selected nodes"),
1354                                                       "nodes_show_handles",
1355                                                       Inkscape::ICON_SIZE_DECORATION );
1356         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1357         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1358         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1359     }
1361     {
1362         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1363                                                       _("Show Outline"),
1364                                                       _("Show the outline of the path"),
1365                                                       "nodes_show_helperpath",
1366                                                       Inkscape::ICON_SIZE_DECORATION );
1367         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1368         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1369         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_helperpath", 0 ) );
1370     }
1372     {
1373         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1374                                           _("Next path effect parameter"),
1375                                           _("Show next path effect parameter for editing"),
1376                                           "edit_next_parameter",
1377                                           Inkscape::ICON_SIZE_DECORATION );
1378         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1379         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1380         g_object_set_data( holder, "nodes_lpeedit", inky);
1381     }
1383     {
1384         InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1385                                           _("Edit clipping path"),
1386                                           _("Edit the clipping path of the object"),
1387                                           "nodeedit-clippath",
1388                                           Inkscape::ICON_SIZE_DECORATION );
1389         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1390         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1391         g_object_set_data( holder, "nodes_clippathedit", inky);
1392     }
1394     {
1395         InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1396                                           _("Edit mask path"),
1397                                           _("Edit the mask of the object"),
1398                                           "nodeedit-mask",
1399                                           Inkscape::ICON_SIZE_DECORATION );
1400         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1401         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1402         g_object_set_data( holder, "nodes_maskedit", inky);
1403     }
1405     /* X coord of selected node(s) */
1406     {
1407         EgeAdjustmentAction* eact = 0;
1408         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1409         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1410         eact = create_adjustment_action( "NodeXAction",
1411                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1412                                          "tools.nodes", "Xcoord", 0,
1413                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1414                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1415                                          labels, values, G_N_ELEMENTS(labels),
1416                                          sp_node_path_x_value_changed );
1417         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1418         g_object_set_data( holder, "nodes_x_action", eact );
1419         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1420         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1421     }
1423     /* Y coord of selected node(s) */
1424     {
1425         EgeAdjustmentAction* eact = 0;
1426         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1427         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1428         eact = create_adjustment_action( "NodeYAction",
1429                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1430                                          "tools.nodes", "Ycoord", 0,
1431                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1432                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1433                                          labels, values, G_N_ELEMENTS(labels),
1434                                          sp_node_path_y_value_changed );
1435         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1436         g_object_set_data( holder, "nodes_y_action", eact );
1437         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1438         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1439     }
1441     // add the units menu
1442     {
1443         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1444         gtk_action_group_add_action( mainActions, act );
1445     }
1448     sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1450     //watch selection
1451     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1453     sigc::connection *c_selection_changed =
1454         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1455                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1456     pool->add_connection ("selection-changed", c_selection_changed);
1458     sigc::connection *c_selection_modified =
1459         new sigc::connection (sp_desktop_selection (desktop)->connectModified
1460                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1461     pool->add_connection ("selection-modified", c_selection_modified);
1463     sigc::connection *c_subselection_changed =
1464         new sigc::connection (desktop->connectToolSubselectionChanged
1465                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1466     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1468     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1470     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1471 } // end of sp_node_toolbox_prep()
1474 //########################
1475 //##    Zoom Toolbox    ##
1476 //########################
1478 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1480     // no custom GtkAction setup needed
1481 } // end of sp_zoom_toolbox_prep()
1483 void
1484 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1486     toolbox_set_desktop(toolbox,
1487                         desktop,
1488                         setup_tool_toolbox,
1489                         update_tool_toolbox,
1490                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1491                                                                          "event_context_connection")));
1495 void
1496 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1498     toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1499                         desktop,
1500                         setup_aux_toolbox,
1501                         update_aux_toolbox,
1502                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1503                                                                          "event_context_connection")));
1506 void
1507 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1509     toolbox_set_desktop(toolbox,
1510                         desktop,
1511                         setup_commands_toolbox,
1512                         update_commands_toolbox,
1513                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1514                                                                          "event_context_connection")));
1517 static void
1518 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1520     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1521     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1523     if (old_desktop) {
1524         GList *children, *iter;
1526         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1527         for ( iter = children ; iter ; iter = iter->next ) {
1528             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1529         }
1530         g_list_free(children);
1531     }
1533     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1535     if (desktop) {
1536         gtk_widget_set_sensitive(toolbox, TRUE);
1537         setup_func(toolbox, desktop);
1538         update_func(desktop, desktop->event_context, toolbox);
1539         *conn = desktop->connectEventContextChanged
1540             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1541     } else {
1542         gtk_widget_set_sensitive(toolbox, FALSE);
1543     }
1545 } // end of toolbox_set_desktop()
1548 static void
1549 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1551     gchar const * descr =
1552         "<ui>"
1553         "  <toolbar name='ToolToolbar'>"
1554         "    <toolitem action='ToolSelector' />"
1555         "    <toolitem action='ToolNode' />"
1556         "    <toolitem action='ToolTweak' />"
1557         "    <toolitem action='ToolZoom' />"
1558         "    <toolitem action='ToolRect' />"
1559         "    <toolitem action='Tool3DBox' />"
1560         "    <toolitem action='ToolArc' />"
1561         "    <toolitem action='ToolStar' />"
1562         "    <toolitem action='ToolSpiral' />"
1563         "    <toolitem action='ToolPencil' />"
1564         "    <toolitem action='ToolPen' />"
1565         "    <toolitem action='ToolCalligraphic' />"
1566         "    <toolitem action='ToolEraser' />"
1567         "    <toolitem action='ToolPaintBucket' />"
1568         "    <toolitem action='ToolText' />"
1569         "    <toolitem action='ToolConnector' />"
1570         "    <toolitem action='ToolGradient' />"
1571         "    <toolitem action='ToolDropper' />"
1572         "  </toolbar>"
1573         "</ui>";
1574     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1575     GtkUIManager* mgr = gtk_ui_manager_new();
1576     GError* errVal = 0;
1578     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1579     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1581     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" );
1582     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1583         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1584     }
1585     Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1586     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1588     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
1589     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1591     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1593     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1594     if ( child ) {
1595         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1596     }
1598     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1599 //     Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1603 static void
1604 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1606     gchar const *const tname = ( eventcontext
1607                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1608                                  : NULL );
1609     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1611     for (int i = 0 ; tools[i].type_name ; i++ ) {
1612         Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1613         if ( act ) {
1614             bool setActive = tname && !strcmp(tname, tools[i].type_name);
1615             Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1616             if ( verbAct ) {
1617                 verbAct->set_active(setActive);
1618             }
1619         }
1620     }
1623 static void
1624 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1626     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1627     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1628     GtkUIManager* mgr = gtk_ui_manager_new();
1629     GError* errVal = 0;
1630     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1631     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1633     std::map<std::string, GtkWidget*> dataHolders;
1635     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1636         if ( aux_toolboxes[i].prep_func ) {
1637             // converted to GtkActions and UIManager
1639             GtkWidget* kludge = gtk_toolbar_new();
1640             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1641             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1642             dataHolders[aux_toolboxes[i].type_name] = kludge;
1643             aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1644         } else {
1646             GtkWidget *sub_toolbox = 0;
1647             if (aux_toolboxes[i].create_func == NULL)
1648                 sub_toolbox = sp_empty_toolbox_new(desktop);
1649             else {
1650                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1651             }
1653             gtk_size_group_add_widget( grouper, sub_toolbox );
1655             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1656             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1658         }
1659     }
1661     // Second pass to create toolbars *after* all GtkActions are created
1662     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1663         if ( aux_toolboxes[i].prep_func ) {
1664             // converted to GtkActions and UIManager
1666             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1668             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1669             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1671             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1672             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1673             g_free( tmp );
1674             tmp = 0;
1676             Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1677             if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1678                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1679             }
1680             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1683             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1685             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1686                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1687                 swatch->setDesktop( desktop );
1688                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1689                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1690                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1691                 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 );
1692             }
1694             gtk_widget_show_all( holder );
1695             sp_set_font_size_smaller( holder );
1697             gtk_size_group_add_widget( grouper, holder );
1699             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1700             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1701         }
1702     }
1704     g_object_unref( G_OBJECT(grouper) );
1707 static void
1708 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1710     gchar const *tname = ( eventcontext
1711                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1712                            : NULL );
1713     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1714         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1715         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1716             gtk_widget_show_all(sub_toolbox);
1717             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1718         } else {
1719             gtk_widget_hide(sub_toolbox);
1720         }
1721     }
1724 static void
1725 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1727     gchar const * descr =
1728         "<ui>"
1729         "  <toolbar name='CommandsToolbar'>"
1730         "    <toolitem action='FileNew' />"
1731         "    <toolitem action='FileOpen' />"
1732         "    <toolitem action='FileSave' />"
1733         "    <toolitem action='FilePrint' />"
1734         "    <separator />"
1735         "    <toolitem action='FileImport' />"
1736         "    <toolitem action='FileExport' />"
1737         "    <separator />"
1738         "    <toolitem action='EditUndo' />"
1739         "    <toolitem action='EditRedo' />"
1740         "    <separator />"
1741         "    <toolitem action='EditCopy' />"
1742         "    <toolitem action='EditCut' />"
1743         "    <toolitem action='EditPaste' />"
1744         "    <separator />"
1745         "    <toolitem action='ZoomSelection' />"
1746         "    <toolitem action='ZoomDrawing' />"
1747         "    <toolitem action='ZoomPage' />"
1748         "    <separator />"
1749         "    <toolitem action='EditDuplicate' />"
1750         "    <toolitem action='EditClone' />"
1751         "    <toolitem action='EditUnlinkClone' />"
1752         "    <separator />"
1753         "    <toolitem action='SelectionGroup' />"
1754         "    <toolitem action='SelectionUnGroup' />"
1755         "    <separator />"
1756         "    <toolitem action='DialogFillStroke' />"
1757         "    <toolitem action='DialogText' />"
1758         "    <toolitem action='DialogXMLEditor' />"
1759         "    <toolitem action='DialogAlignDistribute' />"
1760         "    <separator />"
1761         "    <toolitem action='DialogPreferences' />"
1762         "    <toolitem action='DialogDocumentProperties' />"
1763         "  </toolbar>"
1764         "</ui>";
1765     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1768     GtkUIManager* mgr = gtk_ui_manager_new();
1769     GError* errVal = 0;
1771     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1772     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1774     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1775     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1776         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1777     }
1779     Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1780     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1782     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1783     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1786     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1788     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1789     if ( child ) {
1790         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1791     }
1793     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1796 static void
1797 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1801 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1803     gtk_widget_show(toolbox_toplevel);
1804     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1806     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1807     if (!shown_toolbox) {
1808         return;
1809     }
1810     gtk_widget_show(toolbox);
1812     gtk_widget_show_all(shown_toolbox);
1815 static GtkWidget *
1816 sp_empty_toolbox_new(SPDesktop *desktop)
1818     GtkWidget *tbl = gtk_toolbar_new();
1819     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1820     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1822     gtk_widget_show_all(tbl);
1823     sp_set_font_size_smaller (tbl);
1825     return tbl;
1828 #define MODE_LABEL_WIDTH 70
1830 //########################
1831 //##       Star         ##
1832 //########################
1834 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1836     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1838     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1839         // do not remember prefs if this call is initiated by an undo change, because undoing object
1840         // creation sets bogus values to its attributes before it is deleted
1841         prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1842     }
1844     // quit if run by the attr_changed listener
1845     if (g_object_get_data( dataKludge, "freeze" )) {
1846         return;
1847     }
1849     // in turn, prevent listener from responding
1850     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1852     bool modmade = false;
1854     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1855     GSList const *items = selection->itemList();
1856     for (; items != NULL; items = items->next) {
1857         if (SP_IS_STAR((SPItem *) items->data)) {
1858             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1859             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1860             sp_repr_set_svg_double(repr, "sodipodi:arg2",
1861                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1862                                     + M_PI / (gint)adj->value));
1863             SP_OBJECT((SPItem *) items->data)->updateRepr();
1864             modmade = true;
1865         }
1866     }
1867     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1868                                    _("Star: Change number of corners"));
1870     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1873 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1875     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1877     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1878         prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1879     }
1881     // quit if run by the attr_changed listener
1882     if (g_object_get_data( dataKludge, "freeze" )) {
1883         return;
1884     }
1886     // in turn, prevent listener from responding
1887     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1889     bool modmade = false;
1890     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1891     GSList const *items = selection->itemList();
1892     for (; items != NULL; items = items->next) {
1893         if (SP_IS_STAR((SPItem *) items->data)) {
1894             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1896             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1897             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1898             if (r2 < r1) {
1899                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1900             } else {
1901                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1902             }
1904             SP_OBJECT((SPItem *) items->data)->updateRepr();
1905             modmade = true;
1906         }
1907     }
1909     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1910                                    _("Star: Change spoke ratio"));
1912     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1915 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1917     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1918     bool flat = ege_select_one_action_get_active( act ) == 0;
1920     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1921         prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1922                                     flat ? "true" : "false" );
1923     }
1925     // quit if run by the attr_changed listener
1926     if (g_object_get_data( dataKludge, "freeze" )) {
1927         return;
1928     }
1930     // in turn, prevent listener from responding
1931     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1933     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1934     GSList const *items = selection->itemList();
1935     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1936     bool modmade = false;
1938     if ( prop_action ) {
1939         gtk_action_set_sensitive( prop_action, !flat );
1940     }
1942     for (; items != NULL; items = items->next) {
1943         if (SP_IS_STAR((SPItem *) items->data)) {
1944             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1945             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1946             SP_OBJECT((SPItem *) items->data)->updateRepr();
1947             modmade = true;
1948         }
1949     }
1951     if (modmade) {
1952         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1953                          flat ? _("Make polygon") : _("Make star"));
1954     }
1956     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1959 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1961     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1963     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1964         prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1965     }
1967     // quit if run by the attr_changed listener
1968     if (g_object_get_data( dataKludge, "freeze" )) {
1969         return;
1970     }
1972     // in turn, prevent listener from responding
1973     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1975     bool modmade = false;
1977     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1978     GSList const *items = selection->itemList();
1979     for (; items != NULL; items = items->next) {
1980         if (SP_IS_STAR((SPItem *) items->data)) {
1981             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1982             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1983             SP_OBJECT(items->data)->updateRepr();
1984             modmade = true;
1985         }
1986     }
1987     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1988                                    _("Star: Change rounding"));
1990     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1993 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1995     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1997     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1998         prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1999     }
2001     // quit if run by the attr_changed listener
2002     if (g_object_get_data( dataKludge, "freeze" )) {
2003         return;
2004     }
2006     // in turn, prevent listener from responding
2007     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2009     bool modmade = false;
2011     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2012     GSList const *items = selection->itemList();
2013     for (; items != NULL; items = items->next) {
2014         if (SP_IS_STAR((SPItem *) items->data)) {
2015             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2016             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2017             SP_OBJECT(items->data)->updateRepr();
2018             modmade = true;
2019         }
2020     }
2021     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2022                                    _("Star: Change randomization"));
2024     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2028 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2029                                        gchar const */*old_value*/, gchar const */*new_value*/,
2030                                        bool /*is_interactive*/, gpointer data)
2032     GtkWidget *tbl = GTK_WIDGET(data);
2034     // quit if run by the _changed callbacks
2035     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2036         return;
2037     }
2039     // in turn, prevent callbacks from responding
2040     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2042     GtkAdjustment *adj = 0;
2044     gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2045     bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2047     if (!strcmp(name, "inkscape:randomized")) {
2048         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2049         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2050     } else if (!strcmp(name, "inkscape:rounded")) {
2051         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2052         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2053     } else if (!strcmp(name, "inkscape:flatsided")) {
2054         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2055         char const *flatsides = repr->attribute("inkscape:flatsided");
2056         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2057         if ( flatsides && !strcmp(flatsides,"false") ) {
2058             ege_select_one_action_set_active( flat_action, 1 );
2059             gtk_action_set_sensitive( prop_action, TRUE );
2060         } else {
2061             ege_select_one_action_set_active( flat_action, 0 );
2062             gtk_action_set_sensitive( prop_action, FALSE );
2063         }
2064     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2065         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2066         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2067         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2068         if (r2 < r1) {
2069             gtk_adjustment_set_value(adj, r2/r1);
2070         } else {
2071             gtk_adjustment_set_value(adj, r1/r2);
2072         }
2073     } else if (!strcmp(name, "sodipodi:sides")) {
2074         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2075         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2076     }
2078     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2082 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2084     NULL, /* child_added */
2085     NULL, /* child_removed */
2086     star_tb_event_attr_changed,
2087     NULL, /* content_changed */
2088     NULL  /* order_changed */
2089 };
2092 /**
2093  *  \param selection Should not be NULL.
2094  */
2095 static void
2096 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2098     int n_selected = 0;
2099     Inkscape::XML::Node *repr = NULL;
2101     purge_repr_listener( tbl, tbl );
2103     for (GSList const *items = selection->itemList();
2104          items != NULL;
2105          items = items->next)
2106     {
2107         if (SP_IS_STAR((SPItem *) items->data)) {
2108             n_selected++;
2109             repr = SP_OBJECT_REPR((SPItem *) items->data);
2110         }
2111     }
2113     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2115     if (n_selected == 0) {
2116         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2117     } else if (n_selected == 1) {
2118         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2120         if (repr) {
2121             g_object_set_data( tbl, "repr", repr );
2122             Inkscape::GC::anchor(repr);
2123             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2124             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2125         }
2126     } else {
2127         // FIXME: implement averaging of all parameters for multiple selected stars
2128         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2129         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2130     }
2134 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2136     // FIXME: in this and all other _default functions, set some flag telling the value_changed
2137     // callbacks to lump all the changes for all selected objects in one undo step
2139     GtkAdjustment *adj = 0;
2141     // fixme: make settable in prefs!
2142     gint mag = 5;
2143     gdouble prop = 0.5;
2144     gboolean flat = FALSE;
2145     gdouble randomized = 0;
2146     gdouble rounded = 0;
2148     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2149     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2151     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2152     gtk_action_set_sensitive( sb2, !flat );
2154     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2155     gtk_adjustment_set_value(adj, mag);
2156     gtk_adjustment_value_changed(adj);
2158     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2159     gtk_adjustment_set_value(adj, prop);
2160     gtk_adjustment_value_changed(adj);
2162     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2163     gtk_adjustment_set_value(adj, rounded);
2164     gtk_adjustment_value_changed(adj);
2166     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2167     gtk_adjustment_set_value(adj, randomized);
2168     gtk_adjustment_value_changed(adj);
2172 void
2173 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2175     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2176     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2177     GtkWidget *l = gtk_label_new(NULL);
2178     gtk_label_set_markup(GTK_LABEL(l), title);
2179     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2180     if ( GTK_IS_TOOLBAR(tbl) ) {
2181         gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2182     } else {
2183         gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2184     }
2185     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2189 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2191     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2193     {
2194         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2195         ege_output_action_set_use_markup( act, TRUE );
2196         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2197         g_object_set_data( holder, "mode_action", act );
2198     }
2200     {
2201         EgeAdjustmentAction* eact = 0;
2202         gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2203         bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2205         /* Flatsided checkbox */
2206         {
2207             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2209             GtkTreeIter iter;
2210             gtk_list_store_append( model, &iter );
2211             gtk_list_store_set( model, &iter,
2212                                 0, _("Polygon"),
2213                                 1, _("Regular polygon (with one handle) instead of a star"),
2214                                 2, "star_flat",
2215                                 -1 );
2217             gtk_list_store_append( model, &iter );
2218             gtk_list_store_set( model, &iter,
2219                                 0, _("Star"),
2220                                 1, _("Star instead of a regular polygon (with one handle)"),
2221                                 2, "star_angled",
2222                                 -1 );
2224             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2225             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2226             g_object_set_data( holder, "flat_action", act );
2228             ege_select_one_action_set_appearance( act, "full" );
2229             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2230             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2231             ege_select_one_action_set_icon_column( act, 2 );
2232             ege_select_one_action_set_icon_size( act, secondarySize );
2233             ege_select_one_action_set_tooltip_column( act, 1  );
2235             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2236             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2237         }
2239         /* Magnitude */
2240         {
2241         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2242         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2243         eact = create_adjustment_action( "MagnitudeAction",
2244                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2245                                          "tools.shapes.star", "magnitude", 3,
2246                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2247                                          3, 1024, 1, 5,
2248                                          labels, values, G_N_ELEMENTS(labels),
2249                                          sp_stb_magnitude_value_changed,
2250                                          1.0, 0 );
2251         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2252         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2253         }
2255         /* Spoke ratio */
2256         {
2257         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2258         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2259         eact = create_adjustment_action( "SpokeAction",
2260                                          _("Spoke ratio"), _("Spoke ratio:"),
2261                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2262                                          // Base radius is the same for the closest handle.
2263                                          _("Base radius to tip radius ratio"),
2264                                          "tools.shapes.star", "proportion", 0.5,
2265                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2266                                          0.01, 1.0, 0.01, 0.1,
2267                                          labels, values, G_N_ELEMENTS(labels),
2268                                          sp_stb_proportion_value_changed );
2269         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2270         g_object_set_data( holder, "prop_action", eact );
2271         }
2273         if ( !isFlatSided ) {
2274             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2275         } else {
2276             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2277         }
2279         /* Roundedness */
2280         {
2281         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2282         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2283         eact = create_adjustment_action( "RoundednessAction",
2284                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2285                                          "tools.shapes.star", "rounded", 0.0,
2286                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2287                                          -10.0, 10.0, 0.01, 0.1,
2288                                          labels, values, G_N_ELEMENTS(labels),
2289                                          sp_stb_rounded_value_changed );
2290         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2291         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2292         }
2294         /* Randomization */
2295         {
2296         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2297         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2298         eact = create_adjustment_action( "RandomizationAction",
2299                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2300                                          "tools.shapes.star", "randomized", 0.0,
2301                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2302                                          -10.0, 10.0, 0.001, 0.01,
2303                                          labels, values, G_N_ELEMENTS(labels),
2304                                          sp_stb_randomized_value_changed, 0.1, 3 );
2305         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2306         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2307         }
2308     }
2310     {
2311         /* Reset */
2312         {
2313             GtkAction* act = gtk_action_new( "StarResetAction",
2314                                              _("Defaults"),
2315                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2316                                              GTK_STOCK_CLEAR );
2317             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2318             gtk_action_group_add_action( mainActions, act );
2319             gtk_action_set_sensitive( act, TRUE );
2320         }
2321     }
2323     sigc::connection *connection = new sigc::connection(
2324         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2325         );
2326     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2327     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2331 //########################
2332 //##       Rect         ##
2333 //########################
2335 static void sp_rtb_sensitivize( GObject *tbl )
2337     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2338     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2339     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2341     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2342         gtk_action_set_sensitive( not_rounded, FALSE );
2343     } else {
2344         gtk_action_set_sensitive( not_rounded, TRUE );
2345     }
2349 static void
2350 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2351                           void (*setter)(SPRect *, gdouble))
2353     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2355     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2356     SPUnit const *unit = tracker->getActiveUnit();
2358     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2359         prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2360     }
2362     // quit if run by the attr_changed listener
2363     if (g_object_get_data( tbl, "freeze" )) {
2364         return;
2365     }
2367     // in turn, prevent listener from responding
2368     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2370     bool modmade = false;
2371     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2372     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2373         if (SP_IS_RECT(items->data)) {
2374             if (adj->value != 0) {
2375                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2376             } else {
2377                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2378             }
2379             modmade = true;
2380         }
2381     }
2383     sp_rtb_sensitivize( tbl );
2385     if (modmade) {
2386         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2387                                    _("Change rectangle"));
2388     }
2390     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2393 static void
2394 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2396     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2399 static void
2400 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2402     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2405 static void
2406 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2408     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2411 static void
2412 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2414     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2419 static void
2420 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2422     GtkAdjustment *adj = 0;
2424     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2425     gtk_adjustment_set_value(adj, 0.0);
2426     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2427     gtk_adjustment_value_changed(adj);
2429     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2430     gtk_adjustment_set_value(adj, 0.0);
2431     gtk_adjustment_value_changed(adj);
2433     sp_rtb_sensitivize( obj );
2436 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2437                                        gchar const */*old_value*/, gchar const */*new_value*/,
2438                                        bool /*is_interactive*/, gpointer data)
2440     GObject *tbl = G_OBJECT(data);
2442     // quit if run by the _changed callbacks
2443     if (g_object_get_data( tbl, "freeze" )) {
2444         return;
2445     }
2447     // in turn, prevent callbacks from responding
2448     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2450     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2451     SPUnit const *unit = tracker->getActiveUnit();
2453     gpointer item = g_object_get_data( tbl, "item" );
2454     if (item && SP_IS_RECT(item)) {
2455         {
2456             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2457             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2458             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2459         }
2461         {
2462             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2463             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2464             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2465         }
2467         {
2468             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2469             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2470             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2471         }
2473         {
2474             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2475             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2476             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2477         }
2478     }
2480     sp_rtb_sensitivize( tbl );
2482     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2486 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2487     NULL, /* child_added */
2488     NULL, /* child_removed */
2489     rect_tb_event_attr_changed,
2490     NULL, /* content_changed */
2491     NULL  /* order_changed */
2492 };
2494 /**
2495  *  \param selection should not be NULL.
2496  */
2497 static void
2498 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2500     int n_selected = 0;
2501     Inkscape::XML::Node *repr = NULL;
2502     SPItem *item = NULL;
2504     if ( g_object_get_data( tbl, "repr" ) ) {
2505         g_object_set_data( tbl, "item", NULL );
2506     }
2507     purge_repr_listener( tbl, tbl );
2509     for (GSList const *items = selection->itemList();
2510          items != NULL;
2511          items = items->next) {
2512         if (SP_IS_RECT((SPItem *) items->data)) {
2513             n_selected++;
2514             item = (SPItem *) items->data;
2515             repr = SP_OBJECT_REPR(item);
2516         }
2517     }
2519     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2521     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2523     if (n_selected == 0) {
2524         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2526         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2527         gtk_action_set_sensitive(w, FALSE);
2528         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2529         gtk_action_set_sensitive(h, FALSE);
2531     } else if (n_selected == 1) {
2532         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2533         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2535         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2536         gtk_action_set_sensitive(w, TRUE);
2537         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2538         gtk_action_set_sensitive(h, TRUE);
2540         if (repr) {
2541             g_object_set_data( tbl, "repr", repr );
2542             g_object_set_data( tbl, "item", item );
2543             Inkscape::GC::anchor(repr);
2544             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2545             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2546         }
2547     } else {
2548         // FIXME: implement averaging of all parameters for multiple selected
2549         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2550         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2551         sp_rtb_sensitivize( tbl );
2552     }
2556 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2558     EgeAdjustmentAction* eact = 0;
2559     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2561     {
2562         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2563         ege_output_action_set_use_markup( act, TRUE );
2564         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2565         g_object_set_data( holder, "mode_action", act );
2566     }
2568     // rx/ry units menu: create
2569     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2570     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2571     // fixme: add % meaning per cent of the width/height
2572     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2573     g_object_set_data( holder, "tracker", tracker );
2575     /* W */
2576     {
2577         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2578         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2579         eact = create_adjustment_action( "RectWidthAction",
2580                                          _("Width"), _("W:"), _("Width of rectangle"),
2581                                          "tools.shapes.rect", "width", 0,
2582                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2583                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2584                                          labels, values, G_N_ELEMENTS(labels),
2585                                          sp_rtb_width_value_changed );
2586         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2587         g_object_set_data( holder, "width_action", eact );
2588         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2589         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2590     }
2592     /* H */
2593     {
2594         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2595         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2596         eact = create_adjustment_action( "RectHeightAction",
2597                                          _("Height"), _("H:"), _("Height of rectangle"),
2598                                          "tools.shapes.rect", "height", 0,
2599                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2600                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2601                                          labels, values, G_N_ELEMENTS(labels),
2602                                          sp_rtb_height_value_changed );
2603         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2604         g_object_set_data( holder, "height_action", eact );
2605         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2606         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2607     }
2609     /* rx */
2610     {
2611         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2612         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2613         eact = create_adjustment_action( "RadiusXAction",
2614                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2615                                          "tools.shapes.rect", "rx", 0,
2616                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2617                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2618                                          labels, values, G_N_ELEMENTS(labels),
2619                                          sp_rtb_rx_value_changed);
2620         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2621         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2622     }
2624     /* ry */
2625     {
2626         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2627         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2628         eact = create_adjustment_action( "RadiusYAction",
2629                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2630                                          "tools.shapes.rect", "ry", 0,
2631                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2632                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2633                                          labels, values, G_N_ELEMENTS(labels),
2634                                          sp_rtb_ry_value_changed);
2635         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2636         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2637     }
2639     // add the units menu
2640     {
2641         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2642         gtk_action_group_add_action( mainActions, act );
2643     }
2645     /* Reset */
2646     {
2647         InkAction* inky = ink_action_new( "RectResetAction",
2648                                           _("Not rounded"),
2649                                           _("Make corners sharp"),
2650                                           "squared_corner",
2651                                           secondarySize );
2652         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2653         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2654         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2655         g_object_set_data( holder, "not_rounded", inky );
2656     }
2658     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2659     sp_rtb_sensitivize( holder );
2661     sigc::connection *connection = new sigc::connection(
2662         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2663         );
2664     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2665     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2668 //########################
2669 //##       3D Box       ##
2670 //########################
2672 // normalize angle so that it lies in the interval [0,360]
2673 static double box3d_normalize_angle (double a) {
2674     double angle = a + ((int) (a/360.0))*360;
2675     if (angle < 0) {
2676         angle += 360.0;
2677     }
2678     return angle;
2681 static void
2682 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2683                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2684     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2685     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2686     //       are reset).
2687     bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2689     if (is_infinite) {
2690         gtk_toggle_action_set_active(tact, TRUE);
2691         gtk_action_set_sensitive(act, TRUE);
2693         double angle = persp3d_get_infinite_angle(persp, axis);
2694         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2695             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2696         }
2697     } else {
2698         gtk_toggle_action_set_active(tact, FALSE);
2699         gtk_action_set_sensitive(act, FALSE);
2700     }
2703 static void
2704 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2705     if (!persp_repr) {
2706         g_print ("No perspective given to box3d_resync_toolbar().\n");
2707         return;
2708     }
2710     GtkWidget *tbl = GTK_WIDGET(data);
2711     GtkAdjustment *adj = 0;
2712     GtkAction *act = 0;
2713     GtkToggleAction *tact = 0;
2714     Persp3D *persp = persp3d_get_from_repr(persp_repr);
2715     {
2716         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2717         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2718         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2720         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2721     }
2722     {
2723         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2724         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2725         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2727         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2728     }
2729     {
2730         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2731         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2732         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2734         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2735     }
2738 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2739                                                   gchar const */*old_value*/, gchar const */*new_value*/,
2740                                                   bool /*is_interactive*/, gpointer data)
2742     GtkWidget *tbl = GTK_WIDGET(data);
2744     // quit if run by the attr_changed listener
2745     // note: it used to work without the differently called freeze_ attributes (here and in
2746     //       box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2747     if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2748         return;
2749     }
2751     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2752     // sp_document_maybe_done() when the document is undo insensitive)
2753     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2755     // TODO: Only update the appropriate part of the toolbar
2756 //    if (!strcmp(name, "inkscape:vp_z")) {
2757         box3d_resync_toolbar(repr, G_OBJECT(tbl));
2758 //    }
2760     Persp3D *persp = persp3d_get_from_repr(repr);
2761     persp3d_update_box_reprs(persp);
2763     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2766 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2768     NULL, /* child_added */
2769     NULL, /* child_removed */
2770     box3d_persp_tb_event_attr_changed,
2771     NULL, /* content_changed */
2772     NULL  /* order_changed */
2773 };
2775 /**
2776  *  \param selection Should not be NULL.
2777  */
2778 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2779 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2780 static void
2781 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2783     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2784     // disable the angle entry fields for this direction (otherwise entering a value in them should only
2785     // update the perspectives with infinite VPs and leave the other ones untouched).
2787     Inkscape::XML::Node *persp_repr = NULL;
2788     purge_repr_listener(tbl, tbl);
2790     SPItem *item = selection->singleItem();
2791     if (item && SP_IS_BOX3D(item)) {
2792         // FIXME: Also deal with multiple selected boxes
2793         SPBox3D *box = SP_BOX3D(item);
2794         Persp3D *persp = box3d_get_perspective(box);
2795         persp_repr = SP_OBJECT_REPR(persp);
2796         if (persp_repr) {
2797             g_object_set_data(tbl, "repr", persp_repr);
2798             Inkscape::GC::anchor(persp_repr);
2799             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2800             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2801         }
2803         inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2804         prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2806         box3d_resync_toolbar(persp_repr, tbl);
2807     }
2810 static void
2811 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2813     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2814     SPDocument *document = sp_desktop_document(desktop);
2816     // quit if run by the attr_changed listener
2817     // note: it used to work without the differently called freeze_ attributes (here and in
2818     //       box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2819     if (g_object_get_data( dataKludge, "freeze_attr" )) {
2820         return;
2821     }
2823     // in turn, prevent listener from responding
2824     g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2826     //Persp3D *persp = document->current_persp3d;
2827     std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
2828     if (sel_persps.empty()) {
2829         // this can happen when the document is created; we silently ignore it
2830         return;
2831     }
2832     Persp3D *persp = sel_persps.front();
2834     persp->tmat.set_infinite_direction (axis, adj->value);
2835     SP_OBJECT(persp)->updateRepr();
2837     // TODO: use the correct axis here, too
2838     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2840     g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2844 static void
2845 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2847     box3d_angle_value_changed(adj, dataKludge, Proj::X);
2850 static void
2851 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2853     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2856 static void
2857 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2859     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2863 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2865     // TODO: Take all selected perspectives into account
2866     std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
2867     if (sel_persps.empty()) {
2868         // this can happen when the document is created; we silently ignore it
2869         return;
2870     }
2871     Persp3D *persp = sel_persps.front();
2873     bool set_infinite = gtk_toggle_action_get_active(act);
2874     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2877 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2879     box3d_vp_state_changed(act, box3d_angle, Proj::X);
2882 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2884     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2887 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2889     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2892 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2894     EgeAdjustmentAction* eact = 0;
2895     SPDocument *document = sp_desktop_document (desktop);
2896     Persp3D *persp = document->current_persp3d;
2898     EgeAdjustmentAction* box3d_angle_x = 0;
2899     EgeAdjustmentAction* box3d_angle_y = 0;
2900     EgeAdjustmentAction* box3d_angle_z = 0;
2902     /* Angle X */
2903     {
2904         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2905         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2906         eact = create_adjustment_action( "3DBoxAngleXAction",
2907                                          _("Angle in X direction"), _("Angle X:"),
2908                                          // Translators: PL is short for 'perspective line'
2909                                          _("Angle of PLs in X direction"),
2910                                          "tools.shapes.3dbox", "box3d_angle_x", 30,
2911                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2912                                          -360.0, 360.0, 1.0, 10.0,
2913                                          labels, values, G_N_ELEMENTS(labels),
2914                                          box3d_angle_x_value_changed );
2915         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2916         g_object_set_data( holder, "box3d_angle_x_action", eact );
2917         box3d_angle_x = eact;
2918     }
2920     if (!persp3d_VP_is_finite(persp, Proj::X)) {
2921         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2922     } else {
2923         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2924     }
2927     /* VP X state */
2928     {
2929         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2930                                                       // Translators: VP is short for 'vanishing point'
2931                                                       _("State of VP in X direction"),
2932                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2933                                                       "toggle_vp_x",
2934                                                       Inkscape::ICON_SIZE_DECORATION );
2935         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2936         g_object_set_data( holder, "box3d_vp_x_state_action", act );
2937         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2938         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2939         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2940     }
2942     /* Angle Y */
2943     {
2944         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2945         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2946         eact = create_adjustment_action( "3DBoxAngleYAction",
2947                                          _("Angle in Y direction"), _("Angle Y:"),
2948                                          // Translators: PL is short for 'perspective line'
2949                                          _("Angle of PLs in Y direction"),
2950                                          "tools.shapes.3dbox", "box3d_angle_y", 30,
2951                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2952                                          -360.0, 360.0, 1.0, 10.0,
2953                                          labels, values, G_N_ELEMENTS(labels),
2954                                          box3d_angle_y_value_changed );
2955         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2956         g_object_set_data( holder, "box3d_angle_y_action", eact );
2957         box3d_angle_y = eact;
2958     }
2960     if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2961         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2962     } else {
2963         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2964     }
2966     /* VP Y state */
2967     {
2968         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2969                                                       // Translators: VP is short for 'vanishing point'
2970                                                       _("State of VP in Y direction"),
2971                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2972                                                       "toggle_vp_y",
2973                                                       Inkscape::ICON_SIZE_DECORATION );
2974         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2975         g_object_set_data( holder, "box3d_vp_y_state_action", act );
2976         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2977         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2978         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2979     }
2981     /* Angle Z */
2982     {
2983         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2984         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2985         eact = create_adjustment_action( "3DBoxAngleZAction",
2986                                          _("Angle in Z direction"), _("Angle Z:"),
2987                                          // Translators: PL is short for 'perspective line'
2988                                          _("Angle of PLs in Z direction"),
2989                                          "tools.shapes.3dbox", "box3d_angle_z", 30,
2990                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2991                                          -360.0, 360.0, 1.0, 10.0,
2992                                          labels, values, G_N_ELEMENTS(labels),
2993                                          box3d_angle_z_value_changed );
2994         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2995         g_object_set_data( holder, "box3d_angle_z_action", eact );
2996         box3d_angle_z = eact;
2997     }
2999     if (!persp3d_VP_is_finite(persp, Proj::Z)) {
3000         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3001     } else {
3002         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3003     }
3005     /* VP Z state */
3006     {
3007         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3008                                                       // Translators: VP is short for 'vanishing point'
3009                                                       _("State of VP in Z direction"),
3010                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3011                                                       "toggle_vp_z",
3012                                                       Inkscape::ICON_SIZE_DECORATION );
3013         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3014         g_object_set_data( holder, "box3d_vp_z_state_action", act );
3015         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3016         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3017         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3018     }
3020     sigc::connection *connection = new sigc::connection(
3021         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3022        );
3023     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3024     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3027 //########################
3028 //##       Spiral       ##
3029 //########################
3031 static void
3032 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
3034     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3036     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3037         prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
3038     }
3040     // quit if run by the attr_changed listener
3041     if (g_object_get_data( tbl, "freeze" )) {
3042         return;
3043     }
3045     // in turn, prevent listener from responding
3046     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3048     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3050     bool modmade = false;
3051     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3052          items != NULL;
3053          items = items->next)
3054     {
3055         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3056             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3057             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3058             SP_OBJECT((SPItem *) items->data)->updateRepr();
3059             modmade = true;
3060         }
3061     }
3063     g_free(namespaced_name);
3065     if (modmade) {
3066         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3067                                    _("Change spiral"));
3068     }
3070     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3073 static void
3074 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3076     sp_spl_tb_value_changed(adj, tbl, "revolution");
3079 static void
3080 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3082     sp_spl_tb_value_changed(adj, tbl, "expansion");
3085 static void
3086 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3088     sp_spl_tb_value_changed(adj, tbl, "t0");
3091 static void
3092 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3094     GtkWidget *tbl = GTK_WIDGET(obj);
3096     GtkAdjustment *adj;
3098     // fixme: make settable
3099     gdouble rev = 5;
3100     gdouble exp = 1.0;
3101     gdouble t0 = 0.0;
3103     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3104     gtk_adjustment_set_value(adj, rev);
3105     gtk_adjustment_value_changed(adj);
3107     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3108     gtk_adjustment_set_value(adj, exp);
3109     gtk_adjustment_value_changed(adj);
3111     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3112     gtk_adjustment_set_value(adj, t0);
3113     gtk_adjustment_value_changed(adj);
3115     spinbutton_defocus(GTK_OBJECT(tbl));
3119 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3120                                          gchar const */*old_value*/, gchar const */*new_value*/,
3121                                          bool /*is_interactive*/, gpointer data)
3123     GtkWidget *tbl = GTK_WIDGET(data);
3125     // quit if run by the _changed callbacks
3126     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3127         return;
3128     }
3130     // in turn, prevent callbacks from responding
3131     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3133     GtkAdjustment *adj;
3134     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3135     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3137     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3138     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3140     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3141     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3143     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3147 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3148     NULL, /* child_added */
3149     NULL, /* child_removed */
3150     spiral_tb_event_attr_changed,
3151     NULL, /* content_changed */
3152     NULL  /* order_changed */
3153 };
3155 static void
3156 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3158     int n_selected = 0;
3159     Inkscape::XML::Node *repr = NULL;
3161     purge_repr_listener( tbl, tbl );
3163     for (GSList const *items = selection->itemList();
3164          items != NULL;
3165          items = items->next)
3166     {
3167         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3168             n_selected++;
3169             repr = SP_OBJECT_REPR((SPItem *) items->data);
3170         }
3171     }
3173     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3175     if (n_selected == 0) {
3176         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3177     } else if (n_selected == 1) {
3178         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3180         if (repr) {
3181             g_object_set_data( tbl, "repr", repr );
3182             Inkscape::GC::anchor(repr);
3183             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3184             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3185         }
3186     } else {
3187         // FIXME: implement averaging of all parameters for multiple selected
3188         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3189         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3190     }
3194 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3196     EgeAdjustmentAction* eact = 0;
3197     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3199     {
3200         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3201         ege_output_action_set_use_markup( act, TRUE );
3202         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3203         g_object_set_data( holder, "mode_action", act );
3204     }
3206     /* Revolution */
3207     {
3208         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3209         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3210         eact = create_adjustment_action( "SpiralRevolutionAction",
3211                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3212                                          "tools.shapes.spiral", "revolution", 3.0,
3213                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3214                                          0.01, 1024.0, 0.1, 1.0,
3215                                          labels, values, G_N_ELEMENTS(labels),
3216                                          sp_spl_tb_revolution_value_changed, 1, 2);
3217         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3218     }
3220     /* Expansion */
3221     {
3222         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3223         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3224         eact = create_adjustment_action( "SpiralExpansionAction",
3225                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3226                                          "tools.shapes.spiral", "expansion", 1.0,
3227                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3228                                          0.0, 1000.0, 0.01, 1.0,
3229                                          labels, values, G_N_ELEMENTS(labels),
3230                                          sp_spl_tb_expansion_value_changed);
3231         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3232     }
3234     /* T0 */
3235     {
3236         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3237         gdouble values[] = {0, 0.5, 0.9};
3238         eact = create_adjustment_action( "SpiralT0Action",
3239                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3240                                          "tools.shapes.spiral", "t0", 0.0,
3241                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3242                                          0.0, 0.999, 0.01, 1.0,
3243                                          labels, values, G_N_ELEMENTS(labels),
3244                                          sp_spl_tb_t0_value_changed);
3245         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3246     }
3248     /* Reset */
3249     {
3250         InkAction* inky = ink_action_new( "SpiralResetAction",
3251                                           _("Defaults"),
3252                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3253                                           GTK_STOCK_CLEAR,
3254                                           secondarySize );
3255         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3256         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3257     }
3260     sigc::connection *connection = new sigc::connection(
3261         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3262         );
3263     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3264     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3267 //########################
3268 //##     Pen/Pencil     ##
3269 //########################
3271 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3272 static char const *
3273 freehand_tool_name(GObject *dataKludge)
3275     SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3276     return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3277              ? "tools.freehand.pen"
3278              : "tools.freehand.pencil" );
3281 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3283     gint mode = ege_select_one_action_get_active(act);
3285     prefs_set_int_attribute(freehand_tool_name(tbl), "freehand-mode", mode);
3287     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3289     // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3290     // preparatory work here
3291     if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3292         SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3293         pc->polylines_only = (mode == 2);
3294     }
3297 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3299     /* Freehand mode toggle buttons */
3300     {
3301         guint freehandMode = prefs_get_int_attribute(tool_is_pencil ? "tools.freehand.pencil" : "tools.freehand.pen", "freehand-mode", 0);
3302         Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3304         {
3305             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3307             GtkTreeIter iter;
3308             gtk_list_store_append( model, &iter );
3309             gtk_list_store_set( model, &iter,
3310                                 0, _("Bezier"),
3311                                 1, _("Create regular Bezier path"),
3312                                 2, "bezier_mode",
3313                                 -1 );
3315             gtk_list_store_append( model, &iter );
3316             gtk_list_store_set( model, &iter,
3317                                 0, _("Spiro"),
3318                                 1, _("Create Spiro path"),
3319                                 2, "spiro_splines_mode",
3320                                 -1 );
3322             if (!tool_is_pencil) {
3323                 gtk_list_store_append( model, &iter );
3324                 gtk_list_store_set( model, &iter,
3325                                     0, _("Zigzag"),
3326                                     1, _("Create a sequence of straight line segments"),
3327                                     2, "polylines_mode",
3328                                     -1 );
3329             }
3331             EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3332                                                                 "FreehandModeActionPencil" :
3333                                                                 "FreehandModeActionPen",
3334                                                                 (_("Mode:")), ("Mode"), NULL, GTK_TREE_MODEL(model) );
3335             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3337             ege_select_one_action_set_appearance( act, "full" );
3338             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3339             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3340             ege_select_one_action_set_icon_column( act, 2 );
3341             ege_select_one_action_set_icon_size( act, secondarySize );
3342             ege_select_one_action_set_tooltip_column( act, 1  );
3344             ege_select_one_action_set_active( act, freehandMode);
3345             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3346         }
3347     }
3350 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3351     gint shape = ege_select_one_action_get_active( act );
3352     prefs_set_int_attribute(freehand_tool_name(dataKludge), "shape", shape);
3355 /**
3356  * \brief Generate the list of freehand advanced shape option entries.
3357  */
3358 GList * freehand_shape_dropdown_items_list() {
3359     GList *glist = NULL;
3361     glist = g_list_append (glist, _("None"));
3362     glist = g_list_append (glist, _("Triangle in"));
3363     glist = g_list_append (glist, _("Triangle out"));
3364     glist = g_list_append (glist, _("Ellipse"));
3365     glist = g_list_append (glist, _("From clipboard"));
3367     return glist;
3370 static void
3371 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3372     /*advanced shape options */
3373     {
3374         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3376         GList* items = 0;
3377         gint count = 0;
3378         for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3379         {
3380             GtkTreeIter iter;
3381             gtk_list_store_append( model, &iter );
3382             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3383             count++;
3384         }
3385         g_list_free( items );
3386         items = 0;
3387         EgeSelectOneAction* act1 = ege_select_one_action_new(
3388             tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3389             _("Shape:"), ("Shape"), NULL, GTK_TREE_MODEL(model));
3390         g_object_set( act1, "short_label", _("Shape:"), NULL );
3391         ege_select_one_action_set_appearance( act1, "compact" );
3392         ege_select_one_action_set_active( act1, prefs_get_int_attribute(tool_is_pencil ? "tools.freehand.pencil" : "tools.freehand.pen", "shape", 0) );
3393         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3394         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3395         g_object_set_data( holder, "shape_action", act1 );
3396     }
3399 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3401     sp_add_freehand_mode_toggle(mainActions, holder, false);
3402     freehand_add_advanced_shape_options(mainActions, holder, false);
3406 static void
3407 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3409     GtkWidget *tbl = GTK_WIDGET(obj);
3411     GtkAdjustment *adj;
3413     // fixme: make settable
3414     gdouble tolerance = 4;
3416     adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3417     gtk_adjustment_set_value(adj, tolerance);
3418     gtk_adjustment_value_changed(adj);
3420     spinbutton_defocus(GTK_OBJECT(tbl));
3423 static void
3424 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3427     // quit if run by the attr_changed listener
3428     if (g_object_get_data( tbl, "freeze" )) {
3429         return;
3430     }
3431     // in turn, prevent listener from responding
3432     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3433     prefs_set_double_attribute("tools.freehand.pencil",
3434                                "tolerance", adj->value);
3435     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3441 static void
3442 sp_pencil_tb_tolerance_value_changed_external(Inkscape::XML::Node */*repr*/,
3443                                               const gchar */*key*/,
3444                                               const gchar */*oldval*/,
3445                                               const gchar */*newval*/,
3446                                               bool /*is_interactive*/,
3447                                               void * data)
3449     GObject* tbl = G_OBJECT(data);
3450     if (g_object_get_data( tbl, "freeze" )) {
3451         return;
3452     }
3454     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3456     GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl,
3457                                                             "tolerance");
3459     double v = prefs_get_double_attribute("tools.freehand.pencil",
3460                                             "tolerance", adj->value);
3461     gtk_adjustment_set_value(adj, v);
3462     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3466 static Inkscape::XML::NodeEventVector pencil_node_events =
3468     NULL,
3469     NULL,
3470     sp_pencil_tb_tolerance_value_changed_external,
3471     NULL,
3472     NULL,
3473 };
3476 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3478     sp_add_freehand_mode_toggle(mainActions, holder, true);
3480     EgeAdjustmentAction* eact = 0;
3482     /* Tolerance */
3483     {
3484         gchar const* labels[] = {_("(many nodes, rough)"), ("(default)"), 0, 0, 0, 0, ("(few nodes, smooth)")};
3485         gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
3486         eact = create_adjustment_action( "PencilToleranceAction",
3487                                          _("Smoothing:"), _("Smoothing: "),
3488                  _("How much smoothing (simplifying) is applied to the line"),
3489                                          "tools.freehand.pencil", "tolerance",
3490                                          3.0,
3491                                          GTK_WIDGET(desktop->canvas), NULL,
3492                                          holder, TRUE, "altx-pencil",
3493                                          1, 100.0, 0.5, 0,
3494                                          labels, values, G_N_ELEMENTS(labels),
3495                                          sp_pencil_tb_tolerance_value_changed,
3496                                          1, 2);
3497         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3498         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3500         Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE,
3501                                                       "tools.freehand.pencil");
3502         repr->addListener(&pencil_node_events, G_OBJECT(holder));
3503         g_object_set_data(G_OBJECT(holder), "repr", repr);
3505     }
3507     /* advanced shape options */
3508     freehand_add_advanced_shape_options(mainActions, holder, true);
3510     /* Reset */
3511     {
3512         InkAction* inky = ink_action_new( "PencilResetAction",
3513                                           _("Defaults"),
3514                                           _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3515                                           GTK_STOCK_CLEAR,
3516                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3517         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
3518         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3519     }
3521     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3526 //########################
3527 //##       Tweak        ##
3528 //########################
3530 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3532     prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3535 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3537     prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3540 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3542     prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3545 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3547     int mode = ege_select_one_action_get_active( act );
3548     prefs_set_int_attribute("tools.tweak", "mode", mode);
3550     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3551     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3552     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3553     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3554     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3555     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3556     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3557         if (doh) gtk_action_set_sensitive (doh, TRUE);
3558         if (dos) gtk_action_set_sensitive (dos, TRUE);
3559         if (dol) gtk_action_set_sensitive (dol, TRUE);
3560         if (doo) gtk_action_set_sensitive (doo, TRUE);
3561         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3562         if (fid) gtk_action_set_sensitive (fid, FALSE);
3563     } else {
3564         if (doh) gtk_action_set_sensitive (doh, FALSE);
3565         if (dos) gtk_action_set_sensitive (dos, FALSE);
3566         if (dol) gtk_action_set_sensitive (dol, FALSE);
3567         if (doo) gtk_action_set_sensitive (doo, FALSE);
3568         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3569         if (fid) gtk_action_set_sensitive (fid, TRUE);
3570     }
3573 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3575     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3578 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3579     bool show = gtk_toggle_action_get_active( act );
3580     prefs_set_int_attribute ("tools.tweak", "doh",  show ? 1 : 0);
3582 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3583     bool show = gtk_toggle_action_get_active( act );
3584     prefs_set_int_attribute ("tools.tweak", "dos",  show ? 1 : 0);
3586 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3587     bool show = gtk_toggle_action_get_active( act );
3588     prefs_set_int_attribute ("tools.tweak", "dol",  show ? 1 : 0);
3590 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3591     bool show = gtk_toggle_action_get_active( act );
3592     prefs_set_int_attribute ("tools.tweak", "doo",  show ? 1 : 0);
3595 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3597     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3599     {
3600         /* Width */
3601         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3602         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3603         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3604                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3605                                                               "tools.tweak", "width", 15,
3606                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3607                                                               1, 100, 1.0, 0.0,
3608                                                               labels, values, G_N_ELEMENTS(labels),
3609                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
3610         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3611         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3612         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3613     }
3616     {
3617         /* Force */
3618         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3619         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3620         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3621                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
3622                                                               "tools.tweak", "force", 20,
3623                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3624                                                               1, 100, 1.0, 0.0,
3625                                                               labels, values, G_N_ELEMENTS(labels),
3626                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
3627         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3628         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3629         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3630     }
3632     /* Mode */
3633     {
3634         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3636         GtkTreeIter iter;
3637         gtk_list_store_append( model, &iter );
3638         gtk_list_store_set( model, &iter,
3639                             0, _("Push mode"),
3640                             1, _("Push parts of paths in any direction"),
3641                             2, "tweak_push_mode",
3642                             -1 );
3644         gtk_list_store_append( model, &iter );
3645         gtk_list_store_set( model, &iter,
3646                             0, _("Shrink mode"),
3647                             1, _("Shrink (inset) parts of paths"),
3648                             2, "tweak_shrink_mode",
3649                             -1 );
3651         gtk_list_store_append( model, &iter );
3652         gtk_list_store_set( model, &iter,
3653                             0, _("Grow mode"),
3654                             1, _("Grow (outset) parts of paths"),
3655                             2, "tweak_grow_mode",
3656                             -1 );
3658         gtk_list_store_append( model, &iter );
3659         gtk_list_store_set( model, &iter,
3660                             0, _("Attract mode"),
3661                             1, _("Attract parts of paths towards cursor"),
3662                             2, "tweak_attract_mode",
3663                             -1 );
3665         gtk_list_store_append( model, &iter );
3666         gtk_list_store_set( model, &iter,
3667                             0, _("Repel mode"),
3668                             1, _("Repel parts of paths from cursor"),
3669                             2, "tweak_repel_mode",
3670                             -1 );
3672         gtk_list_store_append( model, &iter );
3673         gtk_list_store_set( model, &iter,
3674                             0, _("Roughen mode"),
3675                             1, _("Roughen parts of paths"),
3676                             2, "tweak_roughen_mode",
3677                             -1 );
3679         gtk_list_store_append( model, &iter );
3680         gtk_list_store_set( model, &iter,
3681                             0, _("Color paint mode"),
3682                             1, _("Paint the tool's color upon selected objects"),
3683                             2, "tweak_colorpaint_mode",
3684                             -1 );
3686         gtk_list_store_append( model, &iter );
3687         gtk_list_store_set( model, &iter,
3688                             0, _("Color jitter mode"),
3689                             1, _("Jitter the colors of selected objects"),
3690                             2, "tweak_colorjitter_mode",
3691                             -1 );
3693         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3694         g_object_set( act, "short_label", _("Mode:"), NULL );
3695         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3696         g_object_set_data( holder, "mode_action", act );
3698         ege_select_one_action_set_appearance( act, "full" );
3699         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3700         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3701         ege_select_one_action_set_icon_column( act, 2 );
3702         ege_select_one_action_set_icon_size( act, secondarySize );
3703         ege_select_one_action_set_tooltip_column( act, 1  );
3705         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3706         ege_select_one_action_set_active( act, mode );
3707         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3709         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3710     }
3712     guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3714     {
3715         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3716         ege_output_action_set_use_markup( act, TRUE );
3717         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3718         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3719             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3720         g_object_set_data( holder, "tweak_channels_label", act);
3721     }
3723     {
3724         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3725                                                       _("Hue"),
3726                                                       _("In color mode, act on objects' hue"),
3727                                                       NULL,
3728                                                       Inkscape::ICON_SIZE_DECORATION );
3729         //TRANSLATORS:  "H" here stands for hue
3730         g_object_set( act, "short_label", _("H"), NULL );
3731         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3732         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3733         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3734         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3735             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3736         g_object_set_data( holder, "tweak_doh", act);
3737     }
3738     {
3739         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3740                                                       _("Saturation"),
3741                                                       _("In color mode, act on objects' saturation"),
3742                                                       NULL,
3743                                                       Inkscape::ICON_SIZE_DECORATION );
3744         //TRANSLATORS: "S" here stands for Saturation
3745         g_object_set( act, "short_label", _("S"), NULL );
3746         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3747         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3748         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3749         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3750             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3751         g_object_set_data( holder, "tweak_dos", act );
3752     }
3753     {
3754         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3755                                                       _("Lightness"),
3756                                                       _("In color mode, act on objects' lightness"),
3757                                                       NULL,
3758                                                       Inkscape::ICON_SIZE_DECORATION );
3759         //TRANSLATORS: "L" here stands for Lightness
3760         g_object_set( act, "short_label", _("L"), NULL );
3761         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3762         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3763         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3764         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3765             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3766         g_object_set_data( holder, "tweak_dol", act );
3767     }
3768     {
3769         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3770                                                       _("Opacity"),
3771                                                       _("In color mode, act on objects' opacity"),
3772                                                       NULL,
3773                                                       Inkscape::ICON_SIZE_DECORATION );
3774         //TRANSLATORS: "O" here stands for Opacity
3775         g_object_set( act, "short_label", _("O"), NULL );
3776         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3777         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3778         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3779         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3780             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3781         g_object_set_data( holder, "tweak_doo", act );
3782     }
3784     {   /* Fidelity */
3785         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3786         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3787         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3788                                                               _("Fidelity"), _("Fidelity:"),
3789                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3790                                                               "tools.tweak", "fidelity", 50,
3791                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3792                                                               1, 100, 1.0, 10.0,
3793                                                               labels, values, G_N_ELEMENTS(labels),
3794                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
3795         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3796         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3797         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3798             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3799         g_object_set_data( holder, "tweak_fidelity", eact );
3800     }
3803     /* Use Pressure button */
3804     {
3805         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3806                                                       _("Pressure"),
3807                                                       _("Use the pressure of the input device to alter the force of tweak action"),
3808                                                       "use_pressure",
3809                                                       Inkscape::ICON_SIZE_DECORATION );
3810         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3811         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3812         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3813     }
3818 //########################
3819 //##     Calligraphy    ##
3820 //########################
3821 static void update_presets_list (GObject *tbl)
3823     if (g_object_get_data(tbl, "presets_blocked"))
3824         return;
3826     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3827     if (!sel) {
3828         ege_select_one_action_set_active(sel, 0);
3829         return;
3830     }
3832     int total_prefs = pref_path_number_of_children("tools.calligraphic.preset");
3834     for (int i = 1; i <= total_prefs; i++) {
3835         gchar *preset_path = get_pref_nth_child("tools.calligraphic.preset", i);
3836         Inkscape::XML::Node *preset_repr = inkscape_get_repr(INKSCAPE, preset_path);
3838         bool match = true;
3840         for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
3841               iter; 
3842               ++iter ) {
3843             const gchar *attr_name = g_quark_to_string(iter->key);
3844             if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
3845                 continue;
3846             void *widget = g_object_get_data(tbl, attr_name);
3847             if (widget) {
3848                 if (GTK_IS_ADJUSTMENT(widget)) {
3849                     double v = prefs_get_double_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
3850                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
3851                     //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
3852                     if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
3853                         match = false;
3854                         break;
3855                     }
3856                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
3857                     int v = prefs_get_int_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
3858                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
3859                     //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
3860                     if (gtk_toggle_action_get_active(toggle) != v) {
3861                         match = false;
3862                         break;
3863                     }
3864                 } 
3865             } 
3866         }
3868         if (match) {
3869             // newly added item is at the same index as the 
3870             // save command, so we need to change twice for it to take effect
3871             ege_select_one_action_set_active(sel, 0);
3872             ege_select_one_action_set_active(sel, i);
3873             return;
3874         }
3875     }
3877     // no match found
3878     ege_select_one_action_set_active(sel, 0);
3881 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3883     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value * 0.01 );
3884     update_presets_list(tbl);
3887 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3889     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value * 0.01 );
3890     update_presets_list(tbl);
3893 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3895     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3896     update_presets_list(tbl);
3899 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3901     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3902     update_presets_list(tbl);
3905 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3907     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value * 0.01 );
3908     update_presets_list(tbl);
3911 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3913     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value * 0.01);
3914     update_presets_list(tbl);
3917 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3919     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value * 0.01 );
3920     update_presets_list(tbl);
3923 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3925     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3926     update_presets_list(tbl);
3929 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
3931     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3932     update_presets_list(tbl);
3935 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
3937     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3938     update_presets_list(tbl);
3941 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
3943     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
3944     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3945     update_presets_list(tbl);
3946     if (calligraphy_angle )
3947         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3951 static gchar const *const widget_names[] = {
3952     "width",
3953     "mass",
3954     "wiggle",
3955     "angle",
3956     "thinning",
3957     "tremor",
3958     "flatness",
3959     "cap_rounding",
3960     "usepressure",
3961     "tracebackground",
3962     "usetilt"
3963 };
3966 static void sp_dcc_build_presets_list(GObject *tbl) 
3968     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
3970     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3971     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
3972     gtk_list_store_clear (model);
3974     {
3975         GtkTreeIter iter;
3976         gtk_list_store_append( model, &iter );
3977         gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
3978     }
3980     //TODO: switch back to prefs API
3981     Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
3982     Inkscape::XML::Node *child_repr = sp_repr_children(repr);
3983     int ii=1;
3984     while (child_repr) {
3985         GtkTreeIter iter;
3986         char *preset_name = (char *) child_repr->attribute("name");
3987         gtk_list_store_append( model, &iter );
3988         gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );
3989         child_repr = sp_repr_next(child_repr);
3990     }
3992     {
3993         GtkTreeIter iter;
3994         gtk_list_store_append( model, &iter );
3995         gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
3996         g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
3997     }
3999     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4001     update_presets_list (tbl);
4004 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4006     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4007     if (! desktop) return;
4009     if (g_object_get_data(tbl, "presets_blocked")) 
4010         return;
4012     Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
4013     if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) {
4014         // dialog cancelled
4015         update_presets_list (tbl);
4016         return;
4017     }
4018     Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
4020     if (!profile_name.c_str() || *profile_name.c_str() == 0) {
4021         // empty name entered
4022         update_presets_list (tbl);
4023         return;
4024     }
4026     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4028     int new_index = -1;
4029     gchar *pref_path = NULL;
4030     int total_prefs = pref_path_number_of_children("tools.calligraphic.preset");
4032     for (int i = 1; i <= total_prefs; i++) {
4033         gchar *path = get_pref_nth_child("tools.calligraphic.preset", i);
4034         const gchar *name = prefs_get_string_attribute(path, "name");
4035         if (name && !strcmp(name, profile_name.c_str())) {
4036             // we already have preset with this name, replace its values
4037             new_index = i;
4038             pref_path = g_strdup(path);
4039             break;
4040         }
4041     }
4043     if (new_index == -1) {
4044         // no preset with this name, create 
4045         new_index = total_prefs + 1;
4046         gchar *profile_id = g_strdup_printf("dcc%d", new_index);
4047         pref_path = create_pref("tools.calligraphic.preset", profile_id);
4048         g_free(profile_id);
4049     }
4051     for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4052         gchar const *const widget_name = widget_names[i];
4053         void *widget = g_object_get_data(tbl, widget_name);
4054         if (widget) {
4055             if (GTK_IS_ADJUSTMENT(widget)) {
4056                 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4057                 double v = gtk_adjustment_get_value(adj);
4058                 prefs_set_double_attribute(pref_path, widget_name, v);
4059                 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4060             } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4061                 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4062                 int v = gtk_toggle_action_get_active(toggle);
4063                 prefs_set_int_attribute(pref_path, widget_name, v);
4064                 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4065             } else {
4066                g_warning("Unknown widget type for preset: %s\n", widget_name);
4067             }
4068         } else {
4069             g_warning("Bad key when writing preset: %s\n", widget_name);
4070         }
4071     }
4072     prefs_set_string_attribute(pref_path, "name", profile_name.c_str());
4074     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4076     sp_dcc_build_presets_list (tbl);
4078     g_free(pref_path);
4082 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4084     gint preset_index = ege_select_one_action_get_active( act );
4085     gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4087     if (preset_index == save_presets_index) {
4088         // this is the Save command
4089         sp_dcc_save_profile(NULL, tbl);
4090         return;
4091     }
4093     if (g_object_get_data(tbl, "presets_blocked")) 
4094         return;
4096     gchar *const preset_path = get_pref_nth_child("tools.calligraphic.preset", preset_index);
4098     if (preset_path) {
4099         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
4101         Inkscape::XML::Node *preset_repr = inkscape_get_repr(INKSCAPE, preset_path);
4103         for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
4104               iter; 
4105               ++iter ) {
4106             const gchar *attr_name = g_quark_to_string(iter->key);
4107             if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
4108                 continue;
4109             void *widget = g_object_get_data(tbl, attr_name);
4110             if (widget) {
4111                 if (GTK_IS_ADJUSTMENT(widget)) {
4112                     double v = prefs_get_double_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4113                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4114                     gtk_adjustment_set_value(adj, v);
4115                     //std::cout << "set adj " << attr_name << " to " << v << "\n";
4116                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4117                     int v = prefs_get_int_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4118                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4119                     gtk_toggle_action_set_active(toggle, v);
4120                     //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4121                 } else {
4122                     g_warning("Unknown widget type for preset: %s\n", attr_name);
4123                 }
4124             } else {
4125                 g_warning("Bad key found in a preset record: %s\n", attr_name);
4126             }
4127         }
4128         g_free(preset_path);
4129         g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4130     }
4135 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4137     {
4138         g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4140         EgeAdjustmentAction* calligraphy_angle = 0;
4142         {
4143         /* Width */
4144         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4145         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4146         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4147                                                               _("Pen Width"), _("Width:"),
4148                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
4149                                                               "tools.calligraphic", "width", 15,
4150                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4151                                                               1, 100, 1.0, 0.0,
4152                                                               labels, values, G_N_ELEMENTS(labels),
4153                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
4154         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4155         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4156         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4157         }
4159         {
4160         /* Thinning */
4161             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4162             gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4163         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4164                                                               _("Stroke Thinning"), _("Thinning:"),
4165                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4166                                                               "tools.calligraphic", "thinning", 10,
4167                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4168                                                               -100, 100, 1, 0.1,
4169                                                               labels, values, G_N_ELEMENTS(labels),
4170                                                               sp_ddc_velthin_value_changed, 0.01, 0, 100);
4171         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4172         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4173         }
4175         {
4176         /* Angle */
4177         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4178         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4179         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4180                                                               _("Pen Angle"), _("Angle:"),
4181                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4182                                                               "tools.calligraphic", "angle", 30,
4183                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4184                                                               -90.0, 90.0, 1.0, 10.0,
4185                                                               labels, values, G_N_ELEMENTS(labels),
4186                                                               sp_ddc_angle_value_changed, 1, 0 );
4187         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4188         g_object_set_data( holder, "angle_action", eact );
4189         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4190         calligraphy_angle = eact;
4191         }
4193         {
4194         /* Fixation */
4195             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4196         gdouble values[] = {0, 20, 40, 60, 90, 100};
4197         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4198                                                               _("Fixation"), _("Fixation:"),
4199                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
4200                                                               "tools.calligraphic", "flatness", 90,
4201                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4202                                                               0.0, 100, 1.0, 10.0,
4203                                                               labels, values, G_N_ELEMENTS(labels),
4204                                                               sp_ddc_flatness_value_changed, 0.01, 0, 100 );
4205         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4206         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4207         }
4209         {
4210         /* Cap Rounding */
4211             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4212         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4213         // TRANSLATORS: "cap" means "end" (both start and finish) here
4214         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4215                                                               _("Cap rounding"), _("Caps:"),
4216                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4217                                                               "tools.calligraphic", "cap_rounding", 0.0,
4218                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4219                                                               0.0, 5.0, 0.01, 0.1,
4220                                                               labels, values, G_N_ELEMENTS(labels),
4221                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4222         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4223         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4224         }
4226         {
4227         /* Tremor */
4228             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4229         gdouble values[] = {0, 10, 20, 40, 60, 100};
4230         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4231                                                               _("Stroke Tremor"), _("Tremor:"),
4232                                                               _("Increase to make strokes rugged and trembling"),
4233                                                               "tools.calligraphic", "tremor", 0.0,
4234                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4235                                                               0.0, 100, 1, 0.0,
4236                                                               labels, values, G_N_ELEMENTS(labels),
4237                                                               sp_ddc_tremor_value_changed, 0.01, 0, 100 );
4239         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4240         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4241         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4242         }
4244         {
4245         /* Wiggle */
4246         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4247         gdouble values[] = {0, 20, 40, 60, 100};
4248         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4249                                                               _("Pen Wiggle"), _("Wiggle:"),
4250                                                               _("Increase to make the pen waver and wiggle"),
4251                                                               "tools.calligraphic", "wiggle", 0.0,
4252                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4253                                                               0.0, 100, 1, 0.0,
4254                                                               labels, values, G_N_ELEMENTS(labels),
4255                                                               sp_ddc_wiggle_value_changed, 0.01, 0, 100 );
4256         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4257         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4258         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4259         }
4261         {
4262         /* Mass */
4263             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4264         gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4265         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4266                                                               _("Pen Mass"), _("Mass:"),
4267                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
4268                                                               "tools.calligraphic", "mass", 2.0,
4269                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4270                                                               0.0, 100, 1, 0.0,
4271                                                               labels, values, G_N_ELEMENTS(labels),
4272                                                               sp_ddc_mass_value_changed, 0.01, 0, 100 );
4273         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4274         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4275         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4276         }
4279         /* Trace Background button */
4280         {
4281             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4282                                                           _("Trace Background"),
4283                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4284                                                           "trace_background",
4285                                                           Inkscape::ICON_SIZE_DECORATION );
4286             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4287             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4288             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
4289             g_object_set_data( holder, "tracebackground", act );
4290         }
4292         /* Use Pressure button */
4293         {
4294             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4295                                                           _("Pressure"),
4296                                                           _("Use the pressure of the input device to alter the width of the pen"),
4297                                                           "use_pressure",
4298                                                           Inkscape::ICON_SIZE_DECORATION );
4299             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4300             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4301             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
4302             g_object_set_data( holder, "usepressure", act );
4303         }
4305         /* Use Tilt button */
4306         {
4307             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4308                                                           _("Tilt"),
4309                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
4310                                                           "use_tilt",
4311                                                           Inkscape::ICON_SIZE_DECORATION );
4312             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4313             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4314             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4315             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4316             g_object_set_data( holder, "usetilt", act );
4317         }
4319         /*calligraphic profile */
4320         {
4321             GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
4322             EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
4323             ege_select_one_action_set_appearance (act1, "compact");
4324             g_object_set_data (holder, "profile_selector", act1 );
4326             g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
4328             sp_dcc_build_presets_list (holder);
4330             g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
4331             gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
4332         }
4333     }
4337 //########################
4338 //##    Circle / Arc    ##
4339 //########################
4341 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4343     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4344     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4346     if (v1 == 0 && v2 == 0) {
4347         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4348             gtk_action_set_sensitive( ocb, FALSE );
4349             gtk_action_set_sensitive( make_whole, FALSE );
4350         }
4351     } else {
4352         gtk_action_set_sensitive( ocb, TRUE );
4353         gtk_action_set_sensitive( make_whole, TRUE );
4354     }
4357 static void
4358 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4360     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4362     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4363         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
4364     }
4366     // quit if run by the attr_changed listener
4367     if (g_object_get_data( tbl, "freeze" )) {
4368         return;
4369     }
4371     // in turn, prevent listener from responding
4372     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4374     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4376     bool modmade = false;
4377     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4378          items != NULL;
4379          items = items->next)
4380     {
4381         SPItem *item = SP_ITEM(items->data);
4383         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4385             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4386             SPArc *arc = SP_ARC(item);
4388             if (!strcmp(value_name, "start"))
4389                 ge->start = (adj->value * M_PI)/ 180;
4390             else
4391                 ge->end = (adj->value * M_PI)/ 180;
4393             sp_genericellipse_normalize(ge);
4394             ((SPObject *)arc)->updateRepr();
4395             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4397             modmade = true;
4398         }
4399     }
4401     g_free(namespaced_name);
4403     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4405     sp_arctb_sensitivize( tbl, adj->value, other->value );
4407     if (modmade) {
4408         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4409                                    _("Arc: Change start/end"));
4410     }
4412     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4416 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
4418     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
4421 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4423     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
4427 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4429     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4430     gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
4431     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4432         prefs_set_int_attribute( "tools.eraser", "mode", eraserMode );
4433     }
4435     // only take action if run by the attr_changed listener
4436     if (!g_object_get_data( tbl, "freeze" )) {
4437         // in turn, prevent listener from responding
4438         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4440         if ( eraserMode != 0 ) {
4441         } else {
4442         }
4443         // TODO finish implementation
4445         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4446     }
4449 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4451     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4452     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4453         if ( ege_select_one_action_get_active( act ) != 0 ) {
4454             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
4455         } else {
4456             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
4457         }
4458     }
4460     // quit if run by the attr_changed listener
4461     if (g_object_get_data( tbl, "freeze" )) {
4462         return;
4463     }
4465     // in turn, prevent listener from responding
4466     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4468     bool modmade = false;
4470     if ( ege_select_one_action_get_active(act) != 0 ) {
4471         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4472              items != NULL;
4473              items = items->next)
4474         {
4475             if (SP_IS_ARC((SPItem *) items->data)) {
4476                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4477                 repr->setAttribute("sodipodi:open", "true");
4478                 SP_OBJECT((SPItem *) items->data)->updateRepr();
4479                 modmade = true;
4480             }
4481         }
4482     } else {
4483         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4484              items != NULL;
4485              items = items->next)
4486         {
4487             if (SP_IS_ARC((SPItem *) items->data))    {
4488                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4489                 repr->setAttribute("sodipodi:open", NULL);
4490                 SP_OBJECT((SPItem *) items->data)->updateRepr();
4491                 modmade = true;
4492             }
4493         }
4494     }
4496     if (modmade) {
4497         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4498                                    _("Arc: Change open/closed"));
4499     }
4501     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4504 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4506     GtkAdjustment *adj;
4507     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
4508     gtk_adjustment_set_value(adj, 0.0);
4509     gtk_adjustment_value_changed(adj);
4511     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
4512     gtk_adjustment_set_value(adj, 0.0);
4513     gtk_adjustment_value_changed(adj);
4515     spinbutton_defocus( GTK_OBJECT(obj) );
4518 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
4519                                       gchar const */*old_value*/, gchar const */*new_value*/,
4520                                       bool /*is_interactive*/, gpointer data)
4522     GObject *tbl = G_OBJECT(data);
4524     // quit if run by the _changed callbacks
4525     if (g_object_get_data( tbl, "freeze" )) {
4526         return;
4527     }
4529     // in turn, prevent callbacks from responding
4530     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4532     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4533     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4535     GtkAdjustment *adj1,*adj2;
4536     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4537     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4538     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4539     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4541     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4543     char const *openstr = NULL;
4544     openstr = repr->attribute("sodipodi:open");
4545     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4547     if (openstr) {
4548         ege_select_one_action_set_active( ocb, 1 );
4549     } else {
4550         ege_select_one_action_set_active( ocb, 0 );
4551     }
4553     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4556 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4557     NULL, /* child_added */
4558     NULL, /* child_removed */
4559     arc_tb_event_attr_changed,
4560     NULL, /* content_changed */
4561     NULL  /* order_changed */
4562 };
4565 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4567     int n_selected = 0;
4568     Inkscape::XML::Node *repr = NULL;
4570     purge_repr_listener( tbl, tbl );
4572     for (GSList const *items = selection->itemList();
4573          items != NULL;
4574          items = items->next)
4575     {
4576         if (SP_IS_ARC((SPItem *) items->data)) {
4577             n_selected++;
4578             repr = SP_OBJECT_REPR((SPItem *) items->data);
4579         }
4580     }
4582     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4584     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4585     if (n_selected == 0) {
4586         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4587     } else if (n_selected == 1) {
4588         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4589         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4591         if (repr) {
4592             g_object_set_data( tbl, "repr", repr );
4593             Inkscape::GC::anchor(repr);
4594             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4595             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4596         }
4597     } else {
4598         // FIXME: implement averaging of all parameters for multiple selected
4599         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4600         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4601         sp_arctb_sensitivize( tbl, 1, 0 );
4602     }
4606 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4608     EgeAdjustmentAction* eact = 0;
4609     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
4612     {
4613         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4614         ege_output_action_set_use_markup( act, TRUE );
4615         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4616         g_object_set_data( holder, "mode_action", act );
4617     }
4619     /* Start */
4620     {
4621         eact = create_adjustment_action( "ArcStartAction",
4622                                          _("Start"), _("Start:"),
4623                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
4624                                          "tools.shapes.arc", "start", 0.0,
4625                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4626                                          -360.0, 360.0, 1.0, 10.0,
4627                                          0, 0, 0,
4628                                          sp_arctb_start_value_changed);
4629         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4630     }
4632     /* End */
4633     {
4634         eact = create_adjustment_action( "ArcEndAction",
4635                                          _("End"), _("End:"),
4636                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
4637                                          "tools.shapes.arc", "end", 0.0,
4638                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4639                                          -360.0, 360.0, 1.0, 10.0,
4640                                          0, 0, 0,
4641                                          sp_arctb_end_value_changed);
4642         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4643     }
4645     /* Segments / Pie checkbox */
4646     {
4647         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4649         GtkTreeIter iter;
4650         gtk_list_store_append( model, &iter );
4651         gtk_list_store_set( model, &iter,
4652                             0, _("Closed arc"),
4653                             1, _("Switch to segment (closed shape with two radii)"),
4654                             2, "circle_closed_arc",
4655                             -1 );
4657         gtk_list_store_append( model, &iter );
4658         gtk_list_store_set( model, &iter,
4659                             0, _("Open Arc"),
4660                             1, _("Switch to arc (unclosed shape)"),
4661                             2, "circle_open_arc",
4662                             -1 );
4664         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4665         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4666         g_object_set_data( holder, "open_action", act );
4668         ege_select_one_action_set_appearance( act, "full" );
4669         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4670         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4671         ege_select_one_action_set_icon_column( act, 2 );
4672         ege_select_one_action_set_icon_size( act, secondarySize );
4673         ege_select_one_action_set_tooltip_column( act, 1  );
4675         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4676         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4677         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4678         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4679     }
4681     /* Make Whole */
4682     {
4683         InkAction* inky = ink_action_new( "ArcResetAction",
4684                                           _("Make whole"),
4685                                           _("Make the shape a whole ellipse, not arc or segment"),
4686                                           "reset_circle",
4687                                           secondarySize );
4688         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4689         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4690         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4691         g_object_set_data( holder, "make_whole", inky );
4692     }
4694     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4695     // sensitivize make whole and open checkbox
4696     {
4697         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4698         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4699         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4700     }
4703     sigc::connection *connection = new sigc::connection(
4704         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4705         );
4706     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4707     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4713 // toggle button callbacks and updaters
4715 //########################
4716 //##      Dropper       ##
4717 //########################
4719 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4720     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4721     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4722     if ( set_action ) {
4723         if ( gtk_toggle_action_get_active( act ) ) {
4724             gtk_action_set_sensitive( set_action, TRUE );
4725         } else {
4726             gtk_action_set_sensitive( set_action, FALSE );
4727         }
4728     }
4730     spinbutton_defocus(GTK_OBJECT(tbl));
4733 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4734     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4735     spinbutton_defocus(GTK_OBJECT(tbl));
4739 /**
4740  * Dropper auxiliary toolbar construction and setup.
4741  *
4742  * TODO: Would like to add swatch of current color.
4743  * TODO: Add queue of last 5 or so colors selected with new swatches so that
4744  *       can drag and drop places. Will provide a nice mixing palette.
4745  */
4746 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4748     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4750     {
4751         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4752         ege_output_action_set_use_markup( act, TRUE );
4753         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4754     }
4756     {
4757         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4758                                                       _("Pick opacity"),
4759                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4760                                                       NULL,
4761                                                       Inkscape::ICON_SIZE_DECORATION );
4762         g_object_set( act, "short_label", _("Pick"), NULL );
4763         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4764         g_object_set_data( holder, "pick_action", act );
4765         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4766         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4767     }
4769     {
4770         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4771                                                       _("Assign opacity"),
4772                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4773                                                       NULL,
4774                                                       Inkscape::ICON_SIZE_DECORATION );
4775         g_object_set( act, "short_label", _("Assign"), NULL );
4776         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4777         g_object_set_data( holder, "set_action", act );
4778         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4779         // make sure it's disabled if we're not picking alpha
4780         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4781         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4782     }
4787 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4789     {
4790         /* Width */
4791         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4792         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4793         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
4794                                                               _("Pen Width"), _("Width:"),
4795                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
4796                                                               "tools.eraser", "width", 15,
4797                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
4798                                                               1, 100, 1.0, 0.0,
4799                                                               labels, values, G_N_ELEMENTS(labels),
4800                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
4801         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4802         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4803         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4804     }
4806     {
4807         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4809         GtkTreeIter iter;
4810         gtk_list_store_append( model, &iter );
4811         gtk_list_store_set( model, &iter,
4812                             0, _("Delete"),
4813                             1, _("Delete objects touched by the eraser"),
4814                             2, "delete_object",
4815                             -1 );
4817         gtk_list_store_append( model, &iter );
4818         gtk_list_store_set( model, &iter,
4819                             0, _("Cut"),
4820                             1, _("Cut out from objects"),
4821                             2, "difference",
4822                             -1 );
4824         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4825         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4826         g_object_set_data( holder, "eraser_mode_action", act );
4828         ege_select_one_action_set_appearance( act, "full" );
4829         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4830         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4831         ege_select_one_action_set_icon_column( act, 2 );
4832         ege_select_one_action_set_tooltip_column( act, 1  );
4834         gint eraserMode = (prefs_get_int_attribute("tools.eraser", "mode", 0) != 0) ? 1 : 0;
4835         ege_select_one_action_set_active( act, eraserMode );
4836         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
4837     }
4841 //########################
4842 //##    Text Toolbox    ##
4843 //########################
4844 /*
4845 static void
4846 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
4848     //Call back for letter sizing spinbutton
4851 static void
4852 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
4854     //Call back for line height spinbutton
4857 static void
4858 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4860     //Call back for horizontal kerning spinbutton
4863 static void
4864 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4866     //Call back for vertical kerning spinbutton
4869 static void
4870 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
4872     //Call back for letter rotation spinbutton
4873 }*/
4875 namespace {
4877 bool popdown_visible = false;
4878 bool popdown_hasfocus = false;
4880 void
4881 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
4883     SPStyle *query =
4884         sp_style_new (SP_ACTIVE_DOCUMENT);
4886 //    int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4888     int result_family =
4889         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4891     int result_style =
4892         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4894     int result_numbers =
4895         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4897     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4899     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4900     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
4901         // there are no texts in selection, read from prefs
4902  
4903         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
4904         if (repr) {
4905             sp_style_read_from_repr (query, repr);
4906             if (g_object_get_data(tbl, "text_style_from_prefs")) {
4907                 // do not reset the toolbar style from prefs if we already did it last time
4908                 sp_style_unref(query);
4909                 return;
4910             }
4911             g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
4912         } else {
4913             sp_style_unref(query);
4914             return;
4915         }
4916     } else {
4917         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
4918     }
4920     if (query->text)
4921     {
4922         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4923             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4924             gtk_entry_set_text (GTK_ENTRY (entry), "");
4926         } else if (query->text->font_specification.value || query->text->font_family.value) {
4928             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4930             // Get the font that corresponds
4931             Glib::ustring familyName;
4933             font_instance * font = font_factory::Default()->FaceFromStyle(query);
4934             if (font) {
4935                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4936                 font->Unref();
4937                 font = NULL;
4938             }
4940             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4942             Gtk::TreePath path;
4943             try {
4944                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4945             } catch (...) {
4946                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4947                 sp_style_unref(query);
4948                 return;
4949             }
4951             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4952             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4954             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4956             gtk_tree_selection_select_path (tselection, path.gobj());
4957             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4959             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4960         }
4962         //Size
4963         {
4964             GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
4965             gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
4966             g_object_set_data(tbl, "size-block", gpointer(1));
4967             gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
4968             g_object_set_data(tbl, "size-block", gpointer(0));
4969             g_free(str);
4970         }
4972         //Anchor
4973         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4974         {
4975             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4976             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4977             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4978             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4979         }
4980         else
4981         {
4982             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4983             {
4984                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4985                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4986                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4987                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4988             }
4989             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4990             {
4991                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4992                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4993                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4994                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4995             }
4996             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4997             {
4998                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4999                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5000                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5001                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5002             }
5003         }
5005         //Style
5006         {
5007             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
5009             gboolean active = gtk_toggle_button_get_active (button);
5010             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
5012             if (active != check)
5013             {
5014                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5015                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5016                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5017             }
5018         }
5020         {
5021             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
5023             gboolean active = gtk_toggle_button_get_active (button);
5024             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
5026             if (active != check)
5027             {
5028                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5029                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5030                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5031             }
5032         }
5034         //Orientation
5035         //locking both buttons, changing one affect all group (both)
5036         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
5037         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5039         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
5040         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
5042         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
5043         {
5044             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5045         }
5046         else
5047         {
5048             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
5049         }
5050         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5051         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
5052     }
5054     sp_style_unref(query);
5057 void
5058 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
5060     sp_text_toolbox_selection_changed (selection, tbl);
5063 void
5064 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
5066     sp_text_toolbox_selection_changed (NULL, tbl);
5069 void
5070 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
5071                                 GObject             *tbl)
5073     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
5074     GtkTreeModel *model = 0;
5075     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5076     GtkTreeIter   iter;
5077     char         *family = 0;
5079     gdk_pointer_ungrab (GDK_CURRENT_TIME);
5080     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5082     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
5083         return;
5084     }
5086     gtk_tree_model_get (model, &iter, 0, &family, -1);
5088     if (g_object_get_data (G_OBJECT (selection), "block"))
5089     {
5090         gtk_entry_set_text (GTK_ENTRY (entry), family);
5091         return;
5092     }
5094     gtk_entry_set_text (GTK_ENTRY (entry), family);
5096     SPStyle *query =
5097         sp_style_new (SP_ACTIVE_DOCUMENT);
5099     int result_fontspec =
5100         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5102     //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5104     SPCSSAttr *css = sp_repr_css_attr_new ();
5107     // First try to get the font spec from the stored value
5108     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
5110     if (fontSpec.empty()) {
5111         // Construct a new font specification if it does not yet exist
5112         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5113         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5114         fontFromStyle->Unref();
5115     }
5117     if (!fontSpec.empty()) {
5118         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
5119         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
5120             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
5121             if (font) {
5122                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5124                 // Set all the these just in case they were altered when finding the best
5125                 // match for the new family and old style...
5127                 gchar c[256];
5129                 font->Family(c, 256);
5130                 sp_repr_css_set_property (css, "font-family", c);
5132                 font->Attribute( "weight", c, 256);
5133                 sp_repr_css_set_property (css, "font-weight", c);
5135                 font->Attribute("style", c, 256);
5136                 sp_repr_css_set_property (css, "font-style", c);
5138                 font->Attribute("stretch", c, 256);
5139                 sp_repr_css_set_property (css, "font-stretch", c);
5141                 font->Attribute("variant", c, 256);
5142                 sp_repr_css_set_property (css, "font-variant", c);
5144                 font->Unref();
5145             }
5146         }
5147     }
5149     // If querying returned nothing, set the default style of the tool (for new texts)
5150     if (result_fontspec == QUERY_STYLE_NOTHING)
5151     {
5152         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5153         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
5154     }
5155     else
5156     {
5157         sp_desktop_set_style (desktop, css, true, true);
5158     }
5160     sp_style_unref(query);
5162     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5163                                    _("Text: Change font family"));
5164     sp_repr_css_attr_unref (css);
5165     g_free(family);
5166     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5168     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5171 void
5172 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
5173                                        GObject      *tbl)
5175     const char *family = gtk_entry_get_text (entry);
5177     try {
5178         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
5179         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5180         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5181         gtk_tree_selection_select_path (selection, path.gobj());
5182         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5183         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5184     } catch (...) {
5185         if (family && strlen (family))
5186         {
5187             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5188         }
5189     }
5192 void
5193 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
5194                                    gpointer          data)
5196     if (g_object_get_data (G_OBJECT (button), "block")) return;
5197     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
5198     int prop = GPOINTER_TO_INT(data);
5200     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5201     SPCSSAttr *css = sp_repr_css_attr_new ();
5203     switch (prop)
5204     {
5205         case 0:
5206         {
5207             sp_repr_css_set_property (css, "text-anchor", "start");
5208             sp_repr_css_set_property (css, "text-align", "start");
5209             break;
5210         }
5211         case 1:
5212         {
5213             sp_repr_css_set_property (css, "text-anchor", "middle");
5214             sp_repr_css_set_property (css, "text-align", "center");
5215             break;
5216         }
5218         case 2:
5219         {
5220             sp_repr_css_set_property (css, "text-anchor", "end");
5221             sp_repr_css_set_property (css, "text-align", "end");
5222             break;
5223         }
5225         case 3:
5226         {
5227             sp_repr_css_set_property (css, "text-anchor", "start");
5228             sp_repr_css_set_property (css, "text-align", "justify");
5229             break;
5230         }
5231     }
5233     SPStyle *query =
5234         sp_style_new (SP_ACTIVE_DOCUMENT);
5235     int result_numbers =
5236         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5238     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5239     if (result_numbers == QUERY_STYLE_NOTHING)
5240     {
5241         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5242     }
5244     sp_style_unref(query);
5246     sp_desktop_set_style (desktop, css, true, true);
5247     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5248                                    _("Text: Change alignment"));
5249     sp_repr_css_attr_unref (css);
5251     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5254 void
5255 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
5256                                gpointer          data)
5258     if (g_object_get_data (G_OBJECT (button), "block")) return;
5260     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5261     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5262     int          prop       = GPOINTER_TO_INT(data);
5263     bool         active     = gtk_toggle_button_get_active (button);
5265     SPStyle *query =
5266         sp_style_new (SP_ACTIVE_DOCUMENT);
5268     int result_fontspec =
5269         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5271     //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5272     //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5273     //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5275     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
5276     Glib::ustring newFontSpec = "";
5278     if (fontSpec.empty()) {
5279         // Construct a new font specification if it does not yet exist
5280         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5281         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5282         fontFromStyle->Unref();
5283     }
5285     switch (prop)
5286     {
5287         case 0:
5288         {
5289             if (!fontSpec.empty()) {
5290                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
5291             }
5292             if (fontSpec != newFontSpec) {
5293                 // Don't even set the bold if the font didn't exist on the system
5294                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
5295             }
5296             break;
5297         }
5299         case 1:
5300         {
5301             if (!fontSpec.empty()) {
5302                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
5303             }
5304             if (fontSpec != newFontSpec) {
5305                 // Don't even set the italic if the font didn't exist on the system
5306                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
5307             }
5308             break;
5309         }
5310     }
5312     if (!newFontSpec.empty()) {
5313         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5314     }
5316     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5317     if (result_fontspec == QUERY_STYLE_NOTHING)
5318     {
5319         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5320     }
5322     sp_style_unref(query);
5324     sp_desktop_set_style (desktop, css, true, true);
5325     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5326                                    _("Text: Change font style"));
5327     sp_repr_css_attr_unref (css);
5329     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5332 void
5333 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
5334                                      gpointer         data)
5336     if (g_object_get_data (G_OBJECT (button), "block")) {
5337         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5338         return;
5339     }
5341     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5342     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5343     int          prop       = GPOINTER_TO_INT(data);
5345     switch (prop)
5346     {
5347         case 0:
5348         {
5349             sp_repr_css_set_property (css, "writing-mode", "lr");
5350             break;
5351         }
5353         case 1:
5354         {
5355             sp_repr_css_set_property (css, "writing-mode", "tb");
5356             break;
5357         }
5358     }
5360     SPStyle *query =
5361         sp_style_new (SP_ACTIVE_DOCUMENT);
5362     int result_numbers =
5363         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5365     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5366     if (result_numbers == QUERY_STYLE_NOTHING)
5367     {
5368         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5369     }
5371     sp_desktop_set_style (desktop, css, true, true);
5372     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5373                                    _("Text: Change orientation"));
5374     sp_repr_css_attr_unref (css);
5376     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5379 gboolean
5380 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5382     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5383     if (!desktop) return FALSE;
5385     switch (get_group0_keyval (event)) {
5386         case GDK_Escape: // defocus
5387             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5388             sp_text_toolbox_selection_changed (NULL, tbl); // update
5389             return TRUE; // I consumed the event
5390             break;
5391     }
5392     return FALSE;
5395 gboolean
5396 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5398     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5399     if (!desktop) return FALSE;
5401     switch (get_group0_keyval (event)) {
5402         case GDK_KP_Enter:
5403         case GDK_Return:
5404         case GDK_Escape: // defocus
5405             gtk_widget_hide (w);
5406             popdown_visible = false;
5407             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5408             return TRUE; // I consumed the event
5409             break;
5410         case GDK_w:
5411         case GDK_W:
5412             if (event->state & GDK_CONTROL_MASK) {
5413                 gtk_widget_hide (w);
5414                 popdown_visible = false;
5415                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5416                 return TRUE; // I consumed the event
5417             }
5418             break;
5419     }
5420     return FALSE;
5424 void
5425 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
5426                                GObject     *tbl)
5428     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5430     if (g_object_get_data (tbl, "size-block")) return;
5432     // If this is not from selecting a size in the list (in which case get_active will give the
5433     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5434     // process this event. This fixes GTK's stupid insistence on sending an activate change every
5435     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5436     if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5437         return;
5439     gdouble value = -1;
5440     {
5441         gchar *endptr;
5442         gchar *const text = gtk_combo_box_get_active_text(cbox);
5443         if (text) {
5444             value = g_strtod(text, &endptr);
5445             if (endptr == text) {  // Conversion failed, non-numeric input.
5446                 value = -1;
5447             }
5448             g_free(text);
5449         }
5450     }
5451     if (value <= 0) {
5452         return; // could not parse value
5453     }
5455     SPCSSAttr *css = sp_repr_css_attr_new ();
5456     Inkscape::CSSOStringStream osfs;
5457     osfs << value;
5458     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5460     SPStyle *query =
5461         sp_style_new (SP_ACTIVE_DOCUMENT);
5462     int result_numbers =
5463         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5465     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5466     if (result_numbers == QUERY_STYLE_NOTHING)
5467     {
5468         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5469     }
5471     sp_style_unref(query);
5473     sp_desktop_set_style (desktop, css, true, true);
5474     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5475                                    _("Text: Change font size"));
5476     sp_repr_css_attr_unref (css);
5478     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5481 gboolean
5482 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5484     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5485     if (!desktop) return FALSE;
5487     if (!g_object_get_data (tbl, "esc-pressed")) {
5488         g_object_set_data (tbl, "enter-pressed", gpointer(1));
5489         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5490         sp_text_toolbox_size_changed (cbox, tbl);
5491         g_object_set_data (tbl, "enter-pressed", gpointer(0));
5492     }
5493     return FALSE; // I consumed the event
5497 gboolean
5498 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5500     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5501     if (!desktop) return FALSE;
5503     switch (get_group0_keyval (event)) {
5504         case GDK_Escape: // defocus
5505             g_object_set_data (tbl, "esc-pressed", gpointer(1));
5506             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5507             g_object_set_data (tbl, "esc-pressed", gpointer(0));
5508             return TRUE; // I consumed the event
5509             break;
5510         case GDK_Return: // defocus
5511         case GDK_KP_Enter:
5512             g_object_set_data (tbl, "enter-pressed", gpointer(1));
5513             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5514             sp_text_toolbox_size_changed (cbox, tbl);
5515             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5516             g_object_set_data (tbl, "enter-pressed", gpointer(0));
5517             return TRUE; // I consumed the event
5518             break;
5519     }
5520     return FALSE;
5523 void
5524 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
5525                                          GObject            *tbl)
5527     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
5528     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5529     int x, y;
5531     if (!popdown_visible)
5532     {
5533         gdk_window_get_origin (widget->window, &x, &y);
5534         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
5535         gtk_widget_show_all (popdown);
5536         //sp_transientize (popdown);
5538         gdk_pointer_grab (widget->window, TRUE,
5539                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
5540                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
5541                                         GDK_POINTER_MOTION_MASK),
5542                           NULL, NULL, GDK_CURRENT_TIME);
5544         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
5546         popdown_visible = true;
5547     }
5548     else
5549     {
5550         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5551         gdk_pointer_ungrab (GDK_CURRENT_TIME);
5552         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5553         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5554         gtk_widget_hide (popdown);
5555         popdown_visible = false;
5556     }
5559 gboolean
5560 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
5561                                  GdkEventFocus    */*event*/,
5562                                  GObject          */*tbl*/)
5564     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
5565     return FALSE;
5568 gboolean
5569 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
5570                                    GdkEventFocus    */*event*/,
5571                                    GObject          */*tbl*/)
5573     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5575     if (popdown_hasfocus) {
5576         gtk_widget_hide (popdown);
5577         popdown_hasfocus = false;
5578         popdown_visible = false;
5579         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5580         return TRUE;
5581     }
5582     return FALSE;
5585 gboolean
5586 sp_text_toolbox_popdown_focus_in (GtkWidget        */*popdown*/,
5587                                    GdkEventFocus    */*event*/,
5588                                    GObject          */*tbl*/)
5590     popdown_hasfocus = true;
5591     return TRUE;
5595 void
5596 cell_data_func  (GtkTreeViewColumn */*column*/,
5597                  GtkCellRenderer   *cell,
5598                  GtkTreeModel      *tree_model,
5599                  GtkTreeIter       *iter,
5600                  gpointer           /*data*/)
5602     gchar *family;
5603     gtk_tree_model_get(tree_model, iter, 0, &family, -1);
5604     gchar *const family_escaped = g_markup_escape_text(family, -1);
5606     static char const *const sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
5607     gchar *const sample_escaped = g_markup_escape_text(sample, -1);
5609     std::stringstream markup;
5610     markup << family_escaped << "  <span foreground='darkgray' font_family='"
5611            << family_escaped << "'>" << sample_escaped << "</span>";
5612     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
5614     g_free(family);
5615     g_free(family_escaped);
5616     g_free(sample_escaped);
5619 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
5620     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
5621     if (completion) {
5622         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
5623         g_object_unref (completion);
5624     }
5627 GtkWidget*
5628 sp_text_toolbox_new (SPDesktop *desktop)
5630     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
5631     GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
5633     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
5634     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
5636     GtkTooltips *tt = gtk_tooltips_new();
5637     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
5639     ////////////Family
5640     //Window
5641     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5642     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
5644     //Entry
5645     GtkWidget           *entry = gtk_entry_new ();
5646     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
5647     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
5648     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
5649     gtk_entry_completion_set_text_column (completion, 0);
5650     gtk_entry_completion_set_minimum_key_length (completion, 1);
5651     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
5652     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
5653     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
5654     gtk_toolbar_append_widget( tbl, entry, "", "" );
5655     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
5657     //Button
5658     GtkWidget   *button = gtk_button_new ();
5659     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
5660     gtk_toolbar_append_widget( tbl, button, "", "");
5662     //Popdown
5663     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
5664     GtkWidget           *treeview = gtk_tree_view_new ();
5666     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
5667     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
5668     gtk_tree_view_column_pack_start (column, cell, FALSE);
5669     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
5670     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5671     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5673     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5674     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5675     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5677     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5679     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5680     gtk_container_add (GTK_CONTAINER (sw), treeview);
5682     gtk_container_add (GTK_CONTAINER (window), sw);
5683     gtk_widget_set_size_request (window, 300, 450);
5685     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5686     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5687     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5689     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5691     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5692     g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5693     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5695     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5696     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5698     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5699     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5700     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5701     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5702     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5704     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
5705     GtkWidget *box = gtk_event_box_new ();
5706     gtk_container_add (GTK_CONTAINER (box), image);
5707     gtk_toolbar_append_widget( tbl, box, "", "");
5708     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5709     GtkTooltips *tooltips = gtk_tooltips_new ();
5710     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5711     gtk_widget_hide (GTK_WIDGET (box));
5712     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
5714     ////////////Size
5715     gchar const *const sizes[] = {
5716         "4", "6", "8", "9", "10", "11", "12", "13", "14",
5717         "16", "18", "20", "22", "24", "28",
5718         "32", "36", "40", "48", "56", "64", "72", "144"
5719     };
5721     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
5722     for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
5723         gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
5724     }
5725     gtk_widget_set_size_request (cbox, 80, -1);
5726     gtk_toolbar_append_widget( tbl, cbox, "", "");
5727     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
5728     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
5729     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
5730     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
5732     ////////////Text anchor
5733     GtkWidget *group   = gtk_radio_button_new (NULL);
5734     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
5735     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
5737     // left
5738     GtkWidget *rbutton = group;
5739     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5740     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
5741     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5743     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5744     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
5745     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
5746     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
5748     // center
5749     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5750     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5751     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
5752     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5754     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5755     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
5756     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5757     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5759     // right
5760     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5761     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5762     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
5763     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5765     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5766     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
5767     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5768     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5770     // fill
5771     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5772     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5773     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
5774     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5776     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5777     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
5778     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
5779     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
5781     gtk_toolbar_append_widget( tbl, row, "", "");
5783     //spacer
5784     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5786     ////////////Text style
5787     row = gtk_hbox_new (FALSE, 4);
5789     // bold
5790     rbutton = gtk_toggle_button_new ();
5791     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5792     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
5793     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5794     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
5796     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5797     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
5798     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
5800     // italic
5801     rbutton = gtk_toggle_button_new ();
5802     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5803     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
5804     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5805     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
5807     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5808     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
5809     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
5811     gtk_toolbar_append_widget( tbl, row, "", "");
5813     //spacer
5814     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5816     ////////////Text orientation
5817     group   = gtk_radio_button_new (NULL);
5818     row     = gtk_hbox_new (FALSE, 4);
5819     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
5821     // horizontal
5822     rbutton = group;
5823     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5824     gtk_container_add           (GTK_CONTAINER (rbutton),
5825                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
5826     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5827     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
5829     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5830     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
5831     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
5833     // vertical
5834     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5835     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5836     gtk_container_add           (GTK_CONTAINER (rbutton),
5837                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
5838     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5839     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
5841     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5842     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
5843     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
5844     gtk_toolbar_append_widget( tbl, row, "", "" );
5847     //watch selection
5848     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
5850     sigc::connection *c_selection_changed =
5851         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5852                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
5853     pool->add_connection ("selection-changed", c_selection_changed);
5855     sigc::connection *c_selection_modified =
5856         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5857                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
5858     pool->add_connection ("selection-modified", c_selection_modified);
5860     sigc::connection *c_subselection_changed =
5861         new sigc::connection (desktop->connectToolSubselectionChanged
5862                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
5863     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
5865     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
5868     gtk_widget_show_all( GTK_WIDGET(tbl) );
5870     return GTK_WIDGET(tbl);
5871 } // end of sp_text_toolbox_new()
5873 }//<unnamed> namespace
5876 //#########################
5877 //##      Connector      ##
5878 //#########################
5880 static void sp_connector_path_set_avoid(void)
5882     cc_selection_set_avoid(true);
5886 static void sp_connector_path_set_ignore(void)
5888     cc_selection_set_avoid(false);
5893 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
5895     // quit if run by the _changed callbacks
5896     if (g_object_get_data( tbl, "freeze" )) {
5897         return;
5898     }
5900     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5901     SPDocument *doc = sp_desktop_document(desktop);
5903     if (!sp_document_get_undo_sensitive(doc))
5904     {
5905         return;
5906     }
5908     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5910     if ( repr->attribute("inkscape:connector-spacing") ) {
5911         gdouble priorValue = gtk_adjustment_get_value(adj);
5912         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
5913         if ( priorValue == gtk_adjustment_get_value(adj) ) {
5914             return;
5915         }
5916     } else if ( adj->value == defaultConnSpacing ) {
5917         return;
5918     }
5920     // in turn, prevent callbacks from responding
5921     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5923     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
5924     SP_OBJECT(desktop->namedview)->updateRepr();
5926     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
5927     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
5928         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
5929         NR::Matrix m = NR::identity();
5930         avoid_item_move(&m, item);
5931     }
5933     if (items) {
5934         g_slist_free(items);
5935     }
5937     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
5938             _("Change connector spacing"));
5940     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5942     spinbutton_defocus(GTK_OBJECT(tbl));
5945 static void sp_connector_graph_layout(void)
5947     if (!SP_ACTIVE_DESKTOP) return;
5949     // hack for clones, see comment in align-and-distribute.cpp
5950     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5951     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5953     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5955     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5957     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5960 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5962     if ( gtk_toggle_action_get_active( act ) ) {
5963         prefs_set_string_attribute("tools.connector", "directedlayout",
5964                 "true");
5965     } else {
5966         prefs_set_string_attribute("tools.connector", "directedlayout",
5967                 "false");
5968     }
5971 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5973     if ( gtk_toggle_action_get_active( act ) ) {
5974         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5975                 "true");
5976     } else {
5977         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5978                 "false");
5979     }
5983 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5985     prefs_set_double_attribute("tools.connector", "length", adj->value);
5986     spinbutton_defocus(GTK_OBJECT(tbl));
5989 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5990                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5991                                             bool /*is_interactive*/, gpointer data)
5993     GtkWidget *tbl = GTK_WIDGET(data);
5995     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5996         return;
5997     }
5998     if (strcmp(name, "inkscape:connector-spacing") != 0) {
5999         return;
6000     }
6002     GtkAdjustment *adj = (GtkAdjustment*)
6003             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
6004     gdouble spacing = defaultConnSpacing;
6005     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
6007     gtk_adjustment_set_value(adj, spacing);
6011 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
6012     NULL, /* child_added */
6013     NULL, /* child_removed */
6014     connector_tb_event_attr_changed,
6015     NULL, /* content_changed */
6016     NULL  /* order_changed */
6017 };
6020 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
6022     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
6024     {
6025         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
6026                                           _("Avoid"),
6027                                           _("Make connectors avoid selected objects"),
6028                                           "connector_avoid",
6029                                           secondarySize );
6030         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
6031         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6032     }
6034     {
6035         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
6036                                           _("Ignore"),
6037                                           _("Make connectors ignore selected objects"),
6038                                           "connector_ignore",
6039                                           secondarySize );
6040         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
6041         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6042     }
6044     EgeAdjustmentAction* eact = 0;
6046     // Spacing spinbox
6047     eact = create_adjustment_action( "ConnectorSpacingAction",
6048                                      _("Connector Spacing"), _("Spacing:"),
6049                                      _("The amount of space left around objects by auto-routing connectors"),
6050                                      "tools.connector", "spacing", defaultConnSpacing,
6051                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
6052                                      0, 100, 1.0, 10.0,
6053                                      0, 0, 0,
6054                                      connector_spacing_changed, 1, 0 );
6055     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6057     // Graph (connector network) layout
6058     {
6059         InkAction* inky = ink_action_new( "ConnectorGraphAction",
6060                                           _("Graph"),
6061                                           _("Nicely arrange selected connector network"),
6062                                           "graph_layout",
6063                                           secondarySize );
6064         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
6065         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6066     }
6068     // Default connector length spinbox
6069     eact = create_adjustment_action( "ConnectorLengthAction",
6070                                      _("Connector Length"), _("Length:"),
6071                                      _("Ideal length for connectors when layout is applied"),
6072                                      "tools.connector", "length", 100,
6073                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
6074                                      10, 1000, 10.0, 100.0,
6075                                      0, 0, 0,
6076                                      connector_length_changed, 1, 0 );
6077     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6080     // Directed edges toggle button
6081     {
6082         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
6083                                                       _("Downwards"),
6084                                                       _("Make connectors with end-markers (arrows) point downwards"),
6085                                                       "directed_graph",
6086                                                       Inkscape::ICON_SIZE_DECORATION );
6087         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6089         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
6090         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6091                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6093         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
6094     }
6096     // Avoid overlaps toggle button
6097     {
6098         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
6099                                                       _("Remove overlaps"),
6100                                                       _("Do not allow overlapping shapes"),
6101                                                       "remove_overlaps",
6102                                                       Inkscape::ICON_SIZE_DECORATION );
6103         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6105         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
6106         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6107                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6109         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
6110     }
6112     // Code to watch for changes to the connector-spacing attribute in
6113     // the XML.
6114     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6115     g_assert(repr != NULL);
6117     purge_repr_listener( holder, holder );
6119     if (repr) {
6120         g_object_set_data( holder, "repr", repr );
6121         Inkscape::GC::anchor(repr);
6122         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
6123         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
6124     }
6125 } // end of sp_connector_toolbox_prep()
6128 //#########################
6129 //##     Paintbucket     ##
6130 //#########################
6132 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
6134     gint channels = ege_select_one_action_get_active( act );
6135     flood_channels_set_channels( channels );
6138 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
6140     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
6143 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
6145     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
6148 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
6150     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
6151     SPUnit const *unit = tracker->getActiveUnit();
6153     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
6155     prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
6158 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
6160     // FIXME: make defaults settable via Inkscape Options
6161     struct KeyValue {
6162         char const *key;
6163         double value;
6164     } const key_values[] = {
6165         {"threshold", 15},
6166         {"offset", 0.0}
6167     };
6169     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
6170         KeyValue const &kv = key_values[i];
6171         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
6172         if ( adj ) {
6173             gtk_adjustment_set_value(adj, kv.value);
6174         }
6175     }
6177     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
6178     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
6179     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
6180     ege_select_one_action_set_active( autogap_action, 0 );
6183 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6185     EgeAdjustmentAction* eact = 0;
6187     {
6188         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6190         GList* items = 0;
6191         gint count = 0;
6192         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
6193         {
6194             GtkTreeIter iter;
6195             gtk_list_store_append( model, &iter );
6196             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6197             count++;
6198         }
6199         g_list_free( items );
6200         items = 0;
6201         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
6202         g_object_set( act1, "short_label", _("Fill by:"), NULL );
6203         ege_select_one_action_set_appearance( act1, "compact" );
6204         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
6205         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
6206         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
6207         g_object_set_data( holder, "channels_action", act1 );
6208     }
6210     // Spacing spinbox
6211     {
6212         eact = create_adjustment_action(
6213             "ThresholdAction",
6214             _("Fill Threshold"), _("Threshold:"),
6215             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
6216             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
6217             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
6218             0, 0, 0,
6219             paintbucket_threshold_changed, 1, 0 );
6221         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6222         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6223     }
6225     // Create the units menu.
6226     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
6227     const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
6228     if (stored_unit)
6229         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
6230     g_object_set_data( holder, "tracker", tracker );
6231     {
6232         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
6233         gtk_action_group_add_action( mainActions, act );
6234     }
6236     // Offset spinbox
6237     {
6238         eact = create_adjustment_action(
6239             "OffsetAction",
6240             _("Grow/shrink by"), _("Grow/shrink by:"),
6241             _("The amount to grow (positive) or shrink (negative) the created fill path"),
6242             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
6243             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
6244             0, 0, 0,
6245             paintbucket_offset_changed, 1, 2);
6246         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
6248         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6249     }
6251     /* Auto Gap */
6252     {
6253         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6255         GList* items = 0;
6256         gint count = 0;
6257         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
6258         {
6259             GtkTreeIter iter;
6260             gtk_list_store_append( model, &iter );
6261             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6262             count++;
6263         }
6264         g_list_free( items );
6265         items = 0;
6266         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
6267         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
6268         ege_select_one_action_set_appearance( act2, "compact" );
6269         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
6270         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
6271         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
6272         g_object_set_data( holder, "autogap_action", act2 );
6273     }
6275     /* Reset */
6276     {
6277         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
6278                                           _("Defaults"),
6279                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
6280                                           GTK_STOCK_CLEAR );
6281         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
6282         gtk_action_group_add_action( mainActions, act );
6283         gtk_action_set_sensitive( act, TRUE );
6284     }
6288 /*
6289   Local Variables:
6290   mode:c++
6291   c-file-style:"stroustrup"
6292   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
6293   indent-tabs-mode:nil
6294   fill-column:99
6295   End:
6296 */
6297 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :