Code

cleanup and comment
[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, _("Bézier"),
3301                                 1, _("Regular Bézier mode"),
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, _("Spiro splines mode"),
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, _("Clipboard"));
3344     glist = g_list_append (glist, _("Crescendo"));
3345     glist = g_list_append (glist, _("Decrescendo"));
3347     return glist;
3350 static void
3351 sp_freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3352     /*advanced shape options */
3353     {
3354         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3356         GList* items = 0;
3357         gint count = 0;
3358         for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3359         {
3360             GtkTreeIter iter;
3361             gtk_list_store_append( model, &iter );
3362             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3363             count++;
3364         }
3365         g_list_free( items );
3366         items = 0;
3367         EgeSelectOneAction* act1 = ege_select_one_action_new(
3368             tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3369             _("Shape:"), ("Shape"), NULL, GTK_TREE_MODEL(model));
3370         g_object_set( act1, "short_label", _("Shape:"), NULL );
3371         ege_select_one_action_set_appearance( act1, "compact" );
3372         ege_select_one_action_set_active( act1, prefs_get_int_attribute(freehand_tool_name(holder), "shape", 0) );
3373         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(sp_freehand_change_shape), holder );
3374         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3375         g_object_set_data( holder, "shape_action", act1 );
3376     }
3379 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3381     sp_add_spiro_toggle(mainActions, holder, false);
3382     sp_freehand_add_advanced_shape_options(mainActions, holder, false);
3386 static void
3387 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3389     GtkWidget *tbl = GTK_WIDGET(obj);
3391     GtkAdjustment *adj;
3393     // fixme: make settable
3394     gdouble tolerance = 4;
3396     adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3397     gtk_adjustment_set_value(adj, tolerance);
3398     gtk_adjustment_value_changed(adj);
3400     spinbutton_defocus(GTK_OBJECT(tbl));
3403 static void
3404 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3407     // quit if run by the attr_changed listener
3408     if (g_object_get_data( tbl, "freeze" )) {
3409         return;
3410     }
3411     // in turn, prevent listener from responding
3412     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3413     prefs_set_double_attribute("tools.freehand.pencil",
3414                                "tolerance", adj->value);
3415     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3421 static void
3422 sp_pencil_tb_tolerance_value_changed_external(Inkscape::XML::Node */*repr*/,
3423                                               const gchar */*key*/,
3424                                               const gchar */*oldval*/,
3425                                               const gchar */*newval*/,
3426                                               bool /*is_interactive*/,
3427                                               void * data)
3429     GObject* tbl = G_OBJECT(data);
3430     if (g_object_get_data( tbl, "freeze" )) {
3431         return;
3432     }
3434     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3436     GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl,
3437                                                             "tolerance");
3439     double v = prefs_get_double_attribute("tools.freehand.pencil",
3440                                             "tolerance", adj->value);
3441     gtk_adjustment_set_value(adj, v);
3442     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3446 static Inkscape::XML::NodeEventVector pencil_node_events =
3448     NULL,
3449     NULL,
3450     sp_pencil_tb_tolerance_value_changed_external,
3451     NULL,
3452     NULL,
3453 };
3456 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3458     sp_add_spiro_toggle(mainActions, holder, true);
3460     EgeAdjustmentAction* eact = 0;
3462     /* Tolerance */
3463     {
3464         gchar const* labels[] = {_("(many nodes, rough)"), ("(default)"), 0, 0, 0, 0, ("(few nodes, smooth)")};
3465         gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
3466         eact = create_adjustment_action( "PencilToleranceAction",
3467                                          _("Smoothing:"), _("Smoothing"),
3468                  _("How much smoothing (simplifying) is applied to the line"),
3469                                          "tools.freehand.pencil", "tolerance",
3470                                          3.0,
3471                                          GTK_WIDGET(desktop->canvas), NULL,
3472                                          holder, TRUE, "altx-pencil",
3473                                          1, 100.0, 0.5, 0,
3474                                          labels, values, G_N_ELEMENTS(labels),
3475                                          sp_pencil_tb_tolerance_value_changed,
3476                                          1, 2);
3477         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3478         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3480         Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE,
3481                                                       "tools.freehand.pencil");
3482         repr->addListener(&pencil_node_events, G_OBJECT(holder));
3483         g_object_set_data(G_OBJECT(holder), "repr", repr);
3485     }
3487     /* advanced shape options */
3488     sp_freehand_add_advanced_shape_options(mainActions, holder, true);
3490     /* Reset */
3491     {
3492         InkAction* inky = ink_action_new( "PencilResetAction",
3493                                           _("Defaults"),
3494                                           _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3495                                           GTK_STOCK_CLEAR,
3496                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3497         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
3498         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3499     }
3501     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3506 //########################
3507 //##       Tweak        ##
3508 //########################
3510 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3512     prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3515 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3517     prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3520 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3522     prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3525 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3527     int mode = ege_select_one_action_get_active( act );
3528     prefs_set_int_attribute("tools.tweak", "mode", mode);
3530     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3531     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3532     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3533     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3534     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3535     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3536     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3537         if (doh) gtk_action_set_sensitive (doh, TRUE);
3538         if (dos) gtk_action_set_sensitive (dos, TRUE);
3539         if (dol) gtk_action_set_sensitive (dol, TRUE);
3540         if (doo) gtk_action_set_sensitive (doo, TRUE);
3541         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3542         if (fid) gtk_action_set_sensitive (fid, FALSE);
3543     } else {
3544         if (doh) gtk_action_set_sensitive (doh, FALSE);
3545         if (dos) gtk_action_set_sensitive (dos, FALSE);
3546         if (dol) gtk_action_set_sensitive (dol, FALSE);
3547         if (doo) gtk_action_set_sensitive (doo, FALSE);
3548         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3549         if (fid) gtk_action_set_sensitive (fid, TRUE);
3550     }
3553 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3555     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3558 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3559     bool show = gtk_toggle_action_get_active( act );
3560     prefs_set_int_attribute ("tools.tweak", "doh",  show ? 1 : 0);
3562 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3563     bool show = gtk_toggle_action_get_active( act );
3564     prefs_set_int_attribute ("tools.tweak", "dos",  show ? 1 : 0);
3566 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3567     bool show = gtk_toggle_action_get_active( act );
3568     prefs_set_int_attribute ("tools.tweak", "dol",  show ? 1 : 0);
3570 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3571     bool show = gtk_toggle_action_get_active( act );
3572     prefs_set_int_attribute ("tools.tweak", "doo",  show ? 1 : 0);
3575 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3577     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3579     {
3580         /* Width */
3581         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3582         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3583         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3584                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3585                                                               "tools.tweak", "width", 15,
3586                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3587                                                               1, 100, 1.0, 0.0,
3588                                                               labels, values, G_N_ELEMENTS(labels),
3589                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
3590         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3591         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3592         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3593     }
3596     {
3597         /* Force */
3598         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3599         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3600         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3601                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
3602                                                               "tools.tweak", "force", 20,
3603                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3604                                                               1, 100, 1.0, 0.0,
3605                                                               labels, values, G_N_ELEMENTS(labels),
3606                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
3607         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3608         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3609         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3610     }
3612     /* Mode */
3613     {
3614         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3616         GtkTreeIter iter;
3617         gtk_list_store_append( model, &iter );
3618         gtk_list_store_set( model, &iter,
3619                             0, _("Push mode"),
3620                             1, _("Push parts of paths in any direction"),
3621                             2, "tweak_push_mode",
3622                             -1 );
3624         gtk_list_store_append( model, &iter );
3625         gtk_list_store_set( model, &iter,
3626                             0, _("Shrink mode"),
3627                             1, _("Shrink (inset) parts of paths"),
3628                             2, "tweak_shrink_mode",
3629                             -1 );
3631         gtk_list_store_append( model, &iter );
3632         gtk_list_store_set( model, &iter,
3633                             0, _("Grow mode"),
3634                             1, _("Grow (outset) parts of paths"),
3635                             2, "tweak_grow_mode",
3636                             -1 );
3638         gtk_list_store_append( model, &iter );
3639         gtk_list_store_set( model, &iter,
3640                             0, _("Attract mode"),
3641                             1, _("Attract parts of paths towards cursor"),
3642                             2, "tweak_attract_mode",
3643                             -1 );
3645         gtk_list_store_append( model, &iter );
3646         gtk_list_store_set( model, &iter,
3647                             0, _("Repel mode"),
3648                             1, _("Repel parts of paths from cursor"),
3649                             2, "tweak_repel_mode",
3650                             -1 );
3652         gtk_list_store_append( model, &iter );
3653         gtk_list_store_set( model, &iter,
3654                             0, _("Roughen mode"),
3655                             1, _("Roughen parts of paths"),
3656                             2, "tweak_roughen_mode",
3657                             -1 );
3659         gtk_list_store_append( model, &iter );
3660         gtk_list_store_set( model, &iter,
3661                             0, _("Color paint mode"),
3662                             1, _("Paint the tool's color upon selected objects"),
3663                             2, "tweak_colorpaint_mode",
3664                             -1 );
3666         gtk_list_store_append( model, &iter );
3667         gtk_list_store_set( model, &iter,
3668                             0, _("Color jitter mode"),
3669                             1, _("Jitter the colors of selected objects"),
3670                             2, "tweak_colorjitter_mode",
3671                             -1 );
3673         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3674         g_object_set( act, "short_label", _("Mode:"), NULL );
3675         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3676         g_object_set_data( holder, "mode_action", act );
3678         ege_select_one_action_set_appearance( act, "full" );
3679         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3680         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3681         ege_select_one_action_set_icon_column( act, 2 );
3682         ege_select_one_action_set_icon_size( act, secondarySize );
3683         ege_select_one_action_set_tooltip_column( act, 1  );
3685         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3686         ege_select_one_action_set_active( act, mode );
3687         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3689         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3690     }
3692     guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3694     {
3695         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3696         ege_output_action_set_use_markup( act, TRUE );
3697         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3698         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3699             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3700         g_object_set_data( holder, "tweak_channels_label", act);
3701     }
3703     {
3704         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3705                                                       _("Hue"),
3706                                                       _("In color mode, act on objects' hue"),
3707                                                       NULL,
3708                                                       Inkscape::ICON_SIZE_DECORATION );
3709         //TRANSLATORS:  "H" here stands for hue
3710         g_object_set( act, "short_label", _("H"), NULL );
3711         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3712         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3713         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3714         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3715             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3716         g_object_set_data( holder, "tweak_doh", act);
3717     }
3718     {
3719         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3720                                                       _("Saturation"),
3721                                                       _("In color mode, act on objects' saturation"),
3722                                                       NULL,
3723                                                       Inkscape::ICON_SIZE_DECORATION );
3724         //TRANSLATORS: "S" here stands for Saturation
3725         g_object_set( act, "short_label", _("S"), NULL );
3726         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3727         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3728         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3729         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3730             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3731         g_object_set_data( holder, "tweak_dos", act );
3732     }
3733     {
3734         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3735                                                       _("Lightness"),
3736                                                       _("In color mode, act on objects' lightness"),
3737                                                       NULL,
3738                                                       Inkscape::ICON_SIZE_DECORATION );
3739         //TRANSLATORS: "L" here stands for Lightness
3740         g_object_set( act, "short_label", _("L"), NULL );
3741         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3742         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3743         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3744         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3745             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3746         g_object_set_data( holder, "tweak_dol", act );
3747     }
3748     {
3749         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3750                                                       _("Opacity"),
3751                                                       _("In color mode, act on objects' opacity"),
3752                                                       NULL,
3753                                                       Inkscape::ICON_SIZE_DECORATION );
3754         //TRANSLATORS: "O" here stands for Opacity
3755         g_object_set( act, "short_label", _("O"), NULL );
3756         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3757         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3758         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3759         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3760             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3761         g_object_set_data( holder, "tweak_doo", act );
3762     }
3764     {   /* Fidelity */
3765         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3766         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3767         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3768                                                               _("Fidelity"), _("Fidelity:"),
3769                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3770                                                               "tools.tweak", "fidelity", 50,
3771                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3772                                                               1, 100, 1.0, 10.0,
3773                                                               labels, values, G_N_ELEMENTS(labels),
3774                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
3775         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3776         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3777         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3778             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3779         g_object_set_data( holder, "tweak_fidelity", eact );
3780     }
3783     /* Use Pressure button */
3784     {
3785         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3786                                                       _("Pressure"),
3787                                                       _("Use the pressure of the input device to alter the force of tweak action"),
3788                                                       "use_pressure",
3789                                                       Inkscape::ICON_SIZE_DECORATION );
3790         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3791         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3792         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3793     }
3798 //########################
3799 //##     Calligraphy    ##
3800 //########################
3801 static void update_presets_list (GObject *tbl){
3803     if (g_object_get_data(tbl, "presets_blocked")) 
3804         return;
3806     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3807     if (sel) {
3808         ege_select_one_action_set_active(sel, 0);
3809     }
3812 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3814     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value * 0.01 );
3815     update_presets_list(tbl);
3818 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3820     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value * 0.01 );
3821     update_presets_list(tbl);
3824 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3826     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3827     update_presets_list(tbl);
3830 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3832     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3833     update_presets_list(tbl);
3836 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3838     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value * 0.01 );
3839     update_presets_list(tbl);
3842 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3844     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value * 0.01);
3845     update_presets_list(tbl);
3848 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3850     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value * 0.01 );
3851     update_presets_list(tbl);
3854 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3856     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3857     update_presets_list(tbl);
3860 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
3862     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3863     update_presets_list(tbl);
3866 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
3868     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3869     update_presets_list(tbl);
3872 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
3874     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
3875     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3876     update_presets_list(tbl);
3877     if (calligraphy_angle )
3878         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3882 #define NUMBER_OF_PRESET_PARAMS 11
3883 static gchar * widget_names[NUMBER_OF_PRESET_PARAMS] = {
3884     "width",
3885     "mass",
3886     "wiggle",
3887     "angle",
3888     "thinning",
3889     "tremor",
3890     "flatness",
3891     "cap_rounding",
3892     "usepressure",
3893     "tracebackground",
3894     "usetilt"
3895 };
3898 static void sp_dcc_build_presets_list(GObject *tbl) 
3900     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3901     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
3902     gtk_list_store_clear (model);
3904     {
3905         GtkTreeIter iter;
3906         gtk_list_store_append( model, &iter );
3907         gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
3908     }
3910     //TODO: switch back to prefs API
3911     Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
3912     Inkscape::XML::Node *child_repr = sp_repr_children(repr);
3913     int ii=1;
3914     while (child_repr) {
3915         GtkTreeIter iter;
3916         char *preset_name = (char *) child_repr->attribute("name");
3917         gtk_list_store_append( model, &iter );
3918         gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );
3919         child_repr = sp_repr_next(child_repr);
3920     }
3922     {
3923         GtkTreeIter iter;
3924         gtk_list_store_append( model, &iter );
3925         gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
3926         g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
3927     }
3929     update_presets_list (tbl);
3932 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
3934     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
3935     if (! desktop) return;
3937     if (g_object_get_data(tbl, "presets_blocked")) 
3938         return;
3940     Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
3941     if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) {
3942         // dialog cancelled
3943         update_presets_list (tbl);
3944         return;
3945     }
3946     Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
3948     if (!profile_name.c_str() || *profile_name.c_str() == 0) {
3949         // empty name entered
3950         update_presets_list (tbl);
3951         return;
3952     }
3954     int new_index = -1;
3955     gchar *pref_path = NULL;
3956     int total_prefs = pref_path_number_of_children("tools.calligraphic.preset");
3958     for (int i = 1; i <= total_prefs; i++) {
3959         gchar *path = get_pref_nth_child("tools.calligraphic.preset", i);
3960         const gchar *name = prefs_get_string_attribute(path, "name");
3961         if (name && !strcmp(name, profile_name.c_str())) {
3962             // we already have preset with this name, replace its values
3963             new_index = i;
3964             pref_path = g_strdup(path);
3965             break;
3966         }
3967     }
3969     if (new_index == -1) {
3970         // no preset with this name, create 
3971         new_index = total_prefs + 1;
3972         gchar *profile_id = g_strdup_printf("dcc%d", new_index);
3973         pref_path = create_pref("tools.calligraphic.preset", profile_id);
3974         free(profile_id);
3975     }
3977     for (unsigned i = 0; i < NUMBER_OF_PRESET_PARAMS; ++i) {
3978         gchar *widget_name = widget_names[i];
3979         void *widget = g_object_get_data(tbl, widget_name);
3980         if (widget) {
3981             if (GTK_IS_ADJUSTMENT(widget)) {
3982                 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
3983                 double v = gtk_adjustment_get_value(adj);
3984                 prefs_set_double_attribute(pref_path, widget_name, v);
3985                 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
3986             } else if (GTK_IS_TOGGLE_ACTION(widget)) {
3987                 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
3988                 int v = gtk_toggle_action_get_active(toggle);
3989                 prefs_set_int_attribute(pref_path, widget_name, v);
3990                 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
3991             } else {
3992                g_warning("Unknown widget type for preset: %s\n", widget_name);
3993             }
3994         } else {
3995             g_warning("Bad key when writing preset: %s\n", widget_name);
3996         }
3997     }
3998     prefs_set_string_attribute(pref_path, "name", profile_name.c_str());
4000     sp_dcc_build_presets_list (tbl);
4002     free (pref_path);
4006 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4008     gint preset_index = ege_select_one_action_get_active( act );
4009     gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4011     if (preset_index == save_presets_index) {
4012         // this is the Save command
4013         sp_dcc_save_profile(NULL, tbl);
4014         return;
4015     }
4017     gchar *preset_path = get_pref_nth_child("tools.calligraphic.preset", preset_index);
4019     if (preset_path) {
4020         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
4022         Inkscape::XML::Node *preset_repr = inkscape_get_repr(INKSCAPE, preset_path);
4024         for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
4025               iter; 
4026               ++iter ) {
4027             const gchar *attr_name = g_quark_to_string(iter->key);
4028             if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
4029                 continue;
4030             void *widget = g_object_get_data(tbl, attr_name);
4031             if (widget) {
4032                 if (GTK_IS_ADJUSTMENT(widget)) {
4033                     double v = prefs_get_double_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4034                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4035                     gtk_adjustment_set_value(adj, v);
4036                     //std::cout << "set adj " << attr_name << " to " << v << "\n";
4037                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4038                     int v = prefs_get_int_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4039                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4040                     gtk_toggle_action_set_active(toggle, v);
4041                     //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4042                 } else {
4043                     g_warning("Unknown widget type for preset: %s\n", attr_name);
4044                 }
4045             } else {
4046                 g_warning("Bad key found in a preset record: %s\n", attr_name);
4047             }
4048         }
4049         free(preset_path);
4050         g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4051     }
4056 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4058     {
4059         EgeAdjustmentAction* calligraphy_angle = 0;
4061         {
4062         /* Width */
4063         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4064         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4065         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4066                                                               _("Pen Width"), _("Width:"),
4067                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
4068                                                               "tools.calligraphic", "width", 15,
4069                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4070                                                               1, 100, 1.0, 0.0,
4071                                                               labels, values, G_N_ELEMENTS(labels),
4072                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
4073         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4074         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4075         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4076         }
4078         {
4079         /* Thinning */
4080             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4081             gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4082         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4083                                                               _("Stroke Thinning"), _("Thinning:"),
4084                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4085                                                               "tools.calligraphic", "thinning", 10,
4086                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4087                                                               -100, 100, 1, 0.1,
4088                                                               labels, values, G_N_ELEMENTS(labels),
4089                                                               sp_ddc_velthin_value_changed, 0.01, 0, 100);
4090         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4091         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4092         }
4094         {
4095         /* Angle */
4096         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4097         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4098         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4099                                                               _("Pen Angle"), _("Angle:"),
4100                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4101                                                               "tools.calligraphic", "angle", 30,
4102                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4103                                                               -90.0, 90.0, 1.0, 10.0,
4104                                                               labels, values, G_N_ELEMENTS(labels),
4105                                                               sp_ddc_angle_value_changed, 1, 0 );
4106         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4107         g_object_set_data( holder, "angle_action", eact );
4108         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4109         calligraphy_angle = eact;
4110         }
4112         {
4113         /* Fixation */
4114             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4115         gdouble values[] = {0, 20, 40, 60, 90, 100};
4116         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4117                                                               _("Fixation"), _("Fixation:"),
4118                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
4119                                                               "tools.calligraphic", "flatness", 90,
4120                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4121                                                               0.0, 100, 1.0, 10.0,
4122                                                               labels, values, G_N_ELEMENTS(labels),
4123                                                               sp_ddc_flatness_value_changed, 0.01, 0, 100 );
4124         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4125         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4126         }
4128         {
4129         /* Cap Rounding */
4130             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4131         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4132         // TRANSLATORS: "cap" means "end" (both start and finish) here
4133         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4134                                                               _("Cap rounding"), _("Caps:"),
4135                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4136                                                               "tools.calligraphic", "cap_rounding", 0.0,
4137                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4138                                                               0.0, 5.0, 0.01, 0.1,
4139                                                               labels, values, G_N_ELEMENTS(labels),
4140                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4141         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4142         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4143         }
4145         {
4146         /* Tremor */
4147             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4148         gdouble values[] = {0, 10, 20, 40, 60, 100};
4149         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4150                                                               _("Stroke Tremor"), _("Tremor:"),
4151                                                               _("Increase to make strokes rugged and trembling"),
4152                                                               "tools.calligraphic", "tremor", 0.0,
4153                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4154                                                               0.0, 100, 1, 0.0,
4155                                                               labels, values, G_N_ELEMENTS(labels),
4156                                                               sp_ddc_tremor_value_changed, 0.01, 0, 100 );
4158         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4159         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4160         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4161         }
4163         {
4164         /* Wiggle */
4165         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4166         gdouble values[] = {0, 20, 40, 60, 100};
4167         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4168                                                               _("Pen Wiggle"), _("Wiggle:"),
4169                                                               _("Increase to make the pen waver and wiggle"),
4170                                                               "tools.calligraphic", "wiggle", 0.0,
4171                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4172                                                               0.0, 100, 1, 0.0,
4173                                                               labels, values, G_N_ELEMENTS(labels),
4174                                                               sp_ddc_wiggle_value_changed, 0.01, 0, 100 );
4175         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4176         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4177         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4178         }
4180         {
4181         /* Mass */
4182             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4183         gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4184         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4185                                                               _("Pen Mass"), _("Mass:"),
4186                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
4187                                                               "tools.calligraphic", "mass", 2.0,
4188                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4189                                                               0.0, 100, 1, 0.0,
4190                                                               labels, values, G_N_ELEMENTS(labels),
4191                                                               sp_ddc_mass_value_changed, 0.01, 0, 100 );
4192         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4193         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4194         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4195         }
4198         /* Trace Background button */
4199         {
4200             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4201                                                           _("Trace Background"),
4202                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4203                                                           "trace_background",
4204                                                           Inkscape::ICON_SIZE_DECORATION );
4205             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4206             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4207             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
4208             g_object_set_data( holder, "tracebackground", act );
4209         }
4211         /* Use Pressure button */
4212         {
4213             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4214                                                           _("Pressure"),
4215                                                           _("Use the pressure of the input device to alter the width of the pen"),
4216                                                           "use_pressure",
4217                                                           Inkscape::ICON_SIZE_DECORATION );
4218             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4219             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4220             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
4221             g_object_set_data( holder, "usepressure", act );
4222         }
4224         /* Use Tilt button */
4225         {
4226             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4227                                                           _("Tilt"),
4228                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
4229                                                           "use_tilt",
4230                                                           Inkscape::ICON_SIZE_DECORATION );
4231             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4232             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4233             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4234             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4235             g_object_set_data( holder, "usetilt", act );
4236         }
4238         /*calligraphic profile */
4239         {
4240             GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
4241             EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Change calligraphic profile")), NULL, GTK_TREE_MODEL(model));
4242             ege_select_one_action_set_appearance (act1, "compact");
4243             g_object_set_data (holder, "profile_selector", act1 );
4245             sp_dcc_build_presets_list (holder);
4247             g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
4248             gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
4249         }
4250     }
4254 //########################
4255 //##    Circle / Arc    ##
4256 //########################
4258 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4260     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4261     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4263     if (v1 == 0 && v2 == 0) {
4264         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4265             gtk_action_set_sensitive( ocb, FALSE );
4266             gtk_action_set_sensitive( make_whole, FALSE );
4267         }
4268     } else {
4269         gtk_action_set_sensitive( ocb, TRUE );
4270         gtk_action_set_sensitive( make_whole, TRUE );
4271     }
4274 static void
4275 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4277     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4279     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4280         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
4281     }
4283     // quit if run by the attr_changed listener
4284     if (g_object_get_data( tbl, "freeze" )) {
4285         return;
4286     }
4288     // in turn, prevent listener from responding
4289     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4291     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4293     bool modmade = false;
4294     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4295          items != NULL;
4296          items = items->next)
4297     {
4298         SPItem *item = SP_ITEM(items->data);
4300         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4302             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4303             SPArc *arc = SP_ARC(item);
4305             if (!strcmp(value_name, "start"))
4306                 ge->start = (adj->value * M_PI)/ 180;
4307             else
4308                 ge->end = (adj->value * M_PI)/ 180;
4310             sp_genericellipse_normalize(ge);
4311             ((SPObject *)arc)->updateRepr();
4312             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4314             modmade = true;
4315         }
4316     }
4318     g_free(namespaced_name);
4320     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4322     sp_arctb_sensitivize( tbl, adj->value, other->value );
4324     if (modmade) {
4325         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4326                                    _("Arc: Change start/end"));
4327     }
4329     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4333 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
4335     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
4338 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4340     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
4344 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4346     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4347     gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
4348     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4349         prefs_set_int_attribute( "tools.eraser", "mode", eraserMode );
4350     }
4352     // only take action if run by the attr_changed listener
4353     if (!g_object_get_data( tbl, "freeze" )) {
4354         // in turn, prevent listener from responding
4355         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4357         if ( eraserMode != 0 ) {
4358         } else {
4359         }
4360         // TODO finish implementation
4362         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4363     }
4366 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4368     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4369     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4370         if ( ege_select_one_action_get_active( act ) != 0 ) {
4371             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
4372         } else {
4373             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
4374         }
4375     }
4377     // quit if run by the attr_changed listener
4378     if (g_object_get_data( tbl, "freeze" )) {
4379         return;
4380     }
4382     // in turn, prevent listener from responding
4383     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4385     bool modmade = false;
4387     if ( ege_select_one_action_get_active(act) != 0 ) {
4388         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4389              items != NULL;
4390              items = items->next)
4391         {
4392             if (SP_IS_ARC((SPItem *) items->data)) {
4393                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4394                 repr->setAttribute("sodipodi:open", "true");
4395                 SP_OBJECT((SPItem *) items->data)->updateRepr();
4396                 modmade = true;
4397             }
4398         }
4399     } else {
4400         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4401              items != NULL;
4402              items = items->next)
4403         {
4404             if (SP_IS_ARC((SPItem *) items->data))    {
4405                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4406                 repr->setAttribute("sodipodi:open", NULL);
4407                 SP_OBJECT((SPItem *) items->data)->updateRepr();
4408                 modmade = true;
4409             }
4410         }
4411     }
4413     if (modmade) {
4414         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4415                                    _("Arc: Change open/closed"));
4416     }
4418     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4421 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4423     GtkAdjustment *adj;
4424     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
4425     gtk_adjustment_set_value(adj, 0.0);
4426     gtk_adjustment_value_changed(adj);
4428     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
4429     gtk_adjustment_set_value(adj, 0.0);
4430     gtk_adjustment_value_changed(adj);
4432     spinbutton_defocus( GTK_OBJECT(obj) );
4435 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
4436                                       gchar const */*old_value*/, gchar const */*new_value*/,
4437                                       bool /*is_interactive*/, gpointer data)
4439     GObject *tbl = G_OBJECT(data);
4441     // quit if run by the _changed callbacks
4442     if (g_object_get_data( tbl, "freeze" )) {
4443         return;
4444     }
4446     // in turn, prevent callbacks from responding
4447     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4449     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4450     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4452     GtkAdjustment *adj1,*adj2;
4453     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4454     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4455     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4456     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4458     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4460     char const *openstr = NULL;
4461     openstr = repr->attribute("sodipodi:open");
4462     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4464     if (openstr) {
4465         ege_select_one_action_set_active( ocb, 1 );
4466     } else {
4467         ege_select_one_action_set_active( ocb, 0 );
4468     }
4470     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4473 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4474     NULL, /* child_added */
4475     NULL, /* child_removed */
4476     arc_tb_event_attr_changed,
4477     NULL, /* content_changed */
4478     NULL  /* order_changed */
4479 };
4482 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4484     int n_selected = 0;
4485     Inkscape::XML::Node *repr = NULL;
4487     purge_repr_listener( tbl, tbl );
4489     for (GSList const *items = selection->itemList();
4490          items != NULL;
4491          items = items->next)
4492     {
4493         if (SP_IS_ARC((SPItem *) items->data)) {
4494             n_selected++;
4495             repr = SP_OBJECT_REPR((SPItem *) items->data);
4496         }
4497     }
4499     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4501     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4502     if (n_selected == 0) {
4503         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4504     } else if (n_selected == 1) {
4505         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4506         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4508         if (repr) {
4509             g_object_set_data( tbl, "repr", repr );
4510             Inkscape::GC::anchor(repr);
4511             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4512             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4513         }
4514     } else {
4515         // FIXME: implement averaging of all parameters for multiple selected
4516         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4517         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4518         sp_arctb_sensitivize( tbl, 1, 0 );
4519     }
4523 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4525     EgeAdjustmentAction* eact = 0;
4526     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
4529     {
4530         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4531         ege_output_action_set_use_markup( act, TRUE );
4532         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4533         g_object_set_data( holder, "mode_action", act );
4534     }
4536     /* Start */
4537     {
4538         eact = create_adjustment_action( "ArcStartAction",
4539                                          _("Start"), _("Start:"),
4540                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
4541                                          "tools.shapes.arc", "start", 0.0,
4542                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4543                                          -360.0, 360.0, 1.0, 10.0,
4544                                          0, 0, 0,
4545                                          sp_arctb_start_value_changed);
4546         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4547     }
4549     /* End */
4550     {
4551         eact = create_adjustment_action( "ArcEndAction",
4552                                          _("End"), _("End:"),
4553                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
4554                                          "tools.shapes.arc", "end", 0.0,
4555                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4556                                          -360.0, 360.0, 1.0, 10.0,
4557                                          0, 0, 0,
4558                                          sp_arctb_end_value_changed);
4559         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4560     }
4562     /* Segments / Pie checkbox */
4563     {
4564         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4566         GtkTreeIter iter;
4567         gtk_list_store_append( model, &iter );
4568         gtk_list_store_set( model, &iter,
4569                             0, _("Closed arc"),
4570                             1, _("Switch to segment (closed shape with two radii)"),
4571                             2, "circle_closed_arc",
4572                             -1 );
4574         gtk_list_store_append( model, &iter );
4575         gtk_list_store_set( model, &iter,
4576                             0, _("Open Arc"),
4577                             1, _("Switch to arc (unclosed shape)"),
4578                             2, "circle_open_arc",
4579                             -1 );
4581         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4582         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4583         g_object_set_data( holder, "open_action", act );
4585         ege_select_one_action_set_appearance( act, "full" );
4586         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4587         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4588         ege_select_one_action_set_icon_column( act, 2 );
4589         ege_select_one_action_set_icon_size( act, secondarySize );
4590         ege_select_one_action_set_tooltip_column( act, 1  );
4592         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4593         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4594         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4595         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4596     }
4598     /* Make Whole */
4599     {
4600         InkAction* inky = ink_action_new( "ArcResetAction",
4601                                           _("Make whole"),
4602                                           _("Make the shape a whole ellipse, not arc or segment"),
4603                                           "reset_circle",
4604                                           secondarySize );
4605         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4606         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4607         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4608         g_object_set_data( holder, "make_whole", inky );
4609     }
4611     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4612     // sensitivize make whole and open checkbox
4613     {
4614         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4615         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4616         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4617     }
4620     sigc::connection *connection = new sigc::connection(
4621         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4622         );
4623     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4624     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4630 // toggle button callbacks and updaters
4632 //########################
4633 //##      Dropper       ##
4634 //########################
4636 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4637     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4638     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4639     if ( set_action ) {
4640         if ( gtk_toggle_action_get_active( act ) ) {
4641             gtk_action_set_sensitive( set_action, TRUE );
4642         } else {
4643             gtk_action_set_sensitive( set_action, FALSE );
4644         }
4645     }
4647     spinbutton_defocus(GTK_OBJECT(tbl));
4650 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4651     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4652     spinbutton_defocus(GTK_OBJECT(tbl));
4656 /**
4657  * Dropper auxiliary toolbar construction and setup.
4658  *
4659  * TODO: Would like to add swatch of current color.
4660  * TODO: Add queue of last 5 or so colors selected with new swatches so that
4661  *       can drag and drop places. Will provide a nice mixing palette.
4662  */
4663 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4665     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4667     {
4668         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4669         ege_output_action_set_use_markup( act, TRUE );
4670         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4671     }
4673     {
4674         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4675                                                       _("Pick opacity"),
4676                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4677                                                       NULL,
4678                                                       Inkscape::ICON_SIZE_DECORATION );
4679         g_object_set( act, "short_label", _("Pick"), NULL );
4680         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4681         g_object_set_data( holder, "pick_action", act );
4682         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4683         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4684     }
4686     {
4687         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4688                                                       _("Assign opacity"),
4689                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4690                                                       NULL,
4691                                                       Inkscape::ICON_SIZE_DECORATION );
4692         g_object_set( act, "short_label", _("Assign"), NULL );
4693         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4694         g_object_set_data( holder, "set_action", act );
4695         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4696         // make sure it's disabled if we're not picking alpha
4697         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4698         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4699     }
4704 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4706     {
4707         /* Width */
4708         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4709         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4710         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
4711                                                               _("Pen Width"), _("Width:"),
4712                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
4713                                                               "tools.eraser", "width", 15,
4714                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
4715                                                               1, 100, 1.0, 0.0,
4716                                                               labels, values, G_N_ELEMENTS(labels),
4717                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
4718         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4719         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4720         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4721     }
4723     {
4724         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4726         GtkTreeIter iter;
4727         gtk_list_store_append( model, &iter );
4728         gtk_list_store_set( model, &iter,
4729                             0, _("Delete"),
4730                             1, _("Delete objects touched by the eraser"),
4731                             2, "delete_object",
4732                             -1 );
4734         gtk_list_store_append( model, &iter );
4735         gtk_list_store_set( model, &iter,
4736                             0, _("Cut"),
4737                             1, _("Cut out from objects"),
4738                             2, "difference",
4739                             -1 );
4741         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4742         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4743         g_object_set_data( holder, "eraser_mode_action", act );
4745         ege_select_one_action_set_appearance( act, "full" );
4746         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4747         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4748         ege_select_one_action_set_icon_column( act, 2 );
4749         ege_select_one_action_set_tooltip_column( act, 1  );
4751         gint eraserMode = (prefs_get_int_attribute("tools.eraser", "mode", 0) != 0) ? 1 : 0;
4752         ege_select_one_action_set_active( act, eraserMode );
4753         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
4754     }
4758 //########################
4759 //##    Text Toolbox    ##
4760 //########################
4761 /*
4762 static void
4763 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
4765     //Call back for letter sizing spinbutton
4768 static void
4769 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
4771     //Call back for line height spinbutton
4774 static void
4775 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4777     //Call back for horizontal kerning spinbutton
4780 static void
4781 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4783     //Call back for vertical kerning spinbutton
4786 static void
4787 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
4789     //Call back for letter rotation spinbutton
4790 }*/
4792 namespace {
4794 bool popdown_visible = false;
4795 bool popdown_hasfocus = false;
4797 void
4798 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
4800     SPStyle *query =
4801         sp_style_new (SP_ACTIVE_DOCUMENT);
4803 //    int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4805     int result_family =
4806         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4808     int result_style =
4809         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4811     int result_numbers =
4812         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4814     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4816     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4817     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
4818         // there are no texts in selection, read from prefs
4819  
4820         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
4821         if (repr) {
4822             sp_style_read_from_repr (query, repr);
4823             if (g_object_get_data(tbl, "text_style_from_prefs")) {
4824                 // do not reset the toolbar style from prefs if we already did it last time
4825                 sp_style_unref(query);
4826                 return;
4827             }
4828             g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
4829         } else {
4830             sp_style_unref(query);
4831             return;
4832         }
4833     } else {
4834         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
4835     }
4837     if (query->text)
4838     {
4839         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4840             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4841             gtk_entry_set_text (GTK_ENTRY (entry), "");
4843         } else if (query->text->font_specification.value || query->text->font_family.value) {
4845             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4847             // Get the font that corresponds
4848             Glib::ustring familyName;
4850             font_instance * font = font_factory::Default()->FaceFromStyle(query);
4851             if (font) {
4852                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4853                 font->Unref();
4854                 font = NULL;
4855             }
4857             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4859             Gtk::TreePath path;
4860             try {
4861                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4862             } catch (...) {
4863                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4864                 sp_style_unref(query);
4865                 return;
4866             }
4868             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4869             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4871             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4873             gtk_tree_selection_select_path (tselection, path.gobj());
4874             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4876             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4877         }
4879         //Size
4880         GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4881         char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4882         g_object_set_data (tbl, "size-block", gpointer(1));
4883         gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4884         g_object_set_data (tbl, "size-block", gpointer(0));
4885         free (str);
4887         //Anchor
4888         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4889         {
4890             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4891             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4892             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4893             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4894         }
4895         else
4896         {
4897             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4898             {
4899                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4900                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4901                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4902                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4903             }
4904             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4905             {
4906                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4907                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4908                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4909                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4910             }
4911             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4912             {
4913                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4914                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4915                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4916                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4917             }
4918         }
4920         //Style
4921         {
4922             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4924             gboolean active = gtk_toggle_button_get_active (button);
4925             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4927             if (active != check)
4928             {
4929                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4930                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4931                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4932             }
4933         }
4935         {
4936             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4938             gboolean active = gtk_toggle_button_get_active (button);
4939             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4941             if (active != check)
4942             {
4943                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4944                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4945                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4946             }
4947         }
4949         //Orientation
4950         //locking both buttons, changing one affect all group (both)
4951         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4952         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4954         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4955         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4957         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4958         {
4959             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4960         }
4961         else
4962         {
4963             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4964         }
4965         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4966         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4967     }
4969     sp_style_unref(query);
4972 void
4973 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4975     sp_text_toolbox_selection_changed (selection, tbl);
4978 void
4979 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4981     sp_text_toolbox_selection_changed (NULL, tbl);
4984 void
4985 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
4986                                 GObject             *tbl)
4988     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
4989     GtkTreeModel *model = 0;
4990     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4991     GtkTreeIter   iter;
4992     char         *family = 0;
4994     gdk_pointer_ungrab (GDK_CURRENT_TIME);
4995     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4997     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4998         return;
4999     }
5001     gtk_tree_model_get (model, &iter, 0, &family, -1);
5003     if (g_object_get_data (G_OBJECT (selection), "block"))
5004     {
5005         gtk_entry_set_text (GTK_ENTRY (entry), family);
5006         return;
5007     }
5009     gtk_entry_set_text (GTK_ENTRY (entry), family);
5011     SPStyle *query =
5012         sp_style_new (SP_ACTIVE_DOCUMENT);
5014     int result_fontspec =
5015         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5017     //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5019     SPCSSAttr *css = sp_repr_css_attr_new ();
5022     // First try to get the font spec from the stored value
5023     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
5025     if (fontSpec.empty()) {
5026         // Construct a new font specification if it does not yet exist
5027         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5028         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5029         fontFromStyle->Unref();
5030     }
5032     if (!fontSpec.empty()) {
5033         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
5034         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
5035             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
5036             if (font) {
5037                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5039                 // Set all the these just in case they were altered when finding the best
5040                 // match for the new family and old style...
5042                 gchar c[256];
5044                 font->Family(c, 256);
5045                 sp_repr_css_set_property (css, "font-family", c);
5047                 font->Attribute( "weight", c, 256);
5048                 sp_repr_css_set_property (css, "font-weight", c);
5050                 font->Attribute("style", c, 256);
5051                 sp_repr_css_set_property (css, "font-style", c);
5053                 font->Attribute("stretch", c, 256);
5054                 sp_repr_css_set_property (css, "font-stretch", c);
5056                 font->Attribute("variant", c, 256);
5057                 sp_repr_css_set_property (css, "font-variant", c);
5059                 font->Unref();
5060             }
5061         }
5062     }
5064     // If querying returned nothing, set the default style of the tool (for new texts)
5065     if (result_fontspec == QUERY_STYLE_NOTHING)
5066     {
5067         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5068         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
5069     }
5070     else
5071     {
5072         sp_desktop_set_style (desktop, css, true, true);
5073     }
5075     sp_style_unref(query);
5077     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5078                                    _("Text: Change font family"));
5079     sp_repr_css_attr_unref (css);
5080     free (family);
5081     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5083     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5086 void
5087 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
5088                                        GObject      *tbl)
5090     const char *family = gtk_entry_get_text (entry);
5092     try {
5093         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
5094         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5095         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5096         gtk_tree_selection_select_path (selection, path.gobj());
5097         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5098         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5099     } catch (...) {
5100         if (family && strlen (family))
5101         {
5102             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5103         }
5104     }
5107 void
5108 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
5109                                    gpointer          data)
5111     if (g_object_get_data (G_OBJECT (button), "block")) return;
5112     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
5113     int prop = GPOINTER_TO_INT(data);
5115     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5116     SPCSSAttr *css = sp_repr_css_attr_new ();
5118     switch (prop)
5119     {
5120         case 0:
5121         {
5122             sp_repr_css_set_property (css, "text-anchor", "start");
5123             sp_repr_css_set_property (css, "text-align", "start");
5124             break;
5125         }
5126         case 1:
5127         {
5128             sp_repr_css_set_property (css, "text-anchor", "middle");
5129             sp_repr_css_set_property (css, "text-align", "center");
5130             break;
5131         }
5133         case 2:
5134         {
5135             sp_repr_css_set_property (css, "text-anchor", "end");
5136             sp_repr_css_set_property (css, "text-align", "end");
5137             break;
5138         }
5140         case 3:
5141         {
5142             sp_repr_css_set_property (css, "text-anchor", "start");
5143             sp_repr_css_set_property (css, "text-align", "justify");
5144             break;
5145         }
5146     }
5148     SPStyle *query =
5149         sp_style_new (SP_ACTIVE_DOCUMENT);
5150     int result_numbers =
5151         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5153     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5154     if (result_numbers == QUERY_STYLE_NOTHING)
5155     {
5156         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5157     }
5159     sp_style_unref(query);
5161     sp_desktop_set_style (desktop, css, true, true);
5162     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5163                                    _("Text: Change alignment"));
5164     sp_repr_css_attr_unref (css);
5166     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5169 void
5170 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
5171                                gpointer          data)
5173     if (g_object_get_data (G_OBJECT (button), "block")) return;
5175     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5176     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5177     int          prop       = GPOINTER_TO_INT(data);
5178     bool         active     = gtk_toggle_button_get_active (button);
5180     SPStyle *query =
5181         sp_style_new (SP_ACTIVE_DOCUMENT);
5183     int result_fontspec =
5184         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5186     //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5187     //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5188     //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5190     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
5191     Glib::ustring newFontSpec = "";
5193     if (fontSpec.empty()) {
5194         // Construct a new font specification if it does not yet exist
5195         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5196         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5197         fontFromStyle->Unref();
5198     }
5200     switch (prop)
5201     {
5202         case 0:
5203         {
5204             if (!fontSpec.empty()) {
5205                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
5206             }
5207             if (fontSpec != newFontSpec) {
5208                 // Don't even set the bold if the font didn't exist on the system
5209                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
5210             }
5211             break;
5212         }
5214         case 1:
5215         {
5216             if (!fontSpec.empty()) {
5217                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
5218             }
5219             if (fontSpec != newFontSpec) {
5220                 // Don't even set the italic if the font didn't exist on the system
5221                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
5222             }
5223             break;
5224         }
5225     }
5227     if (!newFontSpec.empty()) {
5228         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5229     }
5231     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5232     if (result_fontspec == QUERY_STYLE_NOTHING)
5233     {
5234         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5235     }
5237     sp_style_unref(query);
5239     sp_desktop_set_style (desktop, css, true, true);
5240     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5241                                    _("Text: Change font style"));
5242     sp_repr_css_attr_unref (css);
5244     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5247 void
5248 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
5249                                      gpointer         data)
5251     if (g_object_get_data (G_OBJECT (button), "block")) {
5252         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5253         return;
5254     }
5256     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5257     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5258     int          prop       = GPOINTER_TO_INT(data);
5260     switch (prop)
5261     {
5262         case 0:
5263         {
5264             sp_repr_css_set_property (css, "writing-mode", "lr");
5265             break;
5266         }
5268         case 1:
5269         {
5270             sp_repr_css_set_property (css, "writing-mode", "tb");
5271             break;
5272         }
5273     }
5275     SPStyle *query =
5276         sp_style_new (SP_ACTIVE_DOCUMENT);
5277     int result_numbers =
5278         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5280     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5281     if (result_numbers == QUERY_STYLE_NOTHING)
5282     {
5283         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5284     }
5286     sp_desktop_set_style (desktop, css, true, true);
5287     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5288                                    _("Text: Change orientation"));
5289     sp_repr_css_attr_unref (css);
5291     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5294 gboolean
5295 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5297     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5298     if (!desktop) return FALSE;
5300     switch (get_group0_keyval (event)) {
5301         case GDK_Escape: // defocus
5302             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5303             sp_text_toolbox_selection_changed (NULL, tbl); // update
5304             return TRUE; // I consumed the event
5305             break;
5306     }
5307     return FALSE;
5310 gboolean
5311 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5313     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5314     if (!desktop) return FALSE;
5316     switch (get_group0_keyval (event)) {
5317         case GDK_KP_Enter:
5318         case GDK_Return:
5319         case GDK_Escape: // defocus
5320             gtk_widget_hide (w);
5321             popdown_visible = false;
5322             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5323             return TRUE; // I consumed the event
5324             break;
5325         case GDK_w:
5326         case GDK_W:
5327             if (event->state & GDK_CONTROL_MASK) {
5328                 gtk_widget_hide (w);
5329                 popdown_visible = false;
5330                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5331                 return TRUE; // I consumed the event
5332             }
5333             break;
5334     }
5335     return FALSE;
5339 void
5340 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
5341                                GObject     *tbl)
5343     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5345     if (g_object_get_data (tbl, "size-block")) return;
5347     // If this is not from selecting a size in the list (in which case get_active will give the
5348     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5349     // process this event. This fixes GTK's stupid insistence on sending an activate change every
5350     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5351     if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5352         return;
5354     gchar *endptr;
5355     gdouble value = -1;
5356     char *text = gtk_combo_box_get_active_text (cbox);
5357     if (text) {
5358         value = g_strtod (text, &endptr);
5359         if (endptr == text) // conversion failed, non-numeric input
5360             value = -1;
5361         free (text);
5362     }
5363     if (value <= 0) {
5364         return; // could not parse value
5365     }
5367     SPCSSAttr *css = sp_repr_css_attr_new ();
5368     Inkscape::CSSOStringStream osfs;
5369     osfs << value;
5370     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5372     SPStyle *query =
5373         sp_style_new (SP_ACTIVE_DOCUMENT);
5374     int result_numbers =
5375         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5377     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5378     if (result_numbers == QUERY_STYLE_NOTHING)
5379     {
5380         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5381     }
5383     sp_style_unref(query);
5385     sp_desktop_set_style (desktop, css, true, true);
5386     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5387                                    _("Text: Change font size"));
5388     sp_repr_css_attr_unref (css);
5390     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5393 gboolean
5394 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5396     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5397     if (!desktop) return FALSE;
5399     if (!g_object_get_data (tbl, "esc-pressed")) {
5400         g_object_set_data (tbl, "enter-pressed", gpointer(1));
5401         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5402         sp_text_toolbox_size_changed (cbox, tbl);
5403         g_object_set_data (tbl, "enter-pressed", gpointer(0));
5404     }
5405     return FALSE; // I consumed the event
5409 gboolean
5410 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5412     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5413     if (!desktop) return FALSE;
5415     switch (get_group0_keyval (event)) {
5416         case GDK_Escape: // defocus
5417             g_object_set_data (tbl, "esc-pressed", gpointer(1));
5418             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5419             g_object_set_data (tbl, "esc-pressed", gpointer(0));
5420             return TRUE; // I consumed the event
5421             break;
5422         case GDK_Return: // defocus
5423         case GDK_KP_Enter:
5424             g_object_set_data (tbl, "enter-pressed", gpointer(1));
5425             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5426             sp_text_toolbox_size_changed (cbox, tbl);
5427             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5428             g_object_set_data (tbl, "enter-pressed", gpointer(0));
5429             return TRUE; // I consumed the event
5430             break;
5431     }
5432     return FALSE;
5435 void
5436 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
5437                                          GObject            *tbl)
5439     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
5440     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5441     int x, y;
5443     if (!popdown_visible)
5444     {
5445         gdk_window_get_origin (widget->window, &x, &y);
5446         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
5447         gtk_widget_show_all (popdown);
5448         //sp_transientize (popdown);
5450         gdk_pointer_grab (widget->window, TRUE,
5451                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
5452                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
5453                                         GDK_POINTER_MOTION_MASK),
5454                           NULL, NULL, GDK_CURRENT_TIME);
5456         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
5458         popdown_visible = true;
5459     }
5460     else
5461     {
5462         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5463         gdk_pointer_ungrab (GDK_CURRENT_TIME);
5464         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5465         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5466         gtk_widget_hide (popdown);
5467         popdown_visible = false;
5468     }
5471 gboolean
5472 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
5473                                  GdkEventFocus    */*event*/,
5474                                  GObject          */*tbl*/)
5476     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
5477     return FALSE;
5480 gboolean
5481 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
5482                                    GdkEventFocus    */*event*/,
5483                                    GObject          */*tbl*/)
5485     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5487     if (popdown_hasfocus) {
5488         gtk_widget_hide (popdown);
5489         popdown_hasfocus = false;
5490         popdown_visible = false;
5491         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5492         return TRUE;
5493     }
5494     return FALSE;
5497 gboolean
5498 sp_text_toolbox_popdown_focus_in (GtkWidget        */*popdown*/,
5499                                    GdkEventFocus    */*event*/,
5500                                    GObject          */*tbl*/)
5502     popdown_hasfocus = true;
5503     return TRUE;
5507 void
5508 cell_data_func  (GtkTreeViewColumn */*column*/,
5509                  GtkCellRenderer   *cell,
5510                  GtkTreeModel      *tree_model,
5511                  GtkTreeIter       *iter,
5512                  gpointer           /*data*/)
5514     char        *family,
5515         *family_escaped,
5516         *sample_escaped;
5518     static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
5520     gtk_tree_model_get (tree_model, iter, 0, &family, -1);
5522     family_escaped = g_markup_escape_text (family, -1);
5523     sample_escaped = g_markup_escape_text (sample, -1);
5525     std::stringstream markup;
5526     markup << family_escaped << "  <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
5527     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
5529     free (family);
5530     free (family_escaped);
5531     free (sample_escaped);
5534 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
5535     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
5536     if (completion) {
5537         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
5538         g_object_unref (completion);
5539     }
5542 GtkWidget*
5543 sp_text_toolbox_new (SPDesktop *desktop)
5545     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
5546     GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
5548     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
5549     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
5551     GtkTooltips *tt = gtk_tooltips_new();
5552     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
5554     ////////////Family
5555     //Window
5556     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5557     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
5559     //Entry
5560     GtkWidget           *entry = gtk_entry_new ();
5561     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
5562     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
5563     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
5564     gtk_entry_completion_set_text_column (completion, 0);
5565     gtk_entry_completion_set_minimum_key_length (completion, 1);
5566     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
5567     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
5568     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
5569     gtk_toolbar_append_widget( tbl, entry, "", "" );
5570     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
5572     //Button
5573     GtkWidget   *button = gtk_button_new ();
5574     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
5575     gtk_toolbar_append_widget( tbl, button, "", "");
5577     //Popdown
5578     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
5579     GtkWidget           *treeview = gtk_tree_view_new ();
5581     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
5582     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
5583     gtk_tree_view_column_pack_start (column, cell, FALSE);
5584     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
5585     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5586     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5588     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5589     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5590     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5592     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5594     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5595     gtk_container_add (GTK_CONTAINER (sw), treeview);
5597     gtk_container_add (GTK_CONTAINER (window), sw);
5598     gtk_widget_set_size_request (window, 300, 450);
5600     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5601     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5602     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5604     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5606     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5607     g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5608     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5610     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5611     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5613     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5614     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5615     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5616     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5617     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5619     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
5620     GtkWidget *box = gtk_event_box_new ();
5621     gtk_container_add (GTK_CONTAINER (box), image);
5622     gtk_toolbar_append_widget( tbl, box, "", "");
5623     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5624     GtkTooltips *tooltips = gtk_tooltips_new ();
5625     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5626     gtk_widget_hide (GTK_WIDGET (box));
5627     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
5629     ////////////Size
5630     const char *sizes[] = {
5631         "4", "6", "8", "9", "10", "11", "12", "13", "14",
5632         "16", "18", "20", "22", "24", "28",
5633         "32", "36", "40", "48", "56", "64", "72", "144"
5634     };
5636     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
5637     for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
5638     gtk_widget_set_size_request (cbox, 80, -1);
5639     gtk_toolbar_append_widget( tbl, cbox, "", "");
5640     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
5641     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
5642     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
5643     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
5645     ////////////Text anchor
5646     GtkWidget *group   = gtk_radio_button_new (NULL);
5647     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
5648     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
5650     // left
5651     GtkWidget *rbutton = group;
5652     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5653     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
5654     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5656     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5657     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
5658     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
5659     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
5661     // center
5662     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5663     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5664     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
5665     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5667     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5668     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
5669     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5670     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5672     // right
5673     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5674     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5675     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
5676     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5678     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5679     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
5680     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5681     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5683     // fill
5684     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5685     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5686     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
5687     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5689     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5690     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
5691     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
5692     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
5694     gtk_toolbar_append_widget( tbl, row, "", "");
5696     //spacer
5697     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5699     ////////////Text style
5700     row = gtk_hbox_new (FALSE, 4);
5702     // bold
5703     rbutton = gtk_toggle_button_new ();
5704     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5705     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
5706     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5707     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
5709     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5710     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
5711     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
5713     // italic
5714     rbutton = gtk_toggle_button_new ();
5715     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5716     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
5717     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5718     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
5720     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5721     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
5722     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
5724     gtk_toolbar_append_widget( tbl, row, "", "");
5726     //spacer
5727     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5729     ////////////Text orientation
5730     group   = gtk_radio_button_new (NULL);
5731     row     = gtk_hbox_new (FALSE, 4);
5732     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
5734     // horizontal
5735     rbutton = group;
5736     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5737     gtk_container_add           (GTK_CONTAINER (rbutton),
5738                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
5739     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5740     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
5742     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5743     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
5744     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
5746     // vertical
5747     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5748     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5749     gtk_container_add           (GTK_CONTAINER (rbutton),
5750                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
5751     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5752     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
5754     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5755     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
5756     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
5757     gtk_toolbar_append_widget( tbl, row, "", "" );
5760     //watch selection
5761     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
5763     sigc::connection *c_selection_changed =
5764         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5765                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
5766     pool->add_connection ("selection-changed", c_selection_changed);
5768     sigc::connection *c_selection_modified =
5769         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5770                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
5771     pool->add_connection ("selection-modified", c_selection_modified);
5773     sigc::connection *c_subselection_changed =
5774         new sigc::connection (desktop->connectToolSubselectionChanged
5775                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
5776     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
5778     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
5781     gtk_widget_show_all( GTK_WIDGET(tbl) );
5783     return GTK_WIDGET(tbl);
5784 } // end of sp_text_toolbox_new()
5786 }//<unnamed> namespace
5789 //#########################
5790 //##      Connector      ##
5791 //#########################
5793 static void sp_connector_path_set_avoid(void)
5795     cc_selection_set_avoid(true);
5799 static void sp_connector_path_set_ignore(void)
5801     cc_selection_set_avoid(false);
5806 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
5808     // quit if run by the _changed callbacks
5809     if (g_object_get_data( tbl, "freeze" )) {
5810         return;
5811     }
5813     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5814     SPDocument *doc = sp_desktop_document(desktop);
5816     if (!sp_document_get_undo_sensitive(doc))
5817     {
5818         return;
5819     }
5821     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5823     if ( repr->attribute("inkscape:connector-spacing") ) {
5824         gdouble priorValue = gtk_adjustment_get_value(adj);
5825         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
5826         if ( priorValue == gtk_adjustment_get_value(adj) ) {
5827             return;
5828         }
5829     } else if ( adj->value == defaultConnSpacing ) {
5830         return;
5831     }
5833     // in turn, prevent callbacks from responding
5834     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5836     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
5837     SP_OBJECT(desktop->namedview)->updateRepr();
5839     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
5840     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
5841         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
5842         NR::Matrix m = NR::identity();
5843         avoid_item_move(&m, item);
5844     }
5846     if (items) {
5847         g_slist_free(items);
5848     }
5850     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
5851             _("Change connector spacing"));
5853     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5855     spinbutton_defocus(GTK_OBJECT(tbl));
5858 static void sp_connector_graph_layout(void)
5860     if (!SP_ACTIVE_DESKTOP) return;
5862     // hack for clones, see comment in align-and-distribute.cpp
5863     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5864     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5866     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5868     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5870     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5873 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5875     if ( gtk_toggle_action_get_active( act ) ) {
5876         prefs_set_string_attribute("tools.connector", "directedlayout",
5877                 "true");
5878     } else {
5879         prefs_set_string_attribute("tools.connector", "directedlayout",
5880                 "false");
5881     }
5884 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5886     if ( gtk_toggle_action_get_active( act ) ) {
5887         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5888                 "true");
5889     } else {
5890         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5891                 "false");
5892     }
5896 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5898     prefs_set_double_attribute("tools.connector", "length", adj->value);
5899     spinbutton_defocus(GTK_OBJECT(tbl));
5902 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5903                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5904                                             bool /*is_interactive*/, gpointer data)
5906     GtkWidget *tbl = GTK_WIDGET(data);
5908     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5909         return;
5910     }
5911     if (strcmp(name, "inkscape:connector-spacing") != 0) {
5912         return;
5913     }
5915     GtkAdjustment *adj = (GtkAdjustment*)
5916             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5917     gdouble spacing = defaultConnSpacing;
5918     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5920     gtk_adjustment_set_value(adj, spacing);
5924 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5925     NULL, /* child_added */
5926     NULL, /* child_removed */
5927     connector_tb_event_attr_changed,
5928     NULL, /* content_changed */
5929     NULL  /* order_changed */
5930 };
5933 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5935     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
5937     {
5938         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5939                                           _("Avoid"),
5940                                           _("Make connectors avoid selected objects"),
5941                                           "connector_avoid",
5942                                           secondarySize );
5943         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5944         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5945     }
5947     {
5948         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5949                                           _("Ignore"),
5950                                           _("Make connectors ignore selected objects"),
5951                                           "connector_ignore",
5952                                           secondarySize );
5953         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5954         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5955     }
5957     EgeAdjustmentAction* eact = 0;
5959     // Spacing spinbox
5960     eact = create_adjustment_action( "ConnectorSpacingAction",
5961                                      _("Connector Spacing"), _("Spacing:"),
5962                                      _("The amount of space left around objects by auto-routing connectors"),
5963                                      "tools.connector", "spacing", defaultConnSpacing,
5964                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5965                                      0, 100, 1.0, 10.0,
5966                                      0, 0, 0,
5967                                      connector_spacing_changed, 1, 0 );
5968     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5970     // Graph (connector network) layout
5971     {
5972         InkAction* inky = ink_action_new( "ConnectorGraphAction",
5973                                           _("Graph"),
5974                                           _("Nicely arrange selected connector network"),
5975                                           "graph_layout",
5976                                           secondarySize );
5977         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5978         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5979     }
5981     // Default connector length spinbox
5982     eact = create_adjustment_action( "ConnectorLengthAction",
5983                                      _("Connector Length"), _("Length:"),
5984                                      _("Ideal length for connectors when layout is applied"),
5985                                      "tools.connector", "length", 100,
5986                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5987                                      10, 1000, 10.0, 100.0,
5988                                      0, 0, 0,
5989                                      connector_length_changed, 1, 0 );
5990     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5993     // Directed edges toggle button
5994     {
5995         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5996                                                       _("Downwards"),
5997                                                       _("Make connectors with end-markers (arrows) point downwards"),
5998                                                       "directed_graph",
5999                                                       Inkscape::ICON_SIZE_DECORATION );
6000         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6002         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
6003         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6004                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6006         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
6007     }
6009     // Avoid overlaps toggle button
6010     {
6011         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
6012                                                       _("Remove overlaps"),
6013                                                       _("Do not allow overlapping shapes"),
6014                                                       "remove_overlaps",
6015                                                       Inkscape::ICON_SIZE_DECORATION );
6016         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6018         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
6019         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6020                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6022         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
6023     }
6025     // Code to watch for changes to the connector-spacing attribute in
6026     // the XML.
6027     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6028     g_assert(repr != NULL);
6030     purge_repr_listener( holder, holder );
6032     if (repr) {
6033         g_object_set_data( holder, "repr", repr );
6034         Inkscape::GC::anchor(repr);
6035         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
6036         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
6037     }
6038 } // end of sp_connector_toolbox_prep()
6041 //#########################
6042 //##     Paintbucket     ##
6043 //#########################
6045 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
6047     gint channels = ege_select_one_action_get_active( act );
6048     flood_channels_set_channels( channels );
6051 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
6053     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
6056 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
6058     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
6061 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
6063     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
6064     SPUnit const *unit = tracker->getActiveUnit();
6066     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
6068     prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
6071 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
6073     // FIXME: make defaults settable via Inkscape Options
6074     struct KeyValue {
6075         char const *key;
6076         double value;
6077     } const key_values[] = {
6078         {"threshold", 15},
6079         {"offset", 0.0}
6080     };
6082     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
6083         KeyValue const &kv = key_values[i];
6084         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
6085         if ( adj ) {
6086             gtk_adjustment_set_value(adj, kv.value);
6087         }
6088     }
6090     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
6091     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
6092     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
6093     ege_select_one_action_set_active( autogap_action, 0 );
6096 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6098     EgeAdjustmentAction* eact = 0;
6100     {
6101         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6103         GList* items = 0;
6104         gint count = 0;
6105         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
6106         {
6107             GtkTreeIter iter;
6108             gtk_list_store_append( model, &iter );
6109             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6110             count++;
6111         }
6112         g_list_free( items );
6113         items = 0;
6114         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
6115         g_object_set( act1, "short_label", _("Fill by:"), NULL );
6116         ege_select_one_action_set_appearance( act1, "compact" );
6117         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
6118         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
6119         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
6120         g_object_set_data( holder, "channels_action", act1 );
6121     }
6123     // Spacing spinbox
6124     {
6125         eact = create_adjustment_action(
6126             "ThresholdAction",
6127             _("Fill Threshold"), _("Threshold:"),
6128             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
6129             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
6130             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
6131             0, 0, 0,
6132             paintbucket_threshold_changed, 1, 0 );
6134         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6135         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6136     }
6138     // Create the units menu.
6139     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
6140     const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
6141     if (stored_unit)
6142         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
6143     g_object_set_data( holder, "tracker", tracker );
6144     {
6145         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
6146         gtk_action_group_add_action( mainActions, act );
6147     }
6149     // Offset spinbox
6150     {
6151         eact = create_adjustment_action(
6152             "OffsetAction",
6153             _("Grow/shrink by"), _("Grow/shrink by:"),
6154             _("The amount to grow (positive) or shrink (negative) the created fill path"),
6155             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
6156             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
6157             0, 0, 0,
6158             paintbucket_offset_changed, 1, 2);
6159         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
6161         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6162     }
6164     /* Auto Gap */
6165     {
6166         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6168         GList* items = 0;
6169         gint count = 0;
6170         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
6171         {
6172             GtkTreeIter iter;
6173             gtk_list_store_append( model, &iter );
6174             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6175             count++;
6176         }
6177         g_list_free( items );
6178         items = 0;
6179         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
6180         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
6181         ege_select_one_action_set_appearance( act2, "compact" );
6182         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
6183         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
6184         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
6185         g_object_set_data( holder, "autogap_action", act2 );
6186     }
6188     /* Reset */
6189     {
6190         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
6191                                           _("Defaults"),
6192                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
6193                                           GTK_STOCK_CLEAR );
6194         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
6195         gtk_action_group_add_action( mainActions, act );
6196         gtk_action_set_sensitive( act, TRUE );
6197     }
6201 /*
6202   Local Variables:
6203   mode:c++
6204   c-file-style:"stroustrup"
6205   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
6206   indent-tabs-mode:nil
6207   fill-column:99
6208   End:
6209 */
6210 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :