Code

make presets list autoscroll to match the values set
[inkscape.git] / src / widgets / toolbox.cpp
1 /** \file
2  * Controls bars for some of Inkscape's tools
3  * (for some tools, they are in their own files)
4  */
6 /*
7 *
8 * Authors:
9 *   MenTaLguY <mental@rydia.net>
10 *   Lauris Kaplinski <lauris@kaplinski.com>
11 *   bulia byak <buliabyak@users.sf.net>
12 *   Frank Felfe <innerspace@iname.com>
13 *   John Cliff <simarilius@yahoo.com>
14 *   David Turner <novalis@gnu.org>
15 *   Josh Andler <scislac@scislac.com>
16 *   Jon A. Cruz <jon@joncruz.org>
17 *
18 * Copyright (C) 2004 David Turner
19 * Copyright (C) 2003 MenTaLguY
20 * Copyright (C) 1999-2006 authors
21 * Copyright (C) 2001-2002 Ximian, Inc.
22 *
23 * Released under GNU GPL, read the file 'COPYING' for more information
24 */
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
30 #include <cstring>
31 #include <string>
33 #include <gtkmm.h>
34 #include <gtk/gtk.h>
35 #include <iostream>
36 #include <sstream>
38 #include "widgets/button.h"
39 #include "widgets/widget-sizes.h"
40 #include "widgets/spw-utilities.h"
41 #include "widgets/spinbutton-events.h"
42 #include "dialogs/text-edit.h"
43 #include "dialogs/dialog-events.h"
45 #include "ui/widget/style-swatch.h"
47 #include "prefs-utils.h"
48 #include "verbs.h"
49 #include "sp-namedview.h"
50 #include "desktop.h"
51 #include "desktop-handles.h"
52 #include "xml/repr.h"
53 #include "xml/node-event-vector.h"
54 #include "xml/attribute-record.h"
55 #include <glibmm/i18n.h>
56 #include "helper/unit-menu.h"
57 #include "helper/units.h"
58 #include "live_effects/effect.h"
60 #include "inkscape.h"
61 #include "conn-avoid-ref.h"
64 #include "select-toolbar.h"
65 #include "gradient-toolbar.h"
67 #include "connector-context.h"
68 #include "node-context.h"
69 #include "draw-context.h"
70 #include "shape-editor.h"
71 #include "tweak-context.h"
72 #include "sp-rect.h"
73 #include "box3d.h"
74 #include "box3d-context.h"
75 #include "sp-star.h"
76 #include "sp-spiral.h"
77 #include "sp-ellipse.h"
78 #include "sp-text.h"
79 #include "sp-flowtext.h"
80 #include "sp-clippath.h"
81 #include "sp-mask.h"
82 #include "style.h"
83 #include "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 static char const *
3272 freehand_tool_name(GObject *dataKludge)
3274     SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3275     return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3276              ? "tools.freehand.pen"
3277              : "tools.freehand.pencil" );
3280 static void sp_pc_spiro_spline_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3282     prefs_set_int_attribute(freehand_tool_name(tbl), "spiro-spline-mode", ege_select_one_action_get_active(act));
3285 static void sp_add_spiro_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3287     /* Freehand mode toggle buttons */
3288     {
3289         // FIXME: spiroMode seems not to be read correctly here during startup, although the
3290         //        correct mode is used in pen/pencil tool later on; same for freehand shapes
3291         guint spiroMode = prefs_get_int_attribute(freehand_tool_name(holder), "spiro-spline-mode", 0);
3292         Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3294         {
3295             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3297             GtkTreeIter iter;
3298             gtk_list_store_append( model, &iter );
3299             gtk_list_store_set( model, &iter,
3300                                 0, _("Bezier"),
3301                                 1, _("Create regular Bezier path"),
3302                                 2, "bezier_mode",
3303                                 -1 );
3305             gtk_list_store_append( model, &iter );
3306             gtk_list_store_set( model, &iter,
3307                                 0, _("Spiro"),
3308                                 1, _("Create Spiro path"),
3309                                 2, "spiro_splines_mode",
3310                                 -1 );
3312             EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3313                                                                 "FreehandModeActionPencil" :
3314                                                                 "FreehandModeActionPen",
3315                                                                 ("Mode:"), ("Mode"), NULL, GTK_TREE_MODEL(model) );
3316             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3318             ege_select_one_action_set_appearance( act, "full" );
3319             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3320             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3321             ege_select_one_action_set_icon_column( act, 2 );
3322             ege_select_one_action_set_icon_size( act, secondarySize );
3323             ege_select_one_action_set_tooltip_column( act, 1  );
3325             ege_select_one_action_set_active( act, spiroMode);
3326             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_pc_spiro_spline_mode_changed), holder);
3327         }
3328     }
3331 static void sp_freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3332     gint shape = ege_select_one_action_get_active( act );
3333     prefs_set_int_attribute(freehand_tool_name(dataKludge), "shape", shape);
3336 /**
3337  * \brief Generate the list of freehand advanced shape option entries.
3338  */
3339 GList * freehand_shape_dropdown_items_list() {
3340     GList *glist = NULL;
3342     glist = g_list_append (glist, _("None"));
3343     glist = g_list_append (glist, _("Triangle in"));
3344     glist = g_list_append (glist, _("Triangle out"));
3345     glist = g_list_append (glist, _("Ellipse"));
3346     glist = g_list_append (glist, _("From clipboard"));
3348     return glist;
3351 static void
3352 sp_freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3353     /*advanced shape options */
3354     {
3355         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3357         GList* items = 0;
3358         gint count = 0;
3359         for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3360         {
3361             GtkTreeIter iter;
3362             gtk_list_store_append( model, &iter );
3363             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3364             count++;
3365         }
3366         g_list_free( items );
3367         items = 0;
3368         EgeSelectOneAction* act1 = ege_select_one_action_new(
3369             tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3370             _("Shape:"), ("Shape"), NULL, GTK_TREE_MODEL(model));
3371         g_object_set( act1, "short_label", _("Shape:"), NULL );
3372         ege_select_one_action_set_appearance( act1, "compact" );
3373         ege_select_one_action_set_active( act1, prefs_get_int_attribute(freehand_tool_name(holder), "shape", 0) );
3374         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(sp_freehand_change_shape), holder );
3375         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3376         g_object_set_data( holder, "shape_action", act1 );
3377     }
3380 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3382     sp_add_spiro_toggle(mainActions, holder, false);
3383     sp_freehand_add_advanced_shape_options(mainActions, holder, false);
3387 static void
3388 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3390     GtkWidget *tbl = GTK_WIDGET(obj);
3392     GtkAdjustment *adj;
3394     // fixme: make settable
3395     gdouble tolerance = 4;
3397     adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3398     gtk_adjustment_set_value(adj, tolerance);
3399     gtk_adjustment_value_changed(adj);
3401     spinbutton_defocus(GTK_OBJECT(tbl));
3404 static void
3405 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3408     // quit if run by the attr_changed listener
3409     if (g_object_get_data( tbl, "freeze" )) {
3410         return;
3411     }
3412     // in turn, prevent listener from responding
3413     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3414     prefs_set_double_attribute("tools.freehand.pencil",
3415                                "tolerance", adj->value);
3416     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3422 static void
3423 sp_pencil_tb_tolerance_value_changed_external(Inkscape::XML::Node */*repr*/,
3424                                               const gchar */*key*/,
3425                                               const gchar */*oldval*/,
3426                                               const gchar */*newval*/,
3427                                               bool /*is_interactive*/,
3428                                               void * data)
3430     GObject* tbl = G_OBJECT(data);
3431     if (g_object_get_data( tbl, "freeze" )) {
3432         return;
3433     }
3435     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3437     GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl,
3438                                                             "tolerance");
3440     double v = prefs_get_double_attribute("tools.freehand.pencil",
3441                                             "tolerance", adj->value);
3442     gtk_adjustment_set_value(adj, v);
3443     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3447 static Inkscape::XML::NodeEventVector pencil_node_events =
3449     NULL,
3450     NULL,
3451     sp_pencil_tb_tolerance_value_changed_external,
3452     NULL,
3453     NULL,
3454 };
3457 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3459     sp_add_spiro_toggle(mainActions, holder, true);
3461     EgeAdjustmentAction* eact = 0;
3463     /* Tolerance */
3464     {
3465         gchar const* labels[] = {_("(many nodes, rough)"), ("(default)"), 0, 0, 0, 0, ("(few nodes, smooth)")};
3466         gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
3467         eact = create_adjustment_action( "PencilToleranceAction",
3468                                          _("Smoothing:"), _("Smoothing: "),
3469                  _("How much smoothing (simplifying) is applied to the line"),
3470                                          "tools.freehand.pencil", "tolerance",
3471                                          3.0,
3472                                          GTK_WIDGET(desktop->canvas), NULL,
3473                                          holder, TRUE, "altx-pencil",
3474                                          1, 100.0, 0.5, 0,
3475                                          labels, values, G_N_ELEMENTS(labels),
3476                                          sp_pencil_tb_tolerance_value_changed,
3477                                          1, 2);
3478         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3479         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3481         Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE,
3482                                                       "tools.freehand.pencil");
3483         repr->addListener(&pencil_node_events, G_OBJECT(holder));
3484         g_object_set_data(G_OBJECT(holder), "repr", repr);
3486     }
3488     /* advanced shape options */
3489     sp_freehand_add_advanced_shape_options(mainActions, holder, true);
3491     /* Reset */
3492     {
3493         InkAction* inky = ink_action_new( "PencilResetAction",
3494                                           _("Defaults"),
3495                                           _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3496                                           GTK_STOCK_CLEAR,
3497                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3498         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
3499         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3500     }
3502     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3507 //########################
3508 //##       Tweak        ##
3509 //########################
3511 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3513     prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3516 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3518     prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3521 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3523     prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3526 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3528     int mode = ege_select_one_action_get_active( act );
3529     prefs_set_int_attribute("tools.tweak", "mode", mode);
3531     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3532     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3533     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3534     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3535     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3536     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3537     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3538         if (doh) gtk_action_set_sensitive (doh, TRUE);
3539         if (dos) gtk_action_set_sensitive (dos, TRUE);
3540         if (dol) gtk_action_set_sensitive (dol, TRUE);
3541         if (doo) gtk_action_set_sensitive (doo, TRUE);
3542         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3543         if (fid) gtk_action_set_sensitive (fid, FALSE);
3544     } else {
3545         if (doh) gtk_action_set_sensitive (doh, FALSE);
3546         if (dos) gtk_action_set_sensitive (dos, FALSE);
3547         if (dol) gtk_action_set_sensitive (dol, FALSE);
3548         if (doo) gtk_action_set_sensitive (doo, FALSE);
3549         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3550         if (fid) gtk_action_set_sensitive (fid, TRUE);
3551     }
3554 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3556     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3559 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3560     bool show = gtk_toggle_action_get_active( act );
3561     prefs_set_int_attribute ("tools.tweak", "doh",  show ? 1 : 0);
3563 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3564     bool show = gtk_toggle_action_get_active( act );
3565     prefs_set_int_attribute ("tools.tweak", "dos",  show ? 1 : 0);
3567 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3568     bool show = gtk_toggle_action_get_active( act );
3569     prefs_set_int_attribute ("tools.tweak", "dol",  show ? 1 : 0);
3571 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3572     bool show = gtk_toggle_action_get_active( act );
3573     prefs_set_int_attribute ("tools.tweak", "doo",  show ? 1 : 0);
3576 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3578     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3580     {
3581         /* Width */
3582         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3583         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3584         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3585                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3586                                                               "tools.tweak", "width", 15,
3587                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3588                                                               1, 100, 1.0, 0.0,
3589                                                               labels, values, G_N_ELEMENTS(labels),
3590                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
3591         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3592         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3593         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3594     }
3597     {
3598         /* Force */
3599         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3600         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3601         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3602                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
3603                                                               "tools.tweak", "force", 20,
3604                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3605                                                               1, 100, 1.0, 0.0,
3606                                                               labels, values, G_N_ELEMENTS(labels),
3607                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
3608         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3609         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3610         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3611     }
3613     /* Mode */
3614     {
3615         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3617         GtkTreeIter iter;
3618         gtk_list_store_append( model, &iter );
3619         gtk_list_store_set( model, &iter,
3620                             0, _("Push mode"),
3621                             1, _("Push parts of paths in any direction"),
3622                             2, "tweak_push_mode",
3623                             -1 );
3625         gtk_list_store_append( model, &iter );
3626         gtk_list_store_set( model, &iter,
3627                             0, _("Shrink mode"),
3628                             1, _("Shrink (inset) parts of paths"),
3629                             2, "tweak_shrink_mode",
3630                             -1 );
3632         gtk_list_store_append( model, &iter );
3633         gtk_list_store_set( model, &iter,
3634                             0, _("Grow mode"),
3635                             1, _("Grow (outset) parts of paths"),
3636                             2, "tweak_grow_mode",
3637                             -1 );
3639         gtk_list_store_append( model, &iter );
3640         gtk_list_store_set( model, &iter,
3641                             0, _("Attract mode"),
3642                             1, _("Attract parts of paths towards cursor"),
3643                             2, "tweak_attract_mode",
3644                             -1 );
3646         gtk_list_store_append( model, &iter );
3647         gtk_list_store_set( model, &iter,
3648                             0, _("Repel mode"),
3649                             1, _("Repel parts of paths from cursor"),
3650                             2, "tweak_repel_mode",
3651                             -1 );
3653         gtk_list_store_append( model, &iter );
3654         gtk_list_store_set( model, &iter,
3655                             0, _("Roughen mode"),
3656                             1, _("Roughen parts of paths"),
3657                             2, "tweak_roughen_mode",
3658                             -1 );
3660         gtk_list_store_append( model, &iter );
3661         gtk_list_store_set( model, &iter,
3662                             0, _("Color paint mode"),
3663                             1, _("Paint the tool's color upon selected objects"),
3664                             2, "tweak_colorpaint_mode",
3665                             -1 );
3667         gtk_list_store_append( model, &iter );
3668         gtk_list_store_set( model, &iter,
3669                             0, _("Color jitter mode"),
3670                             1, _("Jitter the colors of selected objects"),
3671                             2, "tweak_colorjitter_mode",
3672                             -1 );
3674         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3675         g_object_set( act, "short_label", _("Mode:"), NULL );
3676         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3677         g_object_set_data( holder, "mode_action", act );
3679         ege_select_one_action_set_appearance( act, "full" );
3680         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3681         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3682         ege_select_one_action_set_icon_column( act, 2 );
3683         ege_select_one_action_set_icon_size( act, secondarySize );
3684         ege_select_one_action_set_tooltip_column( act, 1  );
3686         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3687         ege_select_one_action_set_active( act, mode );
3688         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3690         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3691     }
3693     guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3695     {
3696         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3697         ege_output_action_set_use_markup( act, TRUE );
3698         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3699         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3700             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3701         g_object_set_data( holder, "tweak_channels_label", act);
3702     }
3704     {
3705         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3706                                                       _("Hue"),
3707                                                       _("In color mode, act on objects' hue"),
3708                                                       NULL,
3709                                                       Inkscape::ICON_SIZE_DECORATION );
3710         //TRANSLATORS:  "H" here stands for hue
3711         g_object_set( act, "short_label", _("H"), NULL );
3712         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3713         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3714         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3715         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3716             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3717         g_object_set_data( holder, "tweak_doh", act);
3718     }
3719     {
3720         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3721                                                       _("Saturation"),
3722                                                       _("In color mode, act on objects' saturation"),
3723                                                       NULL,
3724                                                       Inkscape::ICON_SIZE_DECORATION );
3725         //TRANSLATORS: "S" here stands for Saturation
3726         g_object_set( act, "short_label", _("S"), NULL );
3727         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3728         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3729         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3730         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3731             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3732         g_object_set_data( holder, "tweak_dos", act );
3733     }
3734     {
3735         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3736                                                       _("Lightness"),
3737                                                       _("In color mode, act on objects' lightness"),
3738                                                       NULL,
3739                                                       Inkscape::ICON_SIZE_DECORATION );
3740         //TRANSLATORS: "L" here stands for Lightness
3741         g_object_set( act, "short_label", _("L"), NULL );
3742         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3743         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3744         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3745         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3746             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3747         g_object_set_data( holder, "tweak_dol", act );
3748     }
3749     {
3750         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3751                                                       _("Opacity"),
3752                                                       _("In color mode, act on objects' opacity"),
3753                                                       NULL,
3754                                                       Inkscape::ICON_SIZE_DECORATION );
3755         //TRANSLATORS: "O" here stands for Opacity
3756         g_object_set( act, "short_label", _("O"), NULL );
3757         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3758         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3759         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3760         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3761             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3762         g_object_set_data( holder, "tweak_doo", act );
3763     }
3765     {   /* Fidelity */
3766         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3767         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3768         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3769                                                               _("Fidelity"), _("Fidelity:"),
3770                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3771                                                               "tools.tweak", "fidelity", 50,
3772                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3773                                                               1, 100, 1.0, 10.0,
3774                                                               labels, values, G_N_ELEMENTS(labels),
3775                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
3776         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3777         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3778         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3779             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3780         g_object_set_data( holder, "tweak_fidelity", eact );
3781     }
3784     /* Use Pressure button */
3785     {
3786         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3787                                                       _("Pressure"),
3788                                                       _("Use the pressure of the input device to alter the force of tweak action"),
3789                                                       "use_pressure",
3790                                                       Inkscape::ICON_SIZE_DECORATION );
3791         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3792         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3793         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3794     }
3799 //########################
3800 //##     Calligraphy    ##
3801 //########################
3802 static void update_presets_list (GObject *tbl) 
3804     if (g_object_get_data(tbl, "presets_blocked")) 
3805         return;
3807     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3808     if (!sel) {
3809         ege_select_one_action_set_active(sel, 0);
3810         return;
3811     }
3813     int total_prefs = pref_path_number_of_children("tools.calligraphic.preset");
3815     for (int i = 1; i <= total_prefs; i++) {
3816         gchar *preset_path = get_pref_nth_child("tools.calligraphic.preset", i);
3817         Inkscape::XML::Node *preset_repr = inkscape_get_repr(INKSCAPE, preset_path);
3819         bool match = true;
3821         for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
3822               iter; 
3823               ++iter ) {
3824             const gchar *attr_name = g_quark_to_string(iter->key);
3825             if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
3826                 continue;
3827             void *widget = g_object_get_data(tbl, attr_name);
3828             if (widget) {
3829                 if (GTK_IS_ADJUSTMENT(widget)) {
3830                     double v = prefs_get_double_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
3831                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
3832                     //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
3833                     if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
3834                         match = false;
3835                         break;
3836                     }
3837                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
3838                     int v = prefs_get_int_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
3839                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
3840                     //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
3841                     if (gtk_toggle_action_get_active(toggle) != v) {
3842                         match = false;
3843                         break;
3844                     }
3845                 } 
3846             } 
3847         }
3849         if (match) {
3850             // newly added item is at the same index as the 
3851             // save command, so we need to change twice for it to take effect
3852             ege_select_one_action_set_active(sel, 0);
3853             ege_select_one_action_set_active(sel, i);
3854             return;
3855         }
3856     }
3858     // no match found
3859     ege_select_one_action_set_active(sel, 0);
3862 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3864     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value * 0.01 );
3865     update_presets_list(tbl);
3868 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3870     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value * 0.01 );
3871     update_presets_list(tbl);
3874 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3876     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3877     update_presets_list(tbl);
3880 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3882     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3883     update_presets_list(tbl);
3886 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3888     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value * 0.01 );
3889     update_presets_list(tbl);
3892 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3894     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value * 0.01);
3895     update_presets_list(tbl);
3898 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3900     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value * 0.01 );
3901     update_presets_list(tbl);
3904 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3906     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3907     update_presets_list(tbl);
3910 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
3912     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3913     update_presets_list(tbl);
3916 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
3918     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3919     update_presets_list(tbl);
3922 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
3924     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
3925     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3926     update_presets_list(tbl);
3927     if (calligraphy_angle )
3928         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3932 #define NUMBER_OF_PRESET_PARAMS 11
3933 static gchar * widget_names[NUMBER_OF_PRESET_PARAMS] = {
3934     "width",
3935     "mass",
3936     "wiggle",
3937     "angle",
3938     "thinning",
3939     "tremor",
3940     "flatness",
3941     "cap_rounding",
3942     "usepressure",
3943     "tracebackground",
3944     "usetilt"
3945 };
3948 static void sp_dcc_build_presets_list(GObject *tbl) 
3950     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
3952     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3953     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
3954     gtk_list_store_clear (model);
3956     {
3957         GtkTreeIter iter;
3958         gtk_list_store_append( model, &iter );
3959         gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
3960     }
3962     //TODO: switch back to prefs API
3963     Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
3964     Inkscape::XML::Node *child_repr = sp_repr_children(repr);
3965     int ii=1;
3966     while (child_repr) {
3967         GtkTreeIter iter;
3968         char *preset_name = (char *) child_repr->attribute("name");
3969         gtk_list_store_append( model, &iter );
3970         gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );
3971         child_repr = sp_repr_next(child_repr);
3972     }
3974     {
3975         GtkTreeIter iter;
3976         gtk_list_store_append( model, &iter );
3977         gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
3978         g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
3979     }
3981     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
3983     update_presets_list (tbl);
3986 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
3988     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
3989     if (! desktop) return;
3991     if (g_object_get_data(tbl, "presets_blocked")) 
3992         return;
3994     Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
3995     if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) {
3996         // dialog cancelled
3997         update_presets_list (tbl);
3998         return;
3999     }
4000     Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
4002     if (!profile_name.c_str() || *profile_name.c_str() == 0) {
4003         // empty name entered
4004         update_presets_list (tbl);
4005         return;
4006     }
4008     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4010     int new_index = -1;
4011     gchar *pref_path = NULL;
4012     int total_prefs = pref_path_number_of_children("tools.calligraphic.preset");
4014     for (int i = 1; i <= total_prefs; i++) {
4015         gchar *path = get_pref_nth_child("tools.calligraphic.preset", i);
4016         const gchar *name = prefs_get_string_attribute(path, "name");
4017         if (name && !strcmp(name, profile_name.c_str())) {
4018             // we already have preset with this name, replace its values
4019             new_index = i;
4020             pref_path = g_strdup(path);
4021             break;
4022         }
4023     }
4025     if (new_index == -1) {
4026         // no preset with this name, create 
4027         new_index = total_prefs + 1;
4028         gchar *profile_id = g_strdup_printf("dcc%d", new_index);
4029         pref_path = create_pref("tools.calligraphic.preset", profile_id);
4030         free(profile_id);
4031     }
4033     for (unsigned i = 0; i < NUMBER_OF_PRESET_PARAMS; ++i) {
4034         gchar *widget_name = widget_names[i];
4035         void *widget = g_object_get_data(tbl, widget_name);
4036         if (widget) {
4037             if (GTK_IS_ADJUSTMENT(widget)) {
4038                 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4039                 double v = gtk_adjustment_get_value(adj);
4040                 prefs_set_double_attribute(pref_path, widget_name, v);
4041                 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4042             } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4043                 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4044                 int v = gtk_toggle_action_get_active(toggle);
4045                 prefs_set_int_attribute(pref_path, widget_name, v);
4046                 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4047             } else {
4048                g_warning("Unknown widget type for preset: %s\n", widget_name);
4049             }
4050         } else {
4051             g_warning("Bad key when writing preset: %s\n", widget_name);
4052         }
4053     }
4054     prefs_set_string_attribute(pref_path, "name", profile_name.c_str());
4056     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4058     sp_dcc_build_presets_list (tbl);
4060     free (pref_path);
4064 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4066     gint preset_index = ege_select_one_action_get_active( act );
4067     gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4069     if (preset_index == save_presets_index) {
4070         // this is the Save command
4071         sp_dcc_save_profile(NULL, tbl);
4072         return;
4073     }
4075     if (g_object_get_data(tbl, "presets_blocked")) 
4076         return;
4078     gchar *preset_path = get_pref_nth_child("tools.calligraphic.preset", preset_index);
4080     if (preset_path) {
4081         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
4083         Inkscape::XML::Node *preset_repr = inkscape_get_repr(INKSCAPE, preset_path);
4085         for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
4086               iter; 
4087               ++iter ) {
4088             const gchar *attr_name = g_quark_to_string(iter->key);
4089             if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
4090                 continue;
4091             void *widget = g_object_get_data(tbl, attr_name);
4092             if (widget) {
4093                 if (GTK_IS_ADJUSTMENT(widget)) {
4094                     double v = prefs_get_double_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4095                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4096                     gtk_adjustment_set_value(adj, v);
4097                     //std::cout << "set adj " << attr_name << " to " << v << "\n";
4098                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4099                     int v = prefs_get_int_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4100                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4101                     gtk_toggle_action_set_active(toggle, v);
4102                     //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4103                 } else {
4104                     g_warning("Unknown widget type for preset: %s\n", attr_name);
4105                 }
4106             } else {
4107                 g_warning("Bad key found in a preset record: %s\n", attr_name);
4108             }
4109         }
4110         free(preset_path);
4111         g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4112     }
4117 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4119     {
4120         g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4122         EgeAdjustmentAction* calligraphy_angle = 0;
4124         {
4125         /* Width */
4126         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4127         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4128         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4129                                                               _("Pen Width"), _("Width:"),
4130                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
4131                                                               "tools.calligraphic", "width", 15,
4132                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4133                                                               1, 100, 1.0, 0.0,
4134                                                               labels, values, G_N_ELEMENTS(labels),
4135                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
4136         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4137         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4138         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4139         }
4141         {
4142         /* Thinning */
4143             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4144             gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4145         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4146                                                               _("Stroke Thinning"), _("Thinning:"),
4147                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4148                                                               "tools.calligraphic", "thinning", 10,
4149                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4150                                                               -100, 100, 1, 0.1,
4151                                                               labels, values, G_N_ELEMENTS(labels),
4152                                                               sp_ddc_velthin_value_changed, 0.01, 0, 100);
4153         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4154         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4155         }
4157         {
4158         /* Angle */
4159         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4160         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4161         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4162                                                               _("Pen Angle"), _("Angle:"),
4163                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4164                                                               "tools.calligraphic", "angle", 30,
4165                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4166                                                               -90.0, 90.0, 1.0, 10.0,
4167                                                               labels, values, G_N_ELEMENTS(labels),
4168                                                               sp_ddc_angle_value_changed, 1, 0 );
4169         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4170         g_object_set_data( holder, "angle_action", eact );
4171         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4172         calligraphy_angle = eact;
4173         }
4175         {
4176         /* Fixation */
4177             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4178         gdouble values[] = {0, 20, 40, 60, 90, 100};
4179         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4180                                                               _("Fixation"), _("Fixation:"),
4181                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
4182                                                               "tools.calligraphic", "flatness", 90,
4183                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4184                                                               0.0, 100, 1.0, 10.0,
4185                                                               labels, values, G_N_ELEMENTS(labels),
4186                                                               sp_ddc_flatness_value_changed, 0.01, 0, 100 );
4187         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4188         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4189         }
4191         {
4192         /* Cap Rounding */
4193             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4194         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4195         // TRANSLATORS: "cap" means "end" (both start and finish) here
4196         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4197                                                               _("Cap rounding"), _("Caps:"),
4198                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4199                                                               "tools.calligraphic", "cap_rounding", 0.0,
4200                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4201                                                               0.0, 5.0, 0.01, 0.1,
4202                                                               labels, values, G_N_ELEMENTS(labels),
4203                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4204         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4205         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4206         }
4208         {
4209         /* Tremor */
4210             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4211         gdouble values[] = {0, 10, 20, 40, 60, 100};
4212         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4213                                                               _("Stroke Tremor"), _("Tremor:"),
4214                                                               _("Increase to make strokes rugged and trembling"),
4215                                                               "tools.calligraphic", "tremor", 0.0,
4216                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4217                                                               0.0, 100, 1, 0.0,
4218                                                               labels, values, G_N_ELEMENTS(labels),
4219                                                               sp_ddc_tremor_value_changed, 0.01, 0, 100 );
4221         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4222         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4223         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4224         }
4226         {
4227         /* Wiggle */
4228         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4229         gdouble values[] = {0, 20, 40, 60, 100};
4230         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4231                                                               _("Pen Wiggle"), _("Wiggle:"),
4232                                                               _("Increase to make the pen waver and wiggle"),
4233                                                               "tools.calligraphic", "wiggle", 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_wiggle_value_changed, 0.01, 0, 100 );
4238         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4239         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4240         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4241         }
4243         {
4244         /* Mass */
4245             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4246         gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4247         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4248                                                               _("Pen Mass"), _("Mass:"),
4249                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
4250                                                               "tools.calligraphic", "mass", 2.0,
4251                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4252                                                               0.0, 100, 1, 0.0,
4253                                                               labels, values, G_N_ELEMENTS(labels),
4254                                                               sp_ddc_mass_value_changed, 0.01, 0, 100 );
4255         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4256         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4257         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4258         }
4261         /* Trace Background button */
4262         {
4263             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4264                                                           _("Trace Background"),
4265                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4266                                                           "trace_background",
4267                                                           Inkscape::ICON_SIZE_DECORATION );
4268             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4269             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4270             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
4271             g_object_set_data( holder, "tracebackground", act );
4272         }
4274         /* Use Pressure button */
4275         {
4276             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4277                                                           _("Pressure"),
4278                                                           _("Use the pressure of the input device to alter the width of the pen"),
4279                                                           "use_pressure",
4280                                                           Inkscape::ICON_SIZE_DECORATION );
4281             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4282             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4283             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
4284             g_object_set_data( holder, "usepressure", act );
4285         }
4287         /* Use Tilt button */
4288         {
4289             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4290                                                           _("Tilt"),
4291                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
4292                                                           "use_tilt",
4293                                                           Inkscape::ICON_SIZE_DECORATION );
4294             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4295             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4296             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4297             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4298             g_object_set_data( holder, "usetilt", act );
4299         }
4301         /*calligraphic profile */
4302         {
4303             GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
4304             EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Change calligraphic profile")), NULL, GTK_TREE_MODEL(model));
4305             ege_select_one_action_set_appearance (act1, "compact");
4306             g_object_set_data (holder, "profile_selector", act1 );
4308             g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
4310             sp_dcc_build_presets_list (holder);
4312             g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
4313             gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
4314         }
4315     }
4319 //########################
4320 //##    Circle / Arc    ##
4321 //########################
4323 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4325     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4326     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4328     if (v1 == 0 && v2 == 0) {
4329         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4330             gtk_action_set_sensitive( ocb, FALSE );
4331             gtk_action_set_sensitive( make_whole, FALSE );
4332         }
4333     } else {
4334         gtk_action_set_sensitive( ocb, TRUE );
4335         gtk_action_set_sensitive( make_whole, TRUE );
4336     }
4339 static void
4340 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4342     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4344     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4345         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
4346     }
4348     // quit if run by the attr_changed listener
4349     if (g_object_get_data( tbl, "freeze" )) {
4350         return;
4351     }
4353     // in turn, prevent listener from responding
4354     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4356     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4358     bool modmade = false;
4359     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4360          items != NULL;
4361          items = items->next)
4362     {
4363         SPItem *item = SP_ITEM(items->data);
4365         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4367             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4368             SPArc *arc = SP_ARC(item);
4370             if (!strcmp(value_name, "start"))
4371                 ge->start = (adj->value * M_PI)/ 180;
4372             else
4373                 ge->end = (adj->value * M_PI)/ 180;
4375             sp_genericellipse_normalize(ge);
4376             ((SPObject *)arc)->updateRepr();
4377             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4379             modmade = true;
4380         }
4381     }
4383     g_free(namespaced_name);
4385     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4387     sp_arctb_sensitivize( tbl, adj->value, other->value );
4389     if (modmade) {
4390         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4391                                    _("Arc: Change start/end"));
4392     }
4394     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4398 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
4400     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
4403 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4405     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
4409 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4411     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4412     gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
4413     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4414         prefs_set_int_attribute( "tools.eraser", "mode", eraserMode );
4415     }
4417     // only take action if run by the attr_changed listener
4418     if (!g_object_get_data( tbl, "freeze" )) {
4419         // in turn, prevent listener from responding
4420         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4422         if ( eraserMode != 0 ) {
4423         } else {
4424         }
4425         // TODO finish implementation
4427         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4428     }
4431 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4433     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4434     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4435         if ( ege_select_one_action_get_active( act ) != 0 ) {
4436             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
4437         } else {
4438             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
4439         }
4440     }
4442     // quit if run by the attr_changed listener
4443     if (g_object_get_data( tbl, "freeze" )) {
4444         return;
4445     }
4447     // in turn, prevent listener from responding
4448     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4450     bool modmade = false;
4452     if ( ege_select_one_action_get_active(act) != 0 ) {
4453         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4454              items != NULL;
4455              items = items->next)
4456         {
4457             if (SP_IS_ARC((SPItem *) items->data)) {
4458                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4459                 repr->setAttribute("sodipodi:open", "true");
4460                 SP_OBJECT((SPItem *) items->data)->updateRepr();
4461                 modmade = true;
4462             }
4463         }
4464     } else {
4465         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4466              items != NULL;
4467              items = items->next)
4468         {
4469             if (SP_IS_ARC((SPItem *) items->data))    {
4470                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4471                 repr->setAttribute("sodipodi:open", NULL);
4472                 SP_OBJECT((SPItem *) items->data)->updateRepr();
4473                 modmade = true;
4474             }
4475         }
4476     }
4478     if (modmade) {
4479         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4480                                    _("Arc: Change open/closed"));
4481     }
4483     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4486 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4488     GtkAdjustment *adj;
4489     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
4490     gtk_adjustment_set_value(adj, 0.0);
4491     gtk_adjustment_value_changed(adj);
4493     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
4494     gtk_adjustment_set_value(adj, 0.0);
4495     gtk_adjustment_value_changed(adj);
4497     spinbutton_defocus( GTK_OBJECT(obj) );
4500 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
4501                                       gchar const */*old_value*/, gchar const */*new_value*/,
4502                                       bool /*is_interactive*/, gpointer data)
4504     GObject *tbl = G_OBJECT(data);
4506     // quit if run by the _changed callbacks
4507     if (g_object_get_data( tbl, "freeze" )) {
4508         return;
4509     }
4511     // in turn, prevent callbacks from responding
4512     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4514     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4515     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4517     GtkAdjustment *adj1,*adj2;
4518     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4519     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4520     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4521     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4523     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4525     char const *openstr = NULL;
4526     openstr = repr->attribute("sodipodi:open");
4527     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4529     if (openstr) {
4530         ege_select_one_action_set_active( ocb, 1 );
4531     } else {
4532         ege_select_one_action_set_active( ocb, 0 );
4533     }
4535     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4538 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4539     NULL, /* child_added */
4540     NULL, /* child_removed */
4541     arc_tb_event_attr_changed,
4542     NULL, /* content_changed */
4543     NULL  /* order_changed */
4544 };
4547 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4549     int n_selected = 0;
4550     Inkscape::XML::Node *repr = NULL;
4552     purge_repr_listener( tbl, tbl );
4554     for (GSList const *items = selection->itemList();
4555          items != NULL;
4556          items = items->next)
4557     {
4558         if (SP_IS_ARC((SPItem *) items->data)) {
4559             n_selected++;
4560             repr = SP_OBJECT_REPR((SPItem *) items->data);
4561         }
4562     }
4564     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4566     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4567     if (n_selected == 0) {
4568         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4569     } else if (n_selected == 1) {
4570         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4571         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4573         if (repr) {
4574             g_object_set_data( tbl, "repr", repr );
4575             Inkscape::GC::anchor(repr);
4576             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4577             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4578         }
4579     } else {
4580         // FIXME: implement averaging of all parameters for multiple selected
4581         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4582         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4583         sp_arctb_sensitivize( tbl, 1, 0 );
4584     }
4588 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4590     EgeAdjustmentAction* eact = 0;
4591     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
4594     {
4595         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4596         ege_output_action_set_use_markup( act, TRUE );
4597         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4598         g_object_set_data( holder, "mode_action", act );
4599     }
4601     /* Start */
4602     {
4603         eact = create_adjustment_action( "ArcStartAction",
4604                                          _("Start"), _("Start:"),
4605                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
4606                                          "tools.shapes.arc", "start", 0.0,
4607                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4608                                          -360.0, 360.0, 1.0, 10.0,
4609                                          0, 0, 0,
4610                                          sp_arctb_start_value_changed);
4611         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4612     }
4614     /* End */
4615     {
4616         eact = create_adjustment_action( "ArcEndAction",
4617                                          _("End"), _("End:"),
4618                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
4619                                          "tools.shapes.arc", "end", 0.0,
4620                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4621                                          -360.0, 360.0, 1.0, 10.0,
4622                                          0, 0, 0,
4623                                          sp_arctb_end_value_changed);
4624         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4625     }
4627     /* Segments / Pie checkbox */
4628     {
4629         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4631         GtkTreeIter iter;
4632         gtk_list_store_append( model, &iter );
4633         gtk_list_store_set( model, &iter,
4634                             0, _("Closed arc"),
4635                             1, _("Switch to segment (closed shape with two radii)"),
4636                             2, "circle_closed_arc",
4637                             -1 );
4639         gtk_list_store_append( model, &iter );
4640         gtk_list_store_set( model, &iter,
4641                             0, _("Open Arc"),
4642                             1, _("Switch to arc (unclosed shape)"),
4643                             2, "circle_open_arc",
4644                             -1 );
4646         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4647         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4648         g_object_set_data( holder, "open_action", act );
4650         ege_select_one_action_set_appearance( act, "full" );
4651         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4652         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4653         ege_select_one_action_set_icon_column( act, 2 );
4654         ege_select_one_action_set_icon_size( act, secondarySize );
4655         ege_select_one_action_set_tooltip_column( act, 1  );
4657         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4658         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4659         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4660         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4661     }
4663     /* Make Whole */
4664     {
4665         InkAction* inky = ink_action_new( "ArcResetAction",
4666                                           _("Make whole"),
4667                                           _("Make the shape a whole ellipse, not arc or segment"),
4668                                           "reset_circle",
4669                                           secondarySize );
4670         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4671         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4672         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4673         g_object_set_data( holder, "make_whole", inky );
4674     }
4676     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4677     // sensitivize make whole and open checkbox
4678     {
4679         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4680         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4681         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4682     }
4685     sigc::connection *connection = new sigc::connection(
4686         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4687         );
4688     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4689     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4695 // toggle button callbacks and updaters
4697 //########################
4698 //##      Dropper       ##
4699 //########################
4701 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4702     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4703     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4704     if ( set_action ) {
4705         if ( gtk_toggle_action_get_active( act ) ) {
4706             gtk_action_set_sensitive( set_action, TRUE );
4707         } else {
4708             gtk_action_set_sensitive( set_action, FALSE );
4709         }
4710     }
4712     spinbutton_defocus(GTK_OBJECT(tbl));
4715 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4716     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4717     spinbutton_defocus(GTK_OBJECT(tbl));
4721 /**
4722  * Dropper auxiliary toolbar construction and setup.
4723  *
4724  * TODO: Would like to add swatch of current color.
4725  * TODO: Add queue of last 5 or so colors selected with new swatches so that
4726  *       can drag and drop places. Will provide a nice mixing palette.
4727  */
4728 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4730     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4732     {
4733         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4734         ege_output_action_set_use_markup( act, TRUE );
4735         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4736     }
4738     {
4739         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4740                                                       _("Pick opacity"),
4741                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4742                                                       NULL,
4743                                                       Inkscape::ICON_SIZE_DECORATION );
4744         g_object_set( act, "short_label", _("Pick"), NULL );
4745         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4746         g_object_set_data( holder, "pick_action", act );
4747         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4748         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4749     }
4751     {
4752         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4753                                                       _("Assign opacity"),
4754                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4755                                                       NULL,
4756                                                       Inkscape::ICON_SIZE_DECORATION );
4757         g_object_set( act, "short_label", _("Assign"), NULL );
4758         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4759         g_object_set_data( holder, "set_action", act );
4760         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4761         // make sure it's disabled if we're not picking alpha
4762         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4763         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4764     }
4769 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4771     {
4772         /* Width */
4773         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4774         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4775         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
4776                                                               _("Pen Width"), _("Width:"),
4777                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
4778                                                               "tools.eraser", "width", 15,
4779                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
4780                                                               1, 100, 1.0, 0.0,
4781                                                               labels, values, G_N_ELEMENTS(labels),
4782                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
4783         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4784         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4785         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4786     }
4788     {
4789         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4791         GtkTreeIter iter;
4792         gtk_list_store_append( model, &iter );
4793         gtk_list_store_set( model, &iter,
4794                             0, _("Delete"),
4795                             1, _("Delete objects touched by the eraser"),
4796                             2, "delete_object",
4797                             -1 );
4799         gtk_list_store_append( model, &iter );
4800         gtk_list_store_set( model, &iter,
4801                             0, _("Cut"),
4802                             1, _("Cut out from objects"),
4803                             2, "difference",
4804                             -1 );
4806         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4807         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4808         g_object_set_data( holder, "eraser_mode_action", act );
4810         ege_select_one_action_set_appearance( act, "full" );
4811         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4812         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4813         ege_select_one_action_set_icon_column( act, 2 );
4814         ege_select_one_action_set_tooltip_column( act, 1  );
4816         gint eraserMode = (prefs_get_int_attribute("tools.eraser", "mode", 0) != 0) ? 1 : 0;
4817         ege_select_one_action_set_active( act, eraserMode );
4818         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
4819     }
4823 //########################
4824 //##    Text Toolbox    ##
4825 //########################
4826 /*
4827 static void
4828 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
4830     //Call back for letter sizing spinbutton
4833 static void
4834 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
4836     //Call back for line height spinbutton
4839 static void
4840 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4842     //Call back for horizontal kerning spinbutton
4845 static void
4846 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4848     //Call back for vertical kerning spinbutton
4851 static void
4852 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
4854     //Call back for letter rotation spinbutton
4855 }*/
4857 namespace {
4859 bool popdown_visible = false;
4860 bool popdown_hasfocus = false;
4862 void
4863 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
4865     SPStyle *query =
4866         sp_style_new (SP_ACTIVE_DOCUMENT);
4868 //    int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4870     int result_family =
4871         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4873     int result_style =
4874         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4876     int result_numbers =
4877         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4879     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4881     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4882     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
4883         // there are no texts in selection, read from prefs
4884  
4885         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
4886         if (repr) {
4887             sp_style_read_from_repr (query, repr);
4888             if (g_object_get_data(tbl, "text_style_from_prefs")) {
4889                 // do not reset the toolbar style from prefs if we already did it last time
4890                 sp_style_unref(query);
4891                 return;
4892             }
4893             g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
4894         } else {
4895             sp_style_unref(query);
4896             return;
4897         }
4898     } else {
4899         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
4900     }
4902     if (query->text)
4903     {
4904         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4905             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4906             gtk_entry_set_text (GTK_ENTRY (entry), "");
4908         } else if (query->text->font_specification.value || query->text->font_family.value) {
4910             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4912             // Get the font that corresponds
4913             Glib::ustring familyName;
4915             font_instance * font = font_factory::Default()->FaceFromStyle(query);
4916             if (font) {
4917                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4918                 font->Unref();
4919                 font = NULL;
4920             }
4922             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4924             Gtk::TreePath path;
4925             try {
4926                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4927             } catch (...) {
4928                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4929                 sp_style_unref(query);
4930                 return;
4931             }
4933             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4934             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4936             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4938             gtk_tree_selection_select_path (tselection, path.gobj());
4939             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4941             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4942         }
4944         //Size
4945         GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4946         char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4947         g_object_set_data (tbl, "size-block", gpointer(1));
4948         gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4949         g_object_set_data (tbl, "size-block", gpointer(0));
4950         free (str);
4952         //Anchor
4953         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4954         {
4955             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4956             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4957             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4958             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4959         }
4960         else
4961         {
4962             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4963             {
4964                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4965                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4966                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4967                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4968             }
4969             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4970             {
4971                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4972                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4973                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4974                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4975             }
4976             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4977             {
4978                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4979                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4980                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4981                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4982             }
4983         }
4985         //Style
4986         {
4987             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4989             gboolean active = gtk_toggle_button_get_active (button);
4990             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4992             if (active != check)
4993             {
4994                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4995                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4996                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4997             }
4998         }
5000         {
5001             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
5003             gboolean active = gtk_toggle_button_get_active (button);
5004             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
5006             if (active != check)
5007             {
5008                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5009                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5010                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5011             }
5012         }
5014         //Orientation
5015         //locking both buttons, changing one affect all group (both)
5016         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
5017         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5019         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
5020         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
5022         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
5023         {
5024             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5025         }
5026         else
5027         {
5028             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
5029         }
5030         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5031         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
5032     }
5034     sp_style_unref(query);
5037 void
5038 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
5040     sp_text_toolbox_selection_changed (selection, tbl);
5043 void
5044 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
5046     sp_text_toolbox_selection_changed (NULL, tbl);
5049 void
5050 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
5051                                 GObject             *tbl)
5053     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
5054     GtkTreeModel *model = 0;
5055     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5056     GtkTreeIter   iter;
5057     char         *family = 0;
5059     gdk_pointer_ungrab (GDK_CURRENT_TIME);
5060     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5062     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
5063         return;
5064     }
5066     gtk_tree_model_get (model, &iter, 0, &family, -1);
5068     if (g_object_get_data (G_OBJECT (selection), "block"))
5069     {
5070         gtk_entry_set_text (GTK_ENTRY (entry), family);
5071         return;
5072     }
5074     gtk_entry_set_text (GTK_ENTRY (entry), family);
5076     SPStyle *query =
5077         sp_style_new (SP_ACTIVE_DOCUMENT);
5079     int result_fontspec =
5080         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5082     //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5084     SPCSSAttr *css = sp_repr_css_attr_new ();
5087     // First try to get the font spec from the stored value
5088     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
5090     if (fontSpec.empty()) {
5091         // Construct a new font specification if it does not yet exist
5092         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5093         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5094         fontFromStyle->Unref();
5095     }
5097     if (!fontSpec.empty()) {
5098         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
5099         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
5100             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
5101             if (font) {
5102                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5104                 // Set all the these just in case they were altered when finding the best
5105                 // match for the new family and old style...
5107                 gchar c[256];
5109                 font->Family(c, 256);
5110                 sp_repr_css_set_property (css, "font-family", c);
5112                 font->Attribute( "weight", c, 256);
5113                 sp_repr_css_set_property (css, "font-weight", c);
5115                 font->Attribute("style", c, 256);
5116                 sp_repr_css_set_property (css, "font-style", c);
5118                 font->Attribute("stretch", c, 256);
5119                 sp_repr_css_set_property (css, "font-stretch", c);
5121                 font->Attribute("variant", c, 256);
5122                 sp_repr_css_set_property (css, "font-variant", c);
5124                 font->Unref();
5125             }
5126         }
5127     }
5129     // If querying returned nothing, set the default style of the tool (for new texts)
5130     if (result_fontspec == QUERY_STYLE_NOTHING)
5131     {
5132         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5133         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
5134     }
5135     else
5136     {
5137         sp_desktop_set_style (desktop, css, true, true);
5138     }
5140     sp_style_unref(query);
5142     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5143                                    _("Text: Change font family"));
5144     sp_repr_css_attr_unref (css);
5145     free (family);
5146     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5148     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5151 void
5152 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
5153                                        GObject      *tbl)
5155     const char *family = gtk_entry_get_text (entry);
5157     try {
5158         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
5159         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5160         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5161         gtk_tree_selection_select_path (selection, path.gobj());
5162         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5163         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5164     } catch (...) {
5165         if (family && strlen (family))
5166         {
5167             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5168         }
5169     }
5172 void
5173 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
5174                                    gpointer          data)
5176     if (g_object_get_data (G_OBJECT (button), "block")) return;
5177     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
5178     int prop = GPOINTER_TO_INT(data);
5180     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5181     SPCSSAttr *css = sp_repr_css_attr_new ();
5183     switch (prop)
5184     {
5185         case 0:
5186         {
5187             sp_repr_css_set_property (css, "text-anchor", "start");
5188             sp_repr_css_set_property (css, "text-align", "start");
5189             break;
5190         }
5191         case 1:
5192         {
5193             sp_repr_css_set_property (css, "text-anchor", "middle");
5194             sp_repr_css_set_property (css, "text-align", "center");
5195             break;
5196         }
5198         case 2:
5199         {
5200             sp_repr_css_set_property (css, "text-anchor", "end");
5201             sp_repr_css_set_property (css, "text-align", "end");
5202             break;
5203         }
5205         case 3:
5206         {
5207             sp_repr_css_set_property (css, "text-anchor", "start");
5208             sp_repr_css_set_property (css, "text-align", "justify");
5209             break;
5210         }
5211     }
5213     SPStyle *query =
5214         sp_style_new (SP_ACTIVE_DOCUMENT);
5215     int result_numbers =
5216         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5218     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5219     if (result_numbers == QUERY_STYLE_NOTHING)
5220     {
5221         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5222     }
5224     sp_style_unref(query);
5226     sp_desktop_set_style (desktop, css, true, true);
5227     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5228                                    _("Text: Change alignment"));
5229     sp_repr_css_attr_unref (css);
5231     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5234 void
5235 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
5236                                gpointer          data)
5238     if (g_object_get_data (G_OBJECT (button), "block")) return;
5240     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5241     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5242     int          prop       = GPOINTER_TO_INT(data);
5243     bool         active     = gtk_toggle_button_get_active (button);
5245     SPStyle *query =
5246         sp_style_new (SP_ACTIVE_DOCUMENT);
5248     int result_fontspec =
5249         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5251     //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5252     //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5253     //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5255     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
5256     Glib::ustring newFontSpec = "";
5258     if (fontSpec.empty()) {
5259         // Construct a new font specification if it does not yet exist
5260         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5261         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5262         fontFromStyle->Unref();
5263     }
5265     switch (prop)
5266     {
5267         case 0:
5268         {
5269             if (!fontSpec.empty()) {
5270                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
5271             }
5272             if (fontSpec != newFontSpec) {
5273                 // Don't even set the bold if the font didn't exist on the system
5274                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
5275             }
5276             break;
5277         }
5279         case 1:
5280         {
5281             if (!fontSpec.empty()) {
5282                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
5283             }
5284             if (fontSpec != newFontSpec) {
5285                 // Don't even set the italic if the font didn't exist on the system
5286                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
5287             }
5288             break;
5289         }
5290     }
5292     if (!newFontSpec.empty()) {
5293         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5294     }
5296     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5297     if (result_fontspec == QUERY_STYLE_NOTHING)
5298     {
5299         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5300     }
5302     sp_style_unref(query);
5304     sp_desktop_set_style (desktop, css, true, true);
5305     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5306                                    _("Text: Change font style"));
5307     sp_repr_css_attr_unref (css);
5309     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5312 void
5313 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
5314                                      gpointer         data)
5316     if (g_object_get_data (G_OBJECT (button), "block")) {
5317         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5318         return;
5319     }
5321     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5322     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5323     int          prop       = GPOINTER_TO_INT(data);
5325     switch (prop)
5326     {
5327         case 0:
5328         {
5329             sp_repr_css_set_property (css, "writing-mode", "lr");
5330             break;
5331         }
5333         case 1:
5334         {
5335             sp_repr_css_set_property (css, "writing-mode", "tb");
5336             break;
5337         }
5338     }
5340     SPStyle *query =
5341         sp_style_new (SP_ACTIVE_DOCUMENT);
5342     int result_numbers =
5343         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5345     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5346     if (result_numbers == QUERY_STYLE_NOTHING)
5347     {
5348         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5349     }
5351     sp_desktop_set_style (desktop, css, true, true);
5352     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5353                                    _("Text: Change orientation"));
5354     sp_repr_css_attr_unref (css);
5356     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5359 gboolean
5360 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5362     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5363     if (!desktop) return FALSE;
5365     switch (get_group0_keyval (event)) {
5366         case GDK_Escape: // defocus
5367             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5368             sp_text_toolbox_selection_changed (NULL, tbl); // update
5369             return TRUE; // I consumed the event
5370             break;
5371     }
5372     return FALSE;
5375 gboolean
5376 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5378     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5379     if (!desktop) return FALSE;
5381     switch (get_group0_keyval (event)) {
5382         case GDK_KP_Enter:
5383         case GDK_Return:
5384         case GDK_Escape: // defocus
5385             gtk_widget_hide (w);
5386             popdown_visible = false;
5387             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5388             return TRUE; // I consumed the event
5389             break;
5390         case GDK_w:
5391         case GDK_W:
5392             if (event->state & GDK_CONTROL_MASK) {
5393                 gtk_widget_hide (w);
5394                 popdown_visible = false;
5395                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5396                 return TRUE; // I consumed the event
5397             }
5398             break;
5399     }
5400     return FALSE;
5404 void
5405 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
5406                                GObject     *tbl)
5408     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5410     if (g_object_get_data (tbl, "size-block")) return;
5412     // If this is not from selecting a size in the list (in which case get_active will give the
5413     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5414     // process this event. This fixes GTK's stupid insistence on sending an activate change every
5415     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5416     if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5417         return;
5419     gchar *endptr;
5420     gdouble value = -1;
5421     char *text = gtk_combo_box_get_active_text (cbox);
5422     if (text) {
5423         value = g_strtod (text, &endptr);
5424         if (endptr == text) // conversion failed, non-numeric input
5425             value = -1;
5426         free (text);
5427     }
5428     if (value <= 0) {
5429         return; // could not parse value
5430     }
5432     SPCSSAttr *css = sp_repr_css_attr_new ();
5433     Inkscape::CSSOStringStream osfs;
5434     osfs << value;
5435     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5437     SPStyle *query =
5438         sp_style_new (SP_ACTIVE_DOCUMENT);
5439     int result_numbers =
5440         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5442     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5443     if (result_numbers == QUERY_STYLE_NOTHING)
5444     {
5445         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5446     }
5448     sp_style_unref(query);
5450     sp_desktop_set_style (desktop, css, true, true);
5451     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5452                                    _("Text: Change font size"));
5453     sp_repr_css_attr_unref (css);
5455     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5458 gboolean
5459 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5461     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5462     if (!desktop) return FALSE;
5464     if (!g_object_get_data (tbl, "esc-pressed")) {
5465         g_object_set_data (tbl, "enter-pressed", gpointer(1));
5466         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5467         sp_text_toolbox_size_changed (cbox, tbl);
5468         g_object_set_data (tbl, "enter-pressed", gpointer(0));
5469     }
5470     return FALSE; // I consumed the event
5474 gboolean
5475 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5477     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5478     if (!desktop) return FALSE;
5480     switch (get_group0_keyval (event)) {
5481         case GDK_Escape: // defocus
5482             g_object_set_data (tbl, "esc-pressed", gpointer(1));
5483             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5484             g_object_set_data (tbl, "esc-pressed", gpointer(0));
5485             return TRUE; // I consumed the event
5486             break;
5487         case GDK_Return: // defocus
5488         case GDK_KP_Enter:
5489             g_object_set_data (tbl, "enter-pressed", gpointer(1));
5490             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5491             sp_text_toolbox_size_changed (cbox, tbl);
5492             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5493             g_object_set_data (tbl, "enter-pressed", gpointer(0));
5494             return TRUE; // I consumed the event
5495             break;
5496     }
5497     return FALSE;
5500 void
5501 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
5502                                          GObject            *tbl)
5504     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
5505     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5506     int x, y;
5508     if (!popdown_visible)
5509     {
5510         gdk_window_get_origin (widget->window, &x, &y);
5511         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
5512         gtk_widget_show_all (popdown);
5513         //sp_transientize (popdown);
5515         gdk_pointer_grab (widget->window, TRUE,
5516                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
5517                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
5518                                         GDK_POINTER_MOTION_MASK),
5519                           NULL, NULL, GDK_CURRENT_TIME);
5521         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
5523         popdown_visible = true;
5524     }
5525     else
5526     {
5527         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5528         gdk_pointer_ungrab (GDK_CURRENT_TIME);
5529         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5530         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5531         gtk_widget_hide (popdown);
5532         popdown_visible = false;
5533     }
5536 gboolean
5537 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
5538                                  GdkEventFocus    */*event*/,
5539                                  GObject          */*tbl*/)
5541     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
5542     return FALSE;
5545 gboolean
5546 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
5547                                    GdkEventFocus    */*event*/,
5548                                    GObject          */*tbl*/)
5550     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5552     if (popdown_hasfocus) {
5553         gtk_widget_hide (popdown);
5554         popdown_hasfocus = false;
5555         popdown_visible = false;
5556         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5557         return TRUE;
5558     }
5559     return FALSE;
5562 gboolean
5563 sp_text_toolbox_popdown_focus_in (GtkWidget        */*popdown*/,
5564                                    GdkEventFocus    */*event*/,
5565                                    GObject          */*tbl*/)
5567     popdown_hasfocus = true;
5568     return TRUE;
5572 void
5573 cell_data_func  (GtkTreeViewColumn */*column*/,
5574                  GtkCellRenderer   *cell,
5575                  GtkTreeModel      *tree_model,
5576                  GtkTreeIter       *iter,
5577                  gpointer           /*data*/)
5579     char        *family,
5580         *family_escaped,
5581         *sample_escaped;
5583     static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
5585     gtk_tree_model_get (tree_model, iter, 0, &family, -1);
5587     family_escaped = g_markup_escape_text (family, -1);
5588     sample_escaped = g_markup_escape_text (sample, -1);
5590     std::stringstream markup;
5591     markup << family_escaped << "  <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
5592     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
5594     free (family);
5595     free (family_escaped);
5596     free (sample_escaped);
5599 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
5600     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
5601     if (completion) {
5602         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
5603         g_object_unref (completion);
5604     }
5607 GtkWidget*
5608 sp_text_toolbox_new (SPDesktop *desktop)
5610     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
5611     GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
5613     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
5614     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
5616     GtkTooltips *tt = gtk_tooltips_new();
5617     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
5619     ////////////Family
5620     //Window
5621     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5622     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
5624     //Entry
5625     GtkWidget           *entry = gtk_entry_new ();
5626     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
5627     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
5628     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
5629     gtk_entry_completion_set_text_column (completion, 0);
5630     gtk_entry_completion_set_minimum_key_length (completion, 1);
5631     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
5632     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
5633     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
5634     gtk_toolbar_append_widget( tbl, entry, "", "" );
5635     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
5637     //Button
5638     GtkWidget   *button = gtk_button_new ();
5639     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
5640     gtk_toolbar_append_widget( tbl, button, "", "");
5642     //Popdown
5643     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
5644     GtkWidget           *treeview = gtk_tree_view_new ();
5646     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
5647     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
5648     gtk_tree_view_column_pack_start (column, cell, FALSE);
5649     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
5650     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5651     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5653     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5654     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5655     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5657     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5659     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5660     gtk_container_add (GTK_CONTAINER (sw), treeview);
5662     gtk_container_add (GTK_CONTAINER (window), sw);
5663     gtk_widget_set_size_request (window, 300, 450);
5665     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5666     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5667     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5669     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5671     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5672     g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5673     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5675     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5676     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5678     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5679     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5680     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5681     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5682     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5684     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
5685     GtkWidget *box = gtk_event_box_new ();
5686     gtk_container_add (GTK_CONTAINER (box), image);
5687     gtk_toolbar_append_widget( tbl, box, "", "");
5688     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5689     GtkTooltips *tooltips = gtk_tooltips_new ();
5690     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5691     gtk_widget_hide (GTK_WIDGET (box));
5692     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
5694     ////////////Size
5695     const char *sizes[] = {
5696         "4", "6", "8", "9", "10", "11", "12", "13", "14",
5697         "16", "18", "20", "22", "24", "28",
5698         "32", "36", "40", "48", "56", "64", "72", "144"
5699     };
5701     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
5702     for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
5703     gtk_widget_set_size_request (cbox, 80, -1);
5704     gtk_toolbar_append_widget( tbl, cbox, "", "");
5705     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
5706     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
5707     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
5708     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
5710     ////////////Text anchor
5711     GtkWidget *group   = gtk_radio_button_new (NULL);
5712     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
5713     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
5715     // left
5716     GtkWidget *rbutton = group;
5717     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5718     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
5719     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5721     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5722     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
5723     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
5724     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
5726     // center
5727     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5728     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5729     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
5730     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5732     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5733     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
5734     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5735     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5737     // right
5738     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (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_RIGHT, 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-end", rbutton);
5745     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5746     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5748     // fill
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_FILL, 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-fill", rbutton);
5756     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
5757     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
5759     gtk_toolbar_append_widget( tbl, row, "", "");
5761     //spacer
5762     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5764     ////////////Text style
5765     row = gtk_hbox_new (FALSE, 4);
5767     // bold
5768     rbutton = gtk_toggle_button_new ();
5769     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5770     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
5771     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5772     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
5774     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5775     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
5776     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
5778     // italic
5779     rbutton = gtk_toggle_button_new ();
5780     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5781     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
5782     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5783     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
5785     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5786     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
5787     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
5789     gtk_toolbar_append_widget( tbl, row, "", "");
5791     //spacer
5792     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5794     ////////////Text orientation
5795     group   = gtk_radio_button_new (NULL);
5796     row     = gtk_hbox_new (FALSE, 4);
5797     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
5799     // horizontal
5800     rbutton = group;
5801     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5802     gtk_container_add           (GTK_CONTAINER (rbutton),
5803                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
5804     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5805     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
5807     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5808     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
5809     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
5811     // vertical
5812     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5813     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5814     gtk_container_add           (GTK_CONTAINER (rbutton),
5815                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
5816     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5817     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
5819     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5820     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
5821     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
5822     gtk_toolbar_append_widget( tbl, row, "", "" );
5825     //watch selection
5826     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
5828     sigc::connection *c_selection_changed =
5829         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5830                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
5831     pool->add_connection ("selection-changed", c_selection_changed);
5833     sigc::connection *c_selection_modified =
5834         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5835                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
5836     pool->add_connection ("selection-modified", c_selection_modified);
5838     sigc::connection *c_subselection_changed =
5839         new sigc::connection (desktop->connectToolSubselectionChanged
5840                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
5841     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
5843     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
5846     gtk_widget_show_all( GTK_WIDGET(tbl) );
5848     return GTK_WIDGET(tbl);
5849 } // end of sp_text_toolbox_new()
5851 }//<unnamed> namespace
5854 //#########################
5855 //##      Connector      ##
5856 //#########################
5858 static void sp_connector_path_set_avoid(void)
5860     cc_selection_set_avoid(true);
5864 static void sp_connector_path_set_ignore(void)
5866     cc_selection_set_avoid(false);
5871 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
5873     // quit if run by the _changed callbacks
5874     if (g_object_get_data( tbl, "freeze" )) {
5875         return;
5876     }
5878     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5879     SPDocument *doc = sp_desktop_document(desktop);
5881     if (!sp_document_get_undo_sensitive(doc))
5882     {
5883         return;
5884     }
5886     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5888     if ( repr->attribute("inkscape:connector-spacing") ) {
5889         gdouble priorValue = gtk_adjustment_get_value(adj);
5890         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
5891         if ( priorValue == gtk_adjustment_get_value(adj) ) {
5892             return;
5893         }
5894     } else if ( adj->value == defaultConnSpacing ) {
5895         return;
5896     }
5898     // in turn, prevent callbacks from responding
5899     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5901     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
5902     SP_OBJECT(desktop->namedview)->updateRepr();
5904     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
5905     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
5906         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
5907         NR::Matrix m = NR::identity();
5908         avoid_item_move(&m, item);
5909     }
5911     if (items) {
5912         g_slist_free(items);
5913     }
5915     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
5916             _("Change connector spacing"));
5918     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5920     spinbutton_defocus(GTK_OBJECT(tbl));
5923 static void sp_connector_graph_layout(void)
5925     if (!SP_ACTIVE_DESKTOP) return;
5927     // hack for clones, see comment in align-and-distribute.cpp
5928     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5929     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5931     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5933     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5935     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5938 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5940     if ( gtk_toggle_action_get_active( act ) ) {
5941         prefs_set_string_attribute("tools.connector", "directedlayout",
5942                 "true");
5943     } else {
5944         prefs_set_string_attribute("tools.connector", "directedlayout",
5945                 "false");
5946     }
5949 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5951     if ( gtk_toggle_action_get_active( act ) ) {
5952         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5953                 "true");
5954     } else {
5955         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5956                 "false");
5957     }
5961 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5963     prefs_set_double_attribute("tools.connector", "length", adj->value);
5964     spinbutton_defocus(GTK_OBJECT(tbl));
5967 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5968                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5969                                             bool /*is_interactive*/, gpointer data)
5971     GtkWidget *tbl = GTK_WIDGET(data);
5973     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5974         return;
5975     }
5976     if (strcmp(name, "inkscape:connector-spacing") != 0) {
5977         return;
5978     }
5980     GtkAdjustment *adj = (GtkAdjustment*)
5981             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5982     gdouble spacing = defaultConnSpacing;
5983     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5985     gtk_adjustment_set_value(adj, spacing);
5989 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5990     NULL, /* child_added */
5991     NULL, /* child_removed */
5992     connector_tb_event_attr_changed,
5993     NULL, /* content_changed */
5994     NULL  /* order_changed */
5995 };
5998 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
6000     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
6002     {
6003         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
6004                                           _("Avoid"),
6005                                           _("Make connectors avoid selected objects"),
6006                                           "connector_avoid",
6007                                           secondarySize );
6008         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
6009         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6010     }
6012     {
6013         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
6014                                           _("Ignore"),
6015                                           _("Make connectors ignore selected objects"),
6016                                           "connector_ignore",
6017                                           secondarySize );
6018         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
6019         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6020     }
6022     EgeAdjustmentAction* eact = 0;
6024     // Spacing spinbox
6025     eact = create_adjustment_action( "ConnectorSpacingAction",
6026                                      _("Connector Spacing"), _("Spacing:"),
6027                                      _("The amount of space left around objects by auto-routing connectors"),
6028                                      "tools.connector", "spacing", defaultConnSpacing,
6029                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
6030                                      0, 100, 1.0, 10.0,
6031                                      0, 0, 0,
6032                                      connector_spacing_changed, 1, 0 );
6033     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6035     // Graph (connector network) layout
6036     {
6037         InkAction* inky = ink_action_new( "ConnectorGraphAction",
6038                                           _("Graph"),
6039                                           _("Nicely arrange selected connector network"),
6040                                           "graph_layout",
6041                                           secondarySize );
6042         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
6043         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6044     }
6046     // Default connector length spinbox
6047     eact = create_adjustment_action( "ConnectorLengthAction",
6048                                      _("Connector Length"), _("Length:"),
6049                                      _("Ideal length for connectors when layout is applied"),
6050                                      "tools.connector", "length", 100,
6051                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
6052                                      10, 1000, 10.0, 100.0,
6053                                      0, 0, 0,
6054                                      connector_length_changed, 1, 0 );
6055     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6058     // Directed edges toggle button
6059     {
6060         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
6061                                                       _("Downwards"),
6062                                                       _("Make connectors with end-markers (arrows) point downwards"),
6063                                                       "directed_graph",
6064                                                       Inkscape::ICON_SIZE_DECORATION );
6065         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6067         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
6068         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6069                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6071         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
6072     }
6074     // Avoid overlaps toggle button
6075     {
6076         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
6077                                                       _("Remove overlaps"),
6078                                                       _("Do not allow overlapping shapes"),
6079                                                       "remove_overlaps",
6080                                                       Inkscape::ICON_SIZE_DECORATION );
6081         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6083         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
6084         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6085                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6087         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
6088     }
6090     // Code to watch for changes to the connector-spacing attribute in
6091     // the XML.
6092     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6093     g_assert(repr != NULL);
6095     purge_repr_listener( holder, holder );
6097     if (repr) {
6098         g_object_set_data( holder, "repr", repr );
6099         Inkscape::GC::anchor(repr);
6100         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
6101         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
6102     }
6103 } // end of sp_connector_toolbox_prep()
6106 //#########################
6107 //##     Paintbucket     ##
6108 //#########################
6110 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
6112     gint channels = ege_select_one_action_get_active( act );
6113     flood_channels_set_channels( channels );
6116 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
6118     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
6121 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
6123     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
6126 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
6128     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
6129     SPUnit const *unit = tracker->getActiveUnit();
6131     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
6133     prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
6136 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
6138     // FIXME: make defaults settable via Inkscape Options
6139     struct KeyValue {
6140         char const *key;
6141         double value;
6142     } const key_values[] = {
6143         {"threshold", 15},
6144         {"offset", 0.0}
6145     };
6147     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
6148         KeyValue const &kv = key_values[i];
6149         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
6150         if ( adj ) {
6151             gtk_adjustment_set_value(adj, kv.value);
6152         }
6153     }
6155     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
6156     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
6157     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
6158     ege_select_one_action_set_active( autogap_action, 0 );
6161 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6163     EgeAdjustmentAction* eact = 0;
6165     {
6166         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6168         GList* items = 0;
6169         gint count = 0;
6170         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
6171         {
6172             GtkTreeIter iter;
6173             gtk_list_store_append( model, &iter );
6174             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6175             count++;
6176         }
6177         g_list_free( items );
6178         items = 0;
6179         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
6180         g_object_set( act1, "short_label", _("Fill by:"), NULL );
6181         ege_select_one_action_set_appearance( act1, "compact" );
6182         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
6183         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
6184         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
6185         g_object_set_data( holder, "channels_action", act1 );
6186     }
6188     // Spacing spinbox
6189     {
6190         eact = create_adjustment_action(
6191             "ThresholdAction",
6192             _("Fill Threshold"), _("Threshold:"),
6193             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
6194             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
6195             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
6196             0, 0, 0,
6197             paintbucket_threshold_changed, 1, 0 );
6199         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6200         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6201     }
6203     // Create the units menu.
6204     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
6205     const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
6206     if (stored_unit)
6207         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
6208     g_object_set_data( holder, "tracker", tracker );
6209     {
6210         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
6211         gtk_action_group_add_action( mainActions, act );
6212     }
6214     // Offset spinbox
6215     {
6216         eact = create_adjustment_action(
6217             "OffsetAction",
6218             _("Grow/shrink by"), _("Grow/shrink by:"),
6219             _("The amount to grow (positive) or shrink (negative) the created fill path"),
6220             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
6221             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
6222             0, 0, 0,
6223             paintbucket_offset_changed, 1, 2);
6224         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
6226         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6227     }
6229     /* Auto Gap */
6230     {
6231         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6233         GList* items = 0;
6234         gint count = 0;
6235         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
6236         {
6237             GtkTreeIter iter;
6238             gtk_list_store_append( model, &iter );
6239             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6240             count++;
6241         }
6242         g_list_free( items );
6243         items = 0;
6244         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
6245         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
6246         ege_select_one_action_set_appearance( act2, "compact" );
6247         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
6248         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
6249         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
6250         g_object_set_data( holder, "autogap_action", act2 );
6251     }
6253     /* Reset */
6254     {
6255         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
6256                                           _("Defaults"),
6257                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
6258                                           GTK_STOCK_CLEAR );
6259         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
6260         gtk_action_group_add_action( mainActions, act );
6261         gtk_action_set_sensitive( act, TRUE );
6262     }
6266 /*
6267   Local Variables:
6268   mode:c++
6269   c-file-style:"stroustrup"
6270   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
6271   indent-tabs-mode:nil
6272   fill-column:99
6273   End:
6274 */
6275 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :