Code

widgets/toolbox.cpp: s/free/g_free/ in a couple more places. Address some warnings.
[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 static gchar const *const widget_names[] = {
3933     "width",
3934     "mass",
3935     "wiggle",
3936     "angle",
3937     "thinning",
3938     "tremor",
3939     "flatness",
3940     "cap_rounding",
3941     "usepressure",
3942     "tracebackground",
3943     "usetilt"
3944 };
3947 static void sp_dcc_build_presets_list(GObject *tbl) 
3949     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
3951     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3952     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
3953     gtk_list_store_clear (model);
3955     {
3956         GtkTreeIter iter;
3957         gtk_list_store_append( model, &iter );
3958         gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
3959     }
3961     //TODO: switch back to prefs API
3962     Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
3963     Inkscape::XML::Node *child_repr = sp_repr_children(repr);
3964     int ii=1;
3965     while (child_repr) {
3966         GtkTreeIter iter;
3967         char *preset_name = (char *) child_repr->attribute("name");
3968         gtk_list_store_append( model, &iter );
3969         gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );
3970         child_repr = sp_repr_next(child_repr);
3971     }
3973     {
3974         GtkTreeIter iter;
3975         gtk_list_store_append( model, &iter );
3976         gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
3977         g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
3978     }
3980     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
3982     update_presets_list (tbl);
3985 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
3987     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
3988     if (! desktop) return;
3990     if (g_object_get_data(tbl, "presets_blocked")) 
3991         return;
3993     Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
3994     if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) {
3995         // dialog cancelled
3996         update_presets_list (tbl);
3997         return;
3998     }
3999     Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
4001     if (!profile_name.c_str() || *profile_name.c_str() == 0) {
4002         // empty name entered
4003         update_presets_list (tbl);
4004         return;
4005     }
4007     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4009     int new_index = -1;
4010     gchar *pref_path = NULL;
4011     int total_prefs = pref_path_number_of_children("tools.calligraphic.preset");
4013     for (int i = 1; i <= total_prefs; i++) {
4014         gchar *path = get_pref_nth_child("tools.calligraphic.preset", i);
4015         const gchar *name = prefs_get_string_attribute(path, "name");
4016         if (name && !strcmp(name, profile_name.c_str())) {
4017             // we already have preset with this name, replace its values
4018             new_index = i;
4019             pref_path = g_strdup(path);
4020             break;
4021         }
4022     }
4024     if (new_index == -1) {
4025         // no preset with this name, create 
4026         new_index = total_prefs + 1;
4027         gchar *profile_id = g_strdup_printf("dcc%d", new_index);
4028         pref_path = create_pref("tools.calligraphic.preset", profile_id);
4029         g_free(profile_id);
4030     }
4032     for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4033         gchar const *const widget_name = widget_names[i];
4034         void *widget = g_object_get_data(tbl, widget_name);
4035         if (widget) {
4036             if (GTK_IS_ADJUSTMENT(widget)) {
4037                 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4038                 double v = gtk_adjustment_get_value(adj);
4039                 prefs_set_double_attribute(pref_path, widget_name, v);
4040                 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4041             } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4042                 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4043                 int v = gtk_toggle_action_get_active(toggle);
4044                 prefs_set_int_attribute(pref_path, widget_name, v);
4045                 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4046             } else {
4047                g_warning("Unknown widget type for preset: %s\n", widget_name);
4048             }
4049         } else {
4050             g_warning("Bad key when writing preset: %s\n", widget_name);
4051         }
4052     }
4053     prefs_set_string_attribute(pref_path, "name", profile_name.c_str());
4055     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4057     sp_dcc_build_presets_list (tbl);
4059     g_free(pref_path);
4063 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4065     gint preset_index = ege_select_one_action_get_active( act );
4066     gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4068     if (preset_index == save_presets_index) {
4069         // this is the Save command
4070         sp_dcc_save_profile(NULL, tbl);
4071         return;
4072     }
4074     if (g_object_get_data(tbl, "presets_blocked")) 
4075         return;
4077     gchar *const preset_path = get_pref_nth_child("tools.calligraphic.preset", preset_index);
4079     if (preset_path) {
4080         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
4082         Inkscape::XML::Node *preset_repr = inkscape_get_repr(INKSCAPE, preset_path);
4084         for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
4085               iter; 
4086               ++iter ) {
4087             const gchar *attr_name = g_quark_to_string(iter->key);
4088             if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
4089                 continue;
4090             void *widget = g_object_get_data(tbl, attr_name);
4091             if (widget) {
4092                 if (GTK_IS_ADJUSTMENT(widget)) {
4093                     double v = prefs_get_double_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4094                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4095                     gtk_adjustment_set_value(adj, v);
4096                     //std::cout << "set adj " << attr_name << " to " << v << "\n";
4097                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4098                     int v = prefs_get_int_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4099                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4100                     gtk_toggle_action_set_active(toggle, v);
4101                     //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4102                 } else {
4103                     g_warning("Unknown widget type for preset: %s\n", attr_name);
4104                 }
4105             } else {
4106                 g_warning("Bad key found in a preset record: %s\n", attr_name);
4107             }
4108         }
4109         g_free(preset_path);
4110         g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4111     }
4116 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4118     {
4119         g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4121         EgeAdjustmentAction* calligraphy_angle = 0;
4123         {
4124         /* Width */
4125         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4126         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4127         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4128                                                               _("Pen Width"), _("Width:"),
4129                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
4130                                                               "tools.calligraphic", "width", 15,
4131                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4132                                                               1, 100, 1.0, 0.0,
4133                                                               labels, values, G_N_ELEMENTS(labels),
4134                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
4135         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4136         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4137         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4138         }
4140         {
4141         /* Thinning */
4142             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4143             gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4144         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4145                                                               _("Stroke Thinning"), _("Thinning:"),
4146                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4147                                                               "tools.calligraphic", "thinning", 10,
4148                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4149                                                               -100, 100, 1, 0.1,
4150                                                               labels, values, G_N_ELEMENTS(labels),
4151                                                               sp_ddc_velthin_value_changed, 0.01, 0, 100);
4152         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4153         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4154         }
4156         {
4157         /* Angle */
4158         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4159         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4160         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4161                                                               _("Pen Angle"), _("Angle:"),
4162                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4163                                                               "tools.calligraphic", "angle", 30,
4164                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4165                                                               -90.0, 90.0, 1.0, 10.0,
4166                                                               labels, values, G_N_ELEMENTS(labels),
4167                                                               sp_ddc_angle_value_changed, 1, 0 );
4168         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4169         g_object_set_data( holder, "angle_action", eact );
4170         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4171         calligraphy_angle = eact;
4172         }
4174         {
4175         /* Fixation */
4176             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4177         gdouble values[] = {0, 20, 40, 60, 90, 100};
4178         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4179                                                               _("Fixation"), _("Fixation:"),
4180                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
4181                                                               "tools.calligraphic", "flatness", 90,
4182                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4183                                                               0.0, 100, 1.0, 10.0,
4184                                                               labels, values, G_N_ELEMENTS(labels),
4185                                                               sp_ddc_flatness_value_changed, 0.01, 0, 100 );
4186         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4187         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4188         }
4190         {
4191         /* Cap Rounding */
4192             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4193         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4194         // TRANSLATORS: "cap" means "end" (both start and finish) here
4195         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4196                                                               _("Cap rounding"), _("Caps:"),
4197                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4198                                                               "tools.calligraphic", "cap_rounding", 0.0,
4199                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4200                                                               0.0, 5.0, 0.01, 0.1,
4201                                                               labels, values, G_N_ELEMENTS(labels),
4202                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4203         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4204         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4205         }
4207         {
4208         /* Tremor */
4209             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4210         gdouble values[] = {0, 10, 20, 40, 60, 100};
4211         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4212                                                               _("Stroke Tremor"), _("Tremor:"),
4213                                                               _("Increase to make strokes rugged and trembling"),
4214                                                               "tools.calligraphic", "tremor", 0.0,
4215                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4216                                                               0.0, 100, 1, 0.0,
4217                                                               labels, values, G_N_ELEMENTS(labels),
4218                                                               sp_ddc_tremor_value_changed, 0.01, 0, 100 );
4220         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4221         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4222         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4223         }
4225         {
4226         /* Wiggle */
4227         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4228         gdouble values[] = {0, 20, 40, 60, 100};
4229         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4230                                                               _("Pen Wiggle"), _("Wiggle:"),
4231                                                               _("Increase to make the pen waver and wiggle"),
4232                                                               "tools.calligraphic", "wiggle", 0.0,
4233                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4234                                                               0.0, 100, 1, 0.0,
4235                                                               labels, values, G_N_ELEMENTS(labels),
4236                                                               sp_ddc_wiggle_value_changed, 0.01, 0, 100 );
4237         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4238         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4239         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4240         }
4242         {
4243         /* Mass */
4244             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4245         gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4246         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4247                                                               _("Pen Mass"), _("Mass:"),
4248                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
4249                                                               "tools.calligraphic", "mass", 2.0,
4250                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4251                                                               0.0, 100, 1, 0.0,
4252                                                               labels, values, G_N_ELEMENTS(labels),
4253                                                               sp_ddc_mass_value_changed, 0.01, 0, 100 );
4254         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4255         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4256         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4257         }
4260         /* Trace Background button */
4261         {
4262             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4263                                                           _("Trace Background"),
4264                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4265                                                           "trace_background",
4266                                                           Inkscape::ICON_SIZE_DECORATION );
4267             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4268             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4269             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
4270             g_object_set_data( holder, "tracebackground", act );
4271         }
4273         /* Use Pressure button */
4274         {
4275             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4276                                                           _("Pressure"),
4277                                                           _("Use the pressure of the input device to alter the width of the pen"),
4278                                                           "use_pressure",
4279                                                           Inkscape::ICON_SIZE_DECORATION );
4280             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4281             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4282             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
4283             g_object_set_data( holder, "usepressure", act );
4284         }
4286         /* Use Tilt button */
4287         {
4288             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4289                                                           _("Tilt"),
4290                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
4291                                                           "use_tilt",
4292                                                           Inkscape::ICON_SIZE_DECORATION );
4293             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4294             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4295             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4296             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4297             g_object_set_data( holder, "usetilt", act );
4298         }
4300         /*calligraphic profile */
4301         {
4302             GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
4303             EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
4304             ege_select_one_action_set_appearance (act1, "compact");
4305             g_object_set_data (holder, "profile_selector", act1 );
4307             g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
4309             sp_dcc_build_presets_list (holder);
4311             g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
4312             gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
4313         }
4314     }
4318 //########################
4319 //##    Circle / Arc    ##
4320 //########################
4322 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4324     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4325     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4327     if (v1 == 0 && v2 == 0) {
4328         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4329             gtk_action_set_sensitive( ocb, FALSE );
4330             gtk_action_set_sensitive( make_whole, FALSE );
4331         }
4332     } else {
4333         gtk_action_set_sensitive( ocb, TRUE );
4334         gtk_action_set_sensitive( make_whole, TRUE );
4335     }
4338 static void
4339 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4341     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4343     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4344         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
4345     }
4347     // quit if run by the attr_changed listener
4348     if (g_object_get_data( tbl, "freeze" )) {
4349         return;
4350     }
4352     // in turn, prevent listener from responding
4353     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4355     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4357     bool modmade = false;
4358     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4359          items != NULL;
4360          items = items->next)
4361     {
4362         SPItem *item = SP_ITEM(items->data);
4364         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4366             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4367             SPArc *arc = SP_ARC(item);
4369             if (!strcmp(value_name, "start"))
4370                 ge->start = (adj->value * M_PI)/ 180;
4371             else
4372                 ge->end = (adj->value * M_PI)/ 180;
4374             sp_genericellipse_normalize(ge);
4375             ((SPObject *)arc)->updateRepr();
4376             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4378             modmade = true;
4379         }
4380     }
4382     g_free(namespaced_name);
4384     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4386     sp_arctb_sensitivize( tbl, adj->value, other->value );
4388     if (modmade) {
4389         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4390                                    _("Arc: Change start/end"));
4391     }
4393     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4397 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
4399     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
4402 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4404     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
4408 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4410     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4411     gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
4412     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4413         prefs_set_int_attribute( "tools.eraser", "mode", eraserMode );
4414     }
4416     // only take action if run by the attr_changed listener
4417     if (!g_object_get_data( tbl, "freeze" )) {
4418         // in turn, prevent listener from responding
4419         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4421         if ( eraserMode != 0 ) {
4422         } else {
4423         }
4424         // TODO finish implementation
4426         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4427     }
4430 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4432     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4433     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4434         if ( ege_select_one_action_get_active( act ) != 0 ) {
4435             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
4436         } else {
4437             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
4438         }
4439     }
4441     // quit if run by the attr_changed listener
4442     if (g_object_get_data( tbl, "freeze" )) {
4443         return;
4444     }
4446     // in turn, prevent listener from responding
4447     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4449     bool modmade = false;
4451     if ( ege_select_one_action_get_active(act) != 0 ) {
4452         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4453              items != NULL;
4454              items = items->next)
4455         {
4456             if (SP_IS_ARC((SPItem *) items->data)) {
4457                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4458                 repr->setAttribute("sodipodi:open", "true");
4459                 SP_OBJECT((SPItem *) items->data)->updateRepr();
4460                 modmade = true;
4461             }
4462         }
4463     } else {
4464         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4465              items != NULL;
4466              items = items->next)
4467         {
4468             if (SP_IS_ARC((SPItem *) items->data))    {
4469                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4470                 repr->setAttribute("sodipodi:open", NULL);
4471                 SP_OBJECT((SPItem *) items->data)->updateRepr();
4472                 modmade = true;
4473             }
4474         }
4475     }
4477     if (modmade) {
4478         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4479                                    _("Arc: Change open/closed"));
4480     }
4482     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4485 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4487     GtkAdjustment *adj;
4488     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
4489     gtk_adjustment_set_value(adj, 0.0);
4490     gtk_adjustment_value_changed(adj);
4492     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
4493     gtk_adjustment_set_value(adj, 0.0);
4494     gtk_adjustment_value_changed(adj);
4496     spinbutton_defocus( GTK_OBJECT(obj) );
4499 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
4500                                       gchar const */*old_value*/, gchar const */*new_value*/,
4501                                       bool /*is_interactive*/, gpointer data)
4503     GObject *tbl = G_OBJECT(data);
4505     // quit if run by the _changed callbacks
4506     if (g_object_get_data( tbl, "freeze" )) {
4507         return;
4508     }
4510     // in turn, prevent callbacks from responding
4511     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4513     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4514     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4516     GtkAdjustment *adj1,*adj2;
4517     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4518     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4519     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4520     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4522     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4524     char const *openstr = NULL;
4525     openstr = repr->attribute("sodipodi:open");
4526     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4528     if (openstr) {
4529         ege_select_one_action_set_active( ocb, 1 );
4530     } else {
4531         ege_select_one_action_set_active( ocb, 0 );
4532     }
4534     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4537 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4538     NULL, /* child_added */
4539     NULL, /* child_removed */
4540     arc_tb_event_attr_changed,
4541     NULL, /* content_changed */
4542     NULL  /* order_changed */
4543 };
4546 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4548     int n_selected = 0;
4549     Inkscape::XML::Node *repr = NULL;
4551     purge_repr_listener( tbl, tbl );
4553     for (GSList const *items = selection->itemList();
4554          items != NULL;
4555          items = items->next)
4556     {
4557         if (SP_IS_ARC((SPItem *) items->data)) {
4558             n_selected++;
4559             repr = SP_OBJECT_REPR((SPItem *) items->data);
4560         }
4561     }
4563     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4565     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4566     if (n_selected == 0) {
4567         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4568     } else if (n_selected == 1) {
4569         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4570         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4572         if (repr) {
4573             g_object_set_data( tbl, "repr", repr );
4574             Inkscape::GC::anchor(repr);
4575             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4576             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4577         }
4578     } else {
4579         // FIXME: implement averaging of all parameters for multiple selected
4580         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4581         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4582         sp_arctb_sensitivize( tbl, 1, 0 );
4583     }
4587 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4589     EgeAdjustmentAction* eact = 0;
4590     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
4593     {
4594         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4595         ege_output_action_set_use_markup( act, TRUE );
4596         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4597         g_object_set_data( holder, "mode_action", act );
4598     }
4600     /* Start */
4601     {
4602         eact = create_adjustment_action( "ArcStartAction",
4603                                          _("Start"), _("Start:"),
4604                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
4605                                          "tools.shapes.arc", "start", 0.0,
4606                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4607                                          -360.0, 360.0, 1.0, 10.0,
4608                                          0, 0, 0,
4609                                          sp_arctb_start_value_changed);
4610         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4611     }
4613     /* End */
4614     {
4615         eact = create_adjustment_action( "ArcEndAction",
4616                                          _("End"), _("End:"),
4617                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
4618                                          "tools.shapes.arc", "end", 0.0,
4619                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4620                                          -360.0, 360.0, 1.0, 10.0,
4621                                          0, 0, 0,
4622                                          sp_arctb_end_value_changed);
4623         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4624     }
4626     /* Segments / Pie checkbox */
4627     {
4628         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4630         GtkTreeIter iter;
4631         gtk_list_store_append( model, &iter );
4632         gtk_list_store_set( model, &iter,
4633                             0, _("Closed arc"),
4634                             1, _("Switch to segment (closed shape with two radii)"),
4635                             2, "circle_closed_arc",
4636                             -1 );
4638         gtk_list_store_append( model, &iter );
4639         gtk_list_store_set( model, &iter,
4640                             0, _("Open Arc"),
4641                             1, _("Switch to arc (unclosed shape)"),
4642                             2, "circle_open_arc",
4643                             -1 );
4645         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4646         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4647         g_object_set_data( holder, "open_action", act );
4649         ege_select_one_action_set_appearance( act, "full" );
4650         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4651         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4652         ege_select_one_action_set_icon_column( act, 2 );
4653         ege_select_one_action_set_icon_size( act, secondarySize );
4654         ege_select_one_action_set_tooltip_column( act, 1  );
4656         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4657         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4658         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4659         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4660     }
4662     /* Make Whole */
4663     {
4664         InkAction* inky = ink_action_new( "ArcResetAction",
4665                                           _("Make whole"),
4666                                           _("Make the shape a whole ellipse, not arc or segment"),
4667                                           "reset_circle",
4668                                           secondarySize );
4669         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4670         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4671         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4672         g_object_set_data( holder, "make_whole", inky );
4673     }
4675     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4676     // sensitivize make whole and open checkbox
4677     {
4678         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4679         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4680         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4681     }
4684     sigc::connection *connection = new sigc::connection(
4685         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4686         );
4687     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4688     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4694 // toggle button callbacks and updaters
4696 //########################
4697 //##      Dropper       ##
4698 //########################
4700 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4701     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4702     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4703     if ( set_action ) {
4704         if ( gtk_toggle_action_get_active( act ) ) {
4705             gtk_action_set_sensitive( set_action, TRUE );
4706         } else {
4707             gtk_action_set_sensitive( set_action, FALSE );
4708         }
4709     }
4711     spinbutton_defocus(GTK_OBJECT(tbl));
4714 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4715     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4716     spinbutton_defocus(GTK_OBJECT(tbl));
4720 /**
4721  * Dropper auxiliary toolbar construction and setup.
4722  *
4723  * TODO: Would like to add swatch of current color.
4724  * TODO: Add queue of last 5 or so colors selected with new swatches so that
4725  *       can drag and drop places. Will provide a nice mixing palette.
4726  */
4727 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4729     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4731     {
4732         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4733         ege_output_action_set_use_markup( act, TRUE );
4734         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4735     }
4737     {
4738         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4739                                                       _("Pick opacity"),
4740                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4741                                                       NULL,
4742                                                       Inkscape::ICON_SIZE_DECORATION );
4743         g_object_set( act, "short_label", _("Pick"), NULL );
4744         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4745         g_object_set_data( holder, "pick_action", act );
4746         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4747         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4748     }
4750     {
4751         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4752                                                       _("Assign opacity"),
4753                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4754                                                       NULL,
4755                                                       Inkscape::ICON_SIZE_DECORATION );
4756         g_object_set( act, "short_label", _("Assign"), NULL );
4757         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4758         g_object_set_data( holder, "set_action", act );
4759         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4760         // make sure it's disabled if we're not picking alpha
4761         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4762         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4763     }
4768 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4770     {
4771         /* Width */
4772         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4773         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4774         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
4775                                                               _("Pen Width"), _("Width:"),
4776                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
4777                                                               "tools.eraser", "width", 15,
4778                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
4779                                                               1, 100, 1.0, 0.0,
4780                                                               labels, values, G_N_ELEMENTS(labels),
4781                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
4782         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4783         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4784         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4785     }
4787     {
4788         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4790         GtkTreeIter iter;
4791         gtk_list_store_append( model, &iter );
4792         gtk_list_store_set( model, &iter,
4793                             0, _("Delete"),
4794                             1, _("Delete objects touched by the eraser"),
4795                             2, "delete_object",
4796                             -1 );
4798         gtk_list_store_append( model, &iter );
4799         gtk_list_store_set( model, &iter,
4800                             0, _("Cut"),
4801                             1, _("Cut out from objects"),
4802                             2, "difference",
4803                             -1 );
4805         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4806         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4807         g_object_set_data( holder, "eraser_mode_action", act );
4809         ege_select_one_action_set_appearance( act, "full" );
4810         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4811         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4812         ege_select_one_action_set_icon_column( act, 2 );
4813         ege_select_one_action_set_tooltip_column( act, 1  );
4815         gint eraserMode = (prefs_get_int_attribute("tools.eraser", "mode", 0) != 0) ? 1 : 0;
4816         ege_select_one_action_set_active( act, eraserMode );
4817         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
4818     }
4822 //########################
4823 //##    Text Toolbox    ##
4824 //########################
4825 /*
4826 static void
4827 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
4829     //Call back for letter sizing spinbutton
4832 static void
4833 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
4835     //Call back for line height spinbutton
4838 static void
4839 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4841     //Call back for horizontal kerning spinbutton
4844 static void
4845 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4847     //Call back for vertical kerning spinbutton
4850 static void
4851 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
4853     //Call back for letter rotation spinbutton
4854 }*/
4856 namespace {
4858 bool popdown_visible = false;
4859 bool popdown_hasfocus = false;
4861 void
4862 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
4864     SPStyle *query =
4865         sp_style_new (SP_ACTIVE_DOCUMENT);
4867 //    int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4869     int result_family =
4870         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4872     int result_style =
4873         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4875     int result_numbers =
4876         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4878     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4880     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4881     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
4882         // there are no texts in selection, read from prefs
4883  
4884         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
4885         if (repr) {
4886             sp_style_read_from_repr (query, repr);
4887             if (g_object_get_data(tbl, "text_style_from_prefs")) {
4888                 // do not reset the toolbar style from prefs if we already did it last time
4889                 sp_style_unref(query);
4890                 return;
4891             }
4892             g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
4893         } else {
4894             sp_style_unref(query);
4895             return;
4896         }
4897     } else {
4898         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
4899     }
4901     if (query->text)
4902     {
4903         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4904             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4905             gtk_entry_set_text (GTK_ENTRY (entry), "");
4907         } else if (query->text->font_specification.value || query->text->font_family.value) {
4909             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4911             // Get the font that corresponds
4912             Glib::ustring familyName;
4914             font_instance * font = font_factory::Default()->FaceFromStyle(query);
4915             if (font) {
4916                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4917                 font->Unref();
4918                 font = NULL;
4919             }
4921             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4923             Gtk::TreePath path;
4924             try {
4925                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4926             } catch (...) {
4927                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4928                 sp_style_unref(query);
4929                 return;
4930             }
4932             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4933             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4935             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4937             gtk_tree_selection_select_path (tselection, path.gobj());
4938             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4940             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4941         }
4943         //Size
4944         {
4945             GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
4946             gchar *const 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             g_free(str);
4951         }
4953         //Anchor
4954         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4955         {
4956             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4957             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4958             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4959             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4960         }
4961         else
4962         {
4963             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4964             {
4965                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4966                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4967                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4968                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4969             }
4970             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4971             {
4972                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4973                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4974                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4975                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4976             }
4977             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4978             {
4979                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4980                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4981                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4982                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4983             }
4984         }
4986         //Style
4987         {
4988             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4990             gboolean active = gtk_toggle_button_get_active (button);
4991             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4993             if (active != check)
4994             {
4995                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4996                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4997                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4998             }
4999         }
5001         {
5002             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
5004             gboolean active = gtk_toggle_button_get_active (button);
5005             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
5007             if (active != check)
5008             {
5009                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5010                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5011                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5012             }
5013         }
5015         //Orientation
5016         //locking both buttons, changing one affect all group (both)
5017         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
5018         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5020         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
5021         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
5023         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
5024         {
5025             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5026         }
5027         else
5028         {
5029             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
5030         }
5031         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5032         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
5033     }
5035     sp_style_unref(query);
5038 void
5039 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
5041     sp_text_toolbox_selection_changed (selection, tbl);
5044 void
5045 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
5047     sp_text_toolbox_selection_changed (NULL, tbl);
5050 void
5051 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
5052                                 GObject             *tbl)
5054     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
5055     GtkTreeModel *model = 0;
5056     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5057     GtkTreeIter   iter;
5058     char         *family = 0;
5060     gdk_pointer_ungrab (GDK_CURRENT_TIME);
5061     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5063     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
5064         return;
5065     }
5067     gtk_tree_model_get (model, &iter, 0, &family, -1);
5069     if (g_object_get_data (G_OBJECT (selection), "block"))
5070     {
5071         gtk_entry_set_text (GTK_ENTRY (entry), family);
5072         return;
5073     }
5075     gtk_entry_set_text (GTK_ENTRY (entry), family);
5077     SPStyle *query =
5078         sp_style_new (SP_ACTIVE_DOCUMENT);
5080     int result_fontspec =
5081         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5083     //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5085     SPCSSAttr *css = sp_repr_css_attr_new ();
5088     // First try to get the font spec from the stored value
5089     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
5091     if (fontSpec.empty()) {
5092         // Construct a new font specification if it does not yet exist
5093         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5094         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5095         fontFromStyle->Unref();
5096     }
5098     if (!fontSpec.empty()) {
5099         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
5100         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
5101             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
5102             if (font) {
5103                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5105                 // Set all the these just in case they were altered when finding the best
5106                 // match for the new family and old style...
5108                 gchar c[256];
5110                 font->Family(c, 256);
5111                 sp_repr_css_set_property (css, "font-family", c);
5113                 font->Attribute( "weight", c, 256);
5114                 sp_repr_css_set_property (css, "font-weight", c);
5116                 font->Attribute("style", c, 256);
5117                 sp_repr_css_set_property (css, "font-style", c);
5119                 font->Attribute("stretch", c, 256);
5120                 sp_repr_css_set_property (css, "font-stretch", c);
5122                 font->Attribute("variant", c, 256);
5123                 sp_repr_css_set_property (css, "font-variant", c);
5125                 font->Unref();
5126             }
5127         }
5128     }
5130     // If querying returned nothing, set the default style of the tool (for new texts)
5131     if (result_fontspec == QUERY_STYLE_NOTHING)
5132     {
5133         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5134         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
5135     }
5136     else
5137     {
5138         sp_desktop_set_style (desktop, css, true, true);
5139     }
5141     sp_style_unref(query);
5143     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5144                                    _("Text: Change font family"));
5145     sp_repr_css_attr_unref (css);
5146     g_free(family);
5147     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5149     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5152 void
5153 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
5154                                        GObject      *tbl)
5156     const char *family = gtk_entry_get_text (entry);
5158     try {
5159         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
5160         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5161         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5162         gtk_tree_selection_select_path (selection, path.gobj());
5163         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5164         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5165     } catch (...) {
5166         if (family && strlen (family))
5167         {
5168             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5169         }
5170     }
5173 void
5174 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
5175                                    gpointer          data)
5177     if (g_object_get_data (G_OBJECT (button), "block")) return;
5178     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
5179     int prop = GPOINTER_TO_INT(data);
5181     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5182     SPCSSAttr *css = sp_repr_css_attr_new ();
5184     switch (prop)
5185     {
5186         case 0:
5187         {
5188             sp_repr_css_set_property (css, "text-anchor", "start");
5189             sp_repr_css_set_property (css, "text-align", "start");
5190             break;
5191         }
5192         case 1:
5193         {
5194             sp_repr_css_set_property (css, "text-anchor", "middle");
5195             sp_repr_css_set_property (css, "text-align", "center");
5196             break;
5197         }
5199         case 2:
5200         {
5201             sp_repr_css_set_property (css, "text-anchor", "end");
5202             sp_repr_css_set_property (css, "text-align", "end");
5203             break;
5204         }
5206         case 3:
5207         {
5208             sp_repr_css_set_property (css, "text-anchor", "start");
5209             sp_repr_css_set_property (css, "text-align", "justify");
5210             break;
5211         }
5212     }
5214     SPStyle *query =
5215         sp_style_new (SP_ACTIVE_DOCUMENT);
5216     int result_numbers =
5217         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5219     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5220     if (result_numbers == QUERY_STYLE_NOTHING)
5221     {
5222         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5223     }
5225     sp_style_unref(query);
5227     sp_desktop_set_style (desktop, css, true, true);
5228     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5229                                    _("Text: Change alignment"));
5230     sp_repr_css_attr_unref (css);
5232     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5235 void
5236 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
5237                                gpointer          data)
5239     if (g_object_get_data (G_OBJECT (button), "block")) return;
5241     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5242     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5243     int          prop       = GPOINTER_TO_INT(data);
5244     bool         active     = gtk_toggle_button_get_active (button);
5246     SPStyle *query =
5247         sp_style_new (SP_ACTIVE_DOCUMENT);
5249     int result_fontspec =
5250         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5252     //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5253     //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5254     //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5256     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
5257     Glib::ustring newFontSpec = "";
5259     if (fontSpec.empty()) {
5260         // Construct a new font specification if it does not yet exist
5261         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5262         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5263         fontFromStyle->Unref();
5264     }
5266     switch (prop)
5267     {
5268         case 0:
5269         {
5270             if (!fontSpec.empty()) {
5271                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
5272             }
5273             if (fontSpec != newFontSpec) {
5274                 // Don't even set the bold if the font didn't exist on the system
5275                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
5276             }
5277             break;
5278         }
5280         case 1:
5281         {
5282             if (!fontSpec.empty()) {
5283                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
5284             }
5285             if (fontSpec != newFontSpec) {
5286                 // Don't even set the italic if the font didn't exist on the system
5287                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
5288             }
5289             break;
5290         }
5291     }
5293     if (!newFontSpec.empty()) {
5294         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5295     }
5297     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5298     if (result_fontspec == QUERY_STYLE_NOTHING)
5299     {
5300         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5301     }
5303     sp_style_unref(query);
5305     sp_desktop_set_style (desktop, css, true, true);
5306     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5307                                    _("Text: Change font style"));
5308     sp_repr_css_attr_unref (css);
5310     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5313 void
5314 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
5315                                      gpointer         data)
5317     if (g_object_get_data (G_OBJECT (button), "block")) {
5318         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5319         return;
5320     }
5322     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5323     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5324     int          prop       = GPOINTER_TO_INT(data);
5326     switch (prop)
5327     {
5328         case 0:
5329         {
5330             sp_repr_css_set_property (css, "writing-mode", "lr");
5331             break;
5332         }
5334         case 1:
5335         {
5336             sp_repr_css_set_property (css, "writing-mode", "tb");
5337             break;
5338         }
5339     }
5341     SPStyle *query =
5342         sp_style_new (SP_ACTIVE_DOCUMENT);
5343     int result_numbers =
5344         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5346     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5347     if (result_numbers == QUERY_STYLE_NOTHING)
5348     {
5349         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5350     }
5352     sp_desktop_set_style (desktop, css, true, true);
5353     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5354                                    _("Text: Change orientation"));
5355     sp_repr_css_attr_unref (css);
5357     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5360 gboolean
5361 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5363     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5364     if (!desktop) return FALSE;
5366     switch (get_group0_keyval (event)) {
5367         case GDK_Escape: // defocus
5368             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5369             sp_text_toolbox_selection_changed (NULL, tbl); // update
5370             return TRUE; // I consumed the event
5371             break;
5372     }
5373     return FALSE;
5376 gboolean
5377 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5379     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5380     if (!desktop) return FALSE;
5382     switch (get_group0_keyval (event)) {
5383         case GDK_KP_Enter:
5384         case GDK_Return:
5385         case GDK_Escape: // defocus
5386             gtk_widget_hide (w);
5387             popdown_visible = false;
5388             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5389             return TRUE; // I consumed the event
5390             break;
5391         case GDK_w:
5392         case GDK_W:
5393             if (event->state & GDK_CONTROL_MASK) {
5394                 gtk_widget_hide (w);
5395                 popdown_visible = false;
5396                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5397                 return TRUE; // I consumed the event
5398             }
5399             break;
5400     }
5401     return FALSE;
5405 void
5406 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
5407                                GObject     *tbl)
5409     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5411     if (g_object_get_data (tbl, "size-block")) return;
5413     // If this is not from selecting a size in the list (in which case get_active will give the
5414     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5415     // process this event. This fixes GTK's stupid insistence on sending an activate change every
5416     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5417     if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5418         return;
5420     gdouble value = -1;
5421     {
5422         gchar *endptr;
5423         gchar *const text = gtk_combo_box_get_active_text(cbox);
5424         if (text) {
5425             value = g_strtod(text, &endptr);
5426             if (endptr == text) {  // Conversion failed, non-numeric input.
5427                 value = -1;
5428             }
5429             g_free(text);
5430         }
5431     }
5432     if (value <= 0) {
5433         return; // could not parse value
5434     }
5436     SPCSSAttr *css = sp_repr_css_attr_new ();
5437     Inkscape::CSSOStringStream osfs;
5438     osfs << value;
5439     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5441     SPStyle *query =
5442         sp_style_new (SP_ACTIVE_DOCUMENT);
5443     int result_numbers =
5444         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5446     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5447     if (result_numbers == QUERY_STYLE_NOTHING)
5448     {
5449         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5450     }
5452     sp_style_unref(query);
5454     sp_desktop_set_style (desktop, css, true, true);
5455     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5456                                    _("Text: Change font size"));
5457     sp_repr_css_attr_unref (css);
5459     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5462 gboolean
5463 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5465     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5466     if (!desktop) return FALSE;
5468     if (!g_object_get_data (tbl, "esc-pressed")) {
5469         g_object_set_data (tbl, "enter-pressed", gpointer(1));
5470         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5471         sp_text_toolbox_size_changed (cbox, tbl);
5472         g_object_set_data (tbl, "enter-pressed", gpointer(0));
5473     }
5474     return FALSE; // I consumed the event
5478 gboolean
5479 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5481     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5482     if (!desktop) return FALSE;
5484     switch (get_group0_keyval (event)) {
5485         case GDK_Escape: // defocus
5486             g_object_set_data (tbl, "esc-pressed", gpointer(1));
5487             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5488             g_object_set_data (tbl, "esc-pressed", gpointer(0));
5489             return TRUE; // I consumed the event
5490             break;
5491         case GDK_Return: // defocus
5492         case GDK_KP_Enter:
5493             g_object_set_data (tbl, "enter-pressed", gpointer(1));
5494             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5495             sp_text_toolbox_size_changed (cbox, tbl);
5496             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5497             g_object_set_data (tbl, "enter-pressed", gpointer(0));
5498             return TRUE; // I consumed the event
5499             break;
5500     }
5501     return FALSE;
5504 void
5505 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
5506                                          GObject            *tbl)
5508     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
5509     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5510     int x, y;
5512     if (!popdown_visible)
5513     {
5514         gdk_window_get_origin (widget->window, &x, &y);
5515         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
5516         gtk_widget_show_all (popdown);
5517         //sp_transientize (popdown);
5519         gdk_pointer_grab (widget->window, TRUE,
5520                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
5521                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
5522                                         GDK_POINTER_MOTION_MASK),
5523                           NULL, NULL, GDK_CURRENT_TIME);
5525         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
5527         popdown_visible = true;
5528     }
5529     else
5530     {
5531         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5532         gdk_pointer_ungrab (GDK_CURRENT_TIME);
5533         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5534         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5535         gtk_widget_hide (popdown);
5536         popdown_visible = false;
5537     }
5540 gboolean
5541 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
5542                                  GdkEventFocus    */*event*/,
5543                                  GObject          */*tbl*/)
5545     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
5546     return FALSE;
5549 gboolean
5550 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
5551                                    GdkEventFocus    */*event*/,
5552                                    GObject          */*tbl*/)
5554     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5556     if (popdown_hasfocus) {
5557         gtk_widget_hide (popdown);
5558         popdown_hasfocus = false;
5559         popdown_visible = false;
5560         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5561         return TRUE;
5562     }
5563     return FALSE;
5566 gboolean
5567 sp_text_toolbox_popdown_focus_in (GtkWidget        */*popdown*/,
5568                                    GdkEventFocus    */*event*/,
5569                                    GObject          */*tbl*/)
5571     popdown_hasfocus = true;
5572     return TRUE;
5576 void
5577 cell_data_func  (GtkTreeViewColumn */*column*/,
5578                  GtkCellRenderer   *cell,
5579                  GtkTreeModel      *tree_model,
5580                  GtkTreeIter       *iter,
5581                  gpointer           /*data*/)
5583     gchar *family;
5584     gtk_tree_model_get(tree_model, iter, 0, &family, -1);
5585     gchar *const family_escaped = g_markup_escape_text(family, -1);
5587     static char const *const sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
5588     gchar *const sample_escaped = g_markup_escape_text(sample, -1);
5590     std::stringstream markup;
5591     markup << family_escaped << "  <span foreground='darkgray' font_family='"
5592            << family_escaped << "'>" << sample_escaped << "</span>";
5593     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
5595     g_free(family);
5596     g_free(family_escaped);
5597     g_free(sample_escaped);
5600 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
5601     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
5602     if (completion) {
5603         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
5604         g_object_unref (completion);
5605     }
5608 GtkWidget*
5609 sp_text_toolbox_new (SPDesktop *desktop)
5611     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
5612     GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
5614     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
5615     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
5617     GtkTooltips *tt = gtk_tooltips_new();
5618     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
5620     ////////////Family
5621     //Window
5622     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5623     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
5625     //Entry
5626     GtkWidget           *entry = gtk_entry_new ();
5627     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
5628     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
5629     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
5630     gtk_entry_completion_set_text_column (completion, 0);
5631     gtk_entry_completion_set_minimum_key_length (completion, 1);
5632     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
5633     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
5634     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
5635     gtk_toolbar_append_widget( tbl, entry, "", "" );
5636     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
5638     //Button
5639     GtkWidget   *button = gtk_button_new ();
5640     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
5641     gtk_toolbar_append_widget( tbl, button, "", "");
5643     //Popdown
5644     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
5645     GtkWidget           *treeview = gtk_tree_view_new ();
5647     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
5648     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
5649     gtk_tree_view_column_pack_start (column, cell, FALSE);
5650     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
5651     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5652     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5654     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5655     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5656     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5658     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5660     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5661     gtk_container_add (GTK_CONTAINER (sw), treeview);
5663     gtk_container_add (GTK_CONTAINER (window), sw);
5664     gtk_widget_set_size_request (window, 300, 450);
5666     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5667     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5668     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5670     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5672     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5673     g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5674     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5676     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5677     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5679     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5680     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5681     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5682     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5683     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5685     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
5686     GtkWidget *box = gtk_event_box_new ();
5687     gtk_container_add (GTK_CONTAINER (box), image);
5688     gtk_toolbar_append_widget( tbl, box, "", "");
5689     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5690     GtkTooltips *tooltips = gtk_tooltips_new ();
5691     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5692     gtk_widget_hide (GTK_WIDGET (box));
5693     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
5695     ////////////Size
5696     gchar const *const sizes[] = {
5697         "4", "6", "8", "9", "10", "11", "12", "13", "14",
5698         "16", "18", "20", "22", "24", "28",
5699         "32", "36", "40", "48", "56", "64", "72", "144"
5700     };
5702     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
5703     for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
5704         gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
5705     }
5706     gtk_widget_set_size_request (cbox, 80, -1);
5707     gtk_toolbar_append_widget( tbl, cbox, "", "");
5708     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
5709     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
5710     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
5711     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
5713     ////////////Text anchor
5714     GtkWidget *group   = gtk_radio_button_new (NULL);
5715     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
5716     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
5718     // left
5719     GtkWidget *rbutton = group;
5720     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5721     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
5722     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5724     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5725     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
5726     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
5727     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
5729     // center
5730     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5731     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5732     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
5733     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5735     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5736     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
5737     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5738     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5740     // right
5741     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5742     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5743     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
5744     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5746     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5747     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
5748     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5749     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5751     // fill
5752     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5753     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5754     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
5755     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5757     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5758     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
5759     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
5760     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
5762     gtk_toolbar_append_widget( tbl, row, "", "");
5764     //spacer
5765     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5767     ////////////Text style
5768     row = gtk_hbox_new (FALSE, 4);
5770     // bold
5771     rbutton = gtk_toggle_button_new ();
5772     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5773     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
5774     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5775     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
5777     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5778     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
5779     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
5781     // italic
5782     rbutton = gtk_toggle_button_new ();
5783     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5784     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
5785     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5786     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
5788     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5789     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
5790     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
5792     gtk_toolbar_append_widget( tbl, row, "", "");
5794     //spacer
5795     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5797     ////////////Text orientation
5798     group   = gtk_radio_button_new (NULL);
5799     row     = gtk_hbox_new (FALSE, 4);
5800     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
5802     // horizontal
5803     rbutton = group;
5804     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5805     gtk_container_add           (GTK_CONTAINER (rbutton),
5806                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
5807     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5808     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
5810     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5811     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
5812     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
5814     // vertical
5815     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5816     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5817     gtk_container_add           (GTK_CONTAINER (rbutton),
5818                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
5819     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5820     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
5822     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5823     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
5824     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
5825     gtk_toolbar_append_widget( tbl, row, "", "" );
5828     //watch selection
5829     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
5831     sigc::connection *c_selection_changed =
5832         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5833                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
5834     pool->add_connection ("selection-changed", c_selection_changed);
5836     sigc::connection *c_selection_modified =
5837         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5838                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
5839     pool->add_connection ("selection-modified", c_selection_modified);
5841     sigc::connection *c_subselection_changed =
5842         new sigc::connection (desktop->connectToolSubselectionChanged
5843                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
5844     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
5846     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
5849     gtk_widget_show_all( GTK_WIDGET(tbl) );
5851     return GTK_WIDGET(tbl);
5852 } // end of sp_text_toolbox_new()
5854 }//<unnamed> namespace
5857 //#########################
5858 //##      Connector      ##
5859 //#########################
5861 static void sp_connector_path_set_avoid(void)
5863     cc_selection_set_avoid(true);
5867 static void sp_connector_path_set_ignore(void)
5869     cc_selection_set_avoid(false);
5874 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
5876     // quit if run by the _changed callbacks
5877     if (g_object_get_data( tbl, "freeze" )) {
5878         return;
5879     }
5881     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5882     SPDocument *doc = sp_desktop_document(desktop);
5884     if (!sp_document_get_undo_sensitive(doc))
5885     {
5886         return;
5887     }
5889     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5891     if ( repr->attribute("inkscape:connector-spacing") ) {
5892         gdouble priorValue = gtk_adjustment_get_value(adj);
5893         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
5894         if ( priorValue == gtk_adjustment_get_value(adj) ) {
5895             return;
5896         }
5897     } else if ( adj->value == defaultConnSpacing ) {
5898         return;
5899     }
5901     // in turn, prevent callbacks from responding
5902     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5904     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
5905     SP_OBJECT(desktop->namedview)->updateRepr();
5907     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
5908     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
5909         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
5910         NR::Matrix m = NR::identity();
5911         avoid_item_move(&m, item);
5912     }
5914     if (items) {
5915         g_slist_free(items);
5916     }
5918     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
5919             _("Change connector spacing"));
5921     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5923     spinbutton_defocus(GTK_OBJECT(tbl));
5926 static void sp_connector_graph_layout(void)
5928     if (!SP_ACTIVE_DESKTOP) return;
5930     // hack for clones, see comment in align-and-distribute.cpp
5931     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5932     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5934     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5936     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5938     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5941 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5943     if ( gtk_toggle_action_get_active( act ) ) {
5944         prefs_set_string_attribute("tools.connector", "directedlayout",
5945                 "true");
5946     } else {
5947         prefs_set_string_attribute("tools.connector", "directedlayout",
5948                 "false");
5949     }
5952 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5954     if ( gtk_toggle_action_get_active( act ) ) {
5955         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5956                 "true");
5957     } else {
5958         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5959                 "false");
5960     }
5964 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5966     prefs_set_double_attribute("tools.connector", "length", adj->value);
5967     spinbutton_defocus(GTK_OBJECT(tbl));
5970 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5971                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5972                                             bool /*is_interactive*/, gpointer data)
5974     GtkWidget *tbl = GTK_WIDGET(data);
5976     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5977         return;
5978     }
5979     if (strcmp(name, "inkscape:connector-spacing") != 0) {
5980         return;
5981     }
5983     GtkAdjustment *adj = (GtkAdjustment*)
5984             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5985     gdouble spacing = defaultConnSpacing;
5986     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5988     gtk_adjustment_set_value(adj, spacing);
5992 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5993     NULL, /* child_added */
5994     NULL, /* child_removed */
5995     connector_tb_event_attr_changed,
5996     NULL, /* content_changed */
5997     NULL  /* order_changed */
5998 };
6001 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
6003     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
6005     {
6006         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
6007                                           _("Avoid"),
6008                                           _("Make connectors avoid selected objects"),
6009                                           "connector_avoid",
6010                                           secondarySize );
6011         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
6012         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6013     }
6015     {
6016         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
6017                                           _("Ignore"),
6018                                           _("Make connectors ignore selected objects"),
6019                                           "connector_ignore",
6020                                           secondarySize );
6021         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
6022         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6023     }
6025     EgeAdjustmentAction* eact = 0;
6027     // Spacing spinbox
6028     eact = create_adjustment_action( "ConnectorSpacingAction",
6029                                      _("Connector Spacing"), _("Spacing:"),
6030                                      _("The amount of space left around objects by auto-routing connectors"),
6031                                      "tools.connector", "spacing", defaultConnSpacing,
6032                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
6033                                      0, 100, 1.0, 10.0,
6034                                      0, 0, 0,
6035                                      connector_spacing_changed, 1, 0 );
6036     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6038     // Graph (connector network) layout
6039     {
6040         InkAction* inky = ink_action_new( "ConnectorGraphAction",
6041                                           _("Graph"),
6042                                           _("Nicely arrange selected connector network"),
6043                                           "graph_layout",
6044                                           secondarySize );
6045         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
6046         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6047     }
6049     // Default connector length spinbox
6050     eact = create_adjustment_action( "ConnectorLengthAction",
6051                                      _("Connector Length"), _("Length:"),
6052                                      _("Ideal length for connectors when layout is applied"),
6053                                      "tools.connector", "length", 100,
6054                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
6055                                      10, 1000, 10.0, 100.0,
6056                                      0, 0, 0,
6057                                      connector_length_changed, 1, 0 );
6058     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6061     // Directed edges toggle button
6062     {
6063         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
6064                                                       _("Downwards"),
6065                                                       _("Make connectors with end-markers (arrows) point downwards"),
6066                                                       "directed_graph",
6067                                                       Inkscape::ICON_SIZE_DECORATION );
6068         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6070         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
6071         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6072                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6074         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
6075     }
6077     // Avoid overlaps toggle button
6078     {
6079         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
6080                                                       _("Remove overlaps"),
6081                                                       _("Do not allow overlapping shapes"),
6082                                                       "remove_overlaps",
6083                                                       Inkscape::ICON_SIZE_DECORATION );
6084         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6086         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
6087         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6088                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6090         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
6091     }
6093     // Code to watch for changes to the connector-spacing attribute in
6094     // the XML.
6095     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6096     g_assert(repr != NULL);
6098     purge_repr_listener( holder, holder );
6100     if (repr) {
6101         g_object_set_data( holder, "repr", repr );
6102         Inkscape::GC::anchor(repr);
6103         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
6104         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
6105     }
6106 } // end of sp_connector_toolbox_prep()
6109 //#########################
6110 //##     Paintbucket     ##
6111 //#########################
6113 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
6115     gint channels = ege_select_one_action_get_active( act );
6116     flood_channels_set_channels( channels );
6119 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
6121     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
6124 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
6126     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
6129 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
6131     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
6132     SPUnit const *unit = tracker->getActiveUnit();
6134     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
6136     prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
6139 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
6141     // FIXME: make defaults settable via Inkscape Options
6142     struct KeyValue {
6143         char const *key;
6144         double value;
6145     } const key_values[] = {
6146         {"threshold", 15},
6147         {"offset", 0.0}
6148     };
6150     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
6151         KeyValue const &kv = key_values[i];
6152         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
6153         if ( adj ) {
6154             gtk_adjustment_set_value(adj, kv.value);
6155         }
6156     }
6158     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
6159     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
6160     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
6161     ege_select_one_action_set_active( autogap_action, 0 );
6164 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6166     EgeAdjustmentAction* eact = 0;
6168     {
6169         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6171         GList* items = 0;
6172         gint count = 0;
6173         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
6174         {
6175             GtkTreeIter iter;
6176             gtk_list_store_append( model, &iter );
6177             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6178             count++;
6179         }
6180         g_list_free( items );
6181         items = 0;
6182         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
6183         g_object_set( act1, "short_label", _("Fill by:"), NULL );
6184         ege_select_one_action_set_appearance( act1, "compact" );
6185         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
6186         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
6187         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
6188         g_object_set_data( holder, "channels_action", act1 );
6189     }
6191     // Spacing spinbox
6192     {
6193         eact = create_adjustment_action(
6194             "ThresholdAction",
6195             _("Fill Threshold"), _("Threshold:"),
6196             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
6197             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
6198             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
6199             0, 0, 0,
6200             paintbucket_threshold_changed, 1, 0 );
6202         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6203         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6204     }
6206     // Create the units menu.
6207     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
6208     const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
6209     if (stored_unit)
6210         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
6211     g_object_set_data( holder, "tracker", tracker );
6212     {
6213         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
6214         gtk_action_group_add_action( mainActions, act );
6215     }
6217     // Offset spinbox
6218     {
6219         eact = create_adjustment_action(
6220             "OffsetAction",
6221             _("Grow/shrink by"), _("Grow/shrink by:"),
6222             _("The amount to grow (positive) or shrink (negative) the created fill path"),
6223             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
6224             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
6225             0, 0, 0,
6226             paintbucket_offset_changed, 1, 2);
6227         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
6229         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6230     }
6232     /* Auto Gap */
6233     {
6234         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6236         GList* items = 0;
6237         gint count = 0;
6238         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
6239         {
6240             GtkTreeIter iter;
6241             gtk_list_store_append( model, &iter );
6242             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6243             count++;
6244         }
6245         g_list_free( items );
6246         items = 0;
6247         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
6248         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
6249         ege_select_one_action_set_appearance( act2, "compact" );
6250         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
6251         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
6252         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
6253         g_object_set_data( holder, "autogap_action", act2 );
6254     }
6256     /* Reset */
6257     {
6258         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
6259                                           _("Defaults"),
6260                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
6261                                           GTK_STOCK_CLEAR );
6262         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
6263         gtk_action_group_add_action( mainActions, act );
6264         gtk_action_set_sensitive( act, TRUE );
6265     }
6269 /*
6270   Local Variables:
6271   mode:c++
6272   c-file-style:"stroustrup"
6273   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
6274   indent-tabs-mode:nil
6275   fill-column:99
6276   End:
6277 */
6278 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :