Code

remove unused function
[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 <glibmm/i18n.h>
55 #include "helper/unit-menu.h"
56 #include "helper/units.h"
57 #include "live_effects/effect.h"
59 #include "inkscape.h"
60 #include "conn-avoid-ref.h"
63 #include "select-toolbar.h"
64 #include "gradient-toolbar.h"
66 #include "connector-context.h"
67 #include "node-context.h"
68 #include "draw-context.h"
69 #include "shape-editor.h"
70 #include "tweak-context.h"
71 #include "sp-rect.h"
72 #include "box3d.h"
73 #include "box3d-context.h"
74 #include "sp-star.h"
75 #include "sp-spiral.h"
76 #include "sp-ellipse.h"
77 #include "sp-text.h"
78 #include "sp-flowtext.h"
79 #include "sp-clippath.h"
80 #include "sp-mask.h"
81 #include "style.h"
82 #include "selection.h"
83 #include "selection-chemistry.h"
84 #include "document-private.h"
85 #include "desktop-style.h"
86 #include "../libnrtype/font-lister.h"
87 #include "../libnrtype/font-instance.h"
88 #include "../connection-pool.h"
89 #include "../prefs-utils.h"
90 #include "../inkscape-stock.h"
91 #include "icon.h"
92 #include "graphlayout/graphlayout.h"
93 #include "interface.h"
94 #include "shortcuts.h"
96 #include "mod360.h"
98 #include "toolbox.h"
100 #include "flood-context.h"
102 #include "ink-action.h"
103 #include "ege-adjustment-action.h"
104 #include "ege-output-action.h"
105 #include "ege-select-one-action.h"
106 #include "helper/unit-tracker.h"
108 #include "svg/css-ostringstream.h"
110 #include "widgets/calligraphic-profile-rename.h"
112 using Inkscape::UnitTracker;
114 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
115 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
117 static void       sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
118 static void       sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
119 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
120 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
121 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
122 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
123 static void       box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
126 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
127 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
128 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
129 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
130 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
131 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
132 static void       sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
134 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
137 Inkscape::IconSize prefToSize( gchar const *path, gchar const *attr, int base ) {
138     static Inkscape::IconSize sizeChoices[] = {
139         Inkscape::ICON_SIZE_LARGE_TOOLBAR,
140         Inkscape::ICON_SIZE_SMALL_TOOLBAR,
141         Inkscape::ICON_SIZE_MENU
142     };
143     int index = prefs_get_int_attribute_limited( path, attr, base, 0, G_N_ELEMENTS(sizeChoices) );
144     return sizeChoices[index];
147 static struct {
148     gchar const *type_name;
149     gchar const *data_name;
150     sp_verb_t verb;
151     sp_verb_t doubleclick_verb;
152 } const tools[] = {
153     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
154     { "SPNodeContext",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
155     { "SPTweakContext",    "tweak_tool",     SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
156     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
157     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
158     { "Box3DContext",      "3dbox_tool",     SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
159     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
160     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
161     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
162     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
163     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
164     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
165     { "SPEraserContext",   "eraser_tool",    SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
166     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
167     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
168     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
169     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
170     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
171     { NULL, NULL, 0, 0 }
172 };
174 static struct {
175     gchar const *type_name;
176     gchar const *data_name;
177     GtkWidget *(*create_func)(SPDesktop *desktop);
178     void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
179     gchar const *ui_name;
180     gint swatch_verb_id;
181     gchar const *swatch_tool;
182     gchar const *swatch_tip;
183 } const aux_toolboxes[] = {
184     { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep,            "SelectToolbar",
185       SP_VERB_INVALID, 0, 0},
186     { "SPNodeContext",   "node_toolbox",   0, sp_node_toolbox_prep,              "NodeToolbar",
187       SP_VERB_INVALID, 0, 0},
188     { "SPTweakContext",   "tweak_toolbox",   0, sp_tweak_toolbox_prep,              "TweakToolbar",
189       SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", N_("Color/opacity used for color tweaking")},
190     { "SPZoomContext",   "zoom_toolbox",   0, sp_zoom_toolbox_prep,              "ZoomToolbar",
191       SP_VERB_INVALID, 0, 0},
192     { "SPStarContext",   "star_toolbox",   0, sp_star_toolbox_prep,              "StarToolbar",
193       SP_VERB_CONTEXT_STAR_PREFS,   "tools.shapes.star",     N_("Style of new stars")},
194     { "SPRectContext",   "rect_toolbox",   0, sp_rect_toolbox_prep,              "RectToolbar",
195       SP_VERB_CONTEXT_RECT_PREFS,   "tools.shapes.rect",     N_("Style of new rectangles")},
196     { "Box3DContext",  "3dbox_toolbox",  0, box3d_toolbox_prep,             "3DBoxToolbar",
197       SP_VERB_CONTEXT_3DBOX_PREFS,  "tools.shapes.3dbox",    N_("Style of new 3D boxes")},
198     { "SPArcContext",    "arc_toolbox",    0, sp_arc_toolbox_prep,               "ArcToolbar",
199       SP_VERB_CONTEXT_ARC_PREFS,    "tools.shapes.arc",      N_("Style of new ellipses")},
200     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
201       SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral",   N_("Style of new spirals")},
202     { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep,            "PencilToolbar",
203       SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", N_("Style of new paths created by Pencil")},
204     { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep,                     "PenToolbar",
205       SP_VERB_CONTEXT_PEN_PREFS,    "tools.freehand.pen",    N_("Style of new paths created by Pen")},
206     { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
207       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", N_("Style of new calligraphic strokes")},
208     { "SPEraserContext", "eraser_toolbox", 0, sp_eraser_toolbox_prep,"EraserToolbar",
209       SP_VERB_CONTEXT_ERASER_PREFS, "tools.eraser", _("TBD")},
210     { "SPTextContext",   "text_toolbox",   sp_text_toolbox_new, 0,               0,
211       SP_VERB_INVALID, 0, 0},
212     { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep,         "DropperToolbar",
213       SP_VERB_INVALID, 0, 0},
214     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0,       0,
215       SP_VERB_INVALID, 0, 0},
216     { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep,   "ConnectorToolbar",
217       SP_VERB_INVALID, 0, 0},
218     { "SPFloodContext",  "paintbucket_toolbox",  0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
219       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", N_("Style of Paint Bucket fill objects")},
220     { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
221 };
224 static gchar const * ui_descr =
225         "<ui>"
226         "  <toolbar name='SelectToolbar'>"
227         "    <toolitem action='EditSelectAll' />"
228         "    <toolitem action='EditSelectAllInAllLayers' />"
229         "    <toolitem action='EditDeselect' />"
230         "    <separator />"
231         "    <toolitem action='ObjectRotate90CCW' />"
232         "    <toolitem action='ObjectRotate90' />"
233         "    <toolitem action='ObjectFlipHorizontally' />"
234         "    <toolitem action='ObjectFlipVertically' />"
235         "    <separator />"
236         "    <toolitem action='SelectionToBack' />"
237         "    <toolitem action='SelectionLower' />"
238         "    <toolitem action='SelectionRaise' />"
239         "    <toolitem action='SelectionToFront' />"
240         "    <separator />"
241         "    <toolitem action='XAction' />"
242         "    <toolitem action='YAction' />"
243         "    <toolitem action='WidthAction' />"
244         "    <toolitem action='LockAction' />"
245         "    <toolitem action='HeightAction' />"
246         "    <toolitem action='UnitsAction' />"
247         "    <separator />"
248         "    <toolitem action='transform_affect_label' />"
249         "    <toolitem action='transform_stroke' />"
250         "    <toolitem action='transform_corners' />"
251         "    <toolitem action='transform_gradient' />"
252         "    <toolitem action='transform_pattern' />"
253         "  </toolbar>"
255         "  <toolbar name='NodeToolbar'>"
256         "    <toolitem action='NodeInsertAction' />"
257         "    <toolitem action='NodeDeleteAction' />"
258         "    <separator />"
259         "    <toolitem action='NodeJoinAction' />"
260         "    <toolitem action='NodeBreakAction' />"
261         "    <separator />"
262         "    <toolitem action='NodeJoinSegmentAction' />"
263         "    <toolitem action='NodeDeleteSegmentAction' />"
264         "    <separator />"
265         "    <toolitem action='NodeCuspAction' />"
266         "    <toolitem action='NodeSmoothAction' />"
267         "    <toolitem action='NodeSymmetricAction' />"
268         "    <separator />"
269         "    <toolitem action='NodeLineAction' />"
270         "    <toolitem action='NodeCurveAction' />"
271         "    <separator />"
272         "    <toolitem action='ObjectToPath' />"
273         "    <toolitem action='StrokeToPath' />"
274         "    <separator />"
275         "    <toolitem action='NodeXAction' />"
276         "    <toolitem action='NodeYAction' />"
277         "    <toolitem action='NodeUnitsAction' />"
278         "    <separator />"
279         "    <toolitem action='ObjectEditClipPathAction' />"
280         "    <toolitem action='ObjectEditMaskPathAction' />"
281         "    <toolitem action='EditNextLPEParameterAction' />"
282         "    <separator />"
283         "    <toolitem action='NodesShowHandlesAction' />"
284         "    <toolitem action='NodesShowHelperpath' />"
285         "  </toolbar>"
287         "  <toolbar name='TweakToolbar'>"
288         "    <toolitem action='TweakWidthAction' />"
289         "    <separator />"
290         "    <toolitem action='TweakForceAction' />"
291         "    <toolitem action='TweakPressureAction' />"
292         "    <separator />"
293         "    <toolitem action='TweakModeAction' />"
294         "    <separator />"
295         "    <toolitem action='TweakFidelityAction' />"
296         "    <separator />"
297         "    <toolitem action='TweakChannelsLabel' />"
298         "    <toolitem action='TweakDoH' />"
299         "    <toolitem action='TweakDoS' />"
300         "    <toolitem action='TweakDoL' />"
301         "    <toolitem action='TweakDoO' />"
302         "  </toolbar>"
304         "  <toolbar name='ZoomToolbar'>"
305         "    <toolitem action='ZoomIn' />"
306         "    <toolitem action='ZoomOut' />"
307         "    <separator />"
308         "    <toolitem action='Zoom1:0' />"
309         "    <toolitem action='Zoom1:2' />"
310         "    <toolitem action='Zoom2:1' />"
311         "    <separator />"
312         "    <toolitem action='ZoomSelection' />"
313         "    <toolitem action='ZoomDrawing' />"
314         "    <toolitem action='ZoomPage' />"
315         "    <toolitem action='ZoomPageWidth' />"
316         "    <separator />"
317         "    <toolitem action='ZoomPrev' />"
318         "    <toolitem action='ZoomNext' />"
319         "  </toolbar>"
321         "  <toolbar name='StarToolbar'>"
322         "    <separator />"
323         "    <toolitem action='StarStateAction' />"
324         "    <separator />"
325         "    <toolitem action='FlatAction' />"
326         "    <separator />"
327         "    <toolitem action='MagnitudeAction' />"
328         "    <toolitem action='SpokeAction' />"
329         "    <toolitem action='RoundednessAction' />"
330         "    <toolitem action='RandomizationAction' />"
331         "    <separator />"
332         "    <toolitem action='StarResetAction' />"
333         "  </toolbar>"
335         "  <toolbar name='RectToolbar'>"
336         "    <toolitem action='RectStateAction' />"
337         "    <toolitem action='RectWidthAction' />"
338         "    <toolitem action='RectHeightAction' />"
339         "    <toolitem action='RadiusXAction' />"
340         "    <toolitem action='RadiusYAction' />"
341         "    <toolitem action='RectUnitsAction' />"
342         "    <separator />"
343         "    <toolitem action='RectResetAction' />"
344         "  </toolbar>"
346         "  <toolbar name='3DBoxToolbar'>"
347         "    <toolitem action='3DBoxAngleXAction' />"
348         "    <toolitem action='3DBoxVPXStateAction' />"
349         "    <separator />"
350         "    <toolitem action='3DBoxAngleYAction' />"
351         "    <toolitem action='3DBoxVPYStateAction' />"
352         "    <separator />"
353         "    <toolitem action='3DBoxAngleZAction' />"
354         "    <toolitem action='3DBoxVPZStateAction' />"
355         "  </toolbar>"
357         "  <toolbar name='SpiralToolbar'>"
358         "    <toolitem action='SpiralStateAction' />"
359         "    <toolitem action='SpiralRevolutionAction' />"
360         "    <toolitem action='SpiralExpansionAction' />"
361         "    <toolitem action='SpiralT0Action' />"
362         "    <separator />"
363         "    <toolitem action='SpiralResetAction' />"
364         "  </toolbar>"
366         "  <toolbar name='PenToolbar'>"
367         "    <toolitem action='FreehandModeActionPenTemp' />"
368         "    <toolitem action='FreehandModeActionPen' />"
369         "  </toolbar>"
371         "  <toolbar name='PencilToolbar'>"
372         "    <toolitem action='FreehandModeActionPencilTemp' />"
373         "    <toolitem action='FreehandModeActionPencil' />"
374         "    <separator />"
375         "    <toolitem action='PencilToleranceAction' />"
376         "    <separator />"
377         "    <toolitem action='PencilResetAction' />"
378         "  </toolbar>"
380         "  <toolbar name='CalligraphyToolbar'>"
381         "    <separator />"
382         "    <toolitem action='SetProfileAction'/>"
383         "    <toolitem action='SaveDeleteProfileAction'/>"
384         "    <separator />"
385         "    <toolitem action='CalligraphyWidthAction' />"
386         "    <toolitem action='PressureAction' />"
387         "    <toolitem action='TraceAction' />"
388         "    <toolitem action='ThinningAction' />"
389         "    <separator />"
390         "    <toolitem action='AngleAction' />"
391         "    <toolitem action='TiltAction' />"
392         "    <toolitem action='FixationAction' />"
393         "    <separator />"
394         "    <toolitem action='CapRoundingAction' />"
395         "    <separator />"
396         "    <toolitem action='TremorAction' />"
397         "    <toolitem action='WiggleAction' />"
398         "    <toolitem action='MassAction' />"
399         "    <separator />"
400         "  </toolbar>"
402         "  <toolbar name='ArcToolbar'>"
403         "    <toolitem action='ArcStateAction' />"
404         "    <separator />"
405         "    <toolitem action='ArcStartAction' />"
406         "    <toolitem action='ArcEndAction' />"
407         "    <separator />"
408         "    <toolitem action='ArcOpenAction' />"
409         "    <separator />"
410         "    <toolitem action='ArcResetAction' />"
411         "    <separator />"
412         "  </toolbar>"
414         "  <toolbar name='PaintbucketToolbar'>"
415         "    <toolitem action='ChannelsAction' />"
416         "    <separator />"
417         "    <toolitem action='ThresholdAction' />"
418         "    <separator />"
419         "    <toolitem action='OffsetAction' />"
420         "    <toolitem action='PaintbucketUnitsAction' />"
421         "    <separator />"
422         "    <toolitem action='AutoGapAction' />"
423         "    <separator />"
424         "    <toolitem action='PaintbucketResetAction' />"
425         "  </toolbar>"
427         "  <toolbar name='EraserToolbar'>"
428         "    <toolitem action='EraserWidthAction' />"
429         "    <separator />"
430         "    <toolitem action='EraserModeAction' />"
431         "  </toolbar>"
433         "  <toolbar name='DropperToolbar'>"
434         "    <toolitem action='DropperOpacityAction' />"
435         "    <toolitem action='DropperPickAlphaAction' />"
436         "    <toolitem action='DropperSetAlphaAction' />"
437         "  </toolbar>"
439         "  <toolbar name='ConnectorToolbar'>"
440         "    <toolitem action='ConnectorAvoidAction' />"
441         "    <toolitem action='ConnectorIgnoreAction' />"
442         "    <toolitem action='ConnectorSpacingAction' />"
443         "    <toolitem action='ConnectorGraphAction' />"
444         "    <toolitem action='ConnectorLengthAction' />"
445         "    <toolitem action='ConnectorDirectedAction' />"
446         "    <toolitem action='ConnectorOverlapAction' />"
447         "  </toolbar>"
449         "</ui>"
452 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
454 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
456 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
457 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
459 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
460 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
462 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
463 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
466 GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick( GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
467                                                               Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
468                                                               Inkscape::UI::View::View *view, GtkTooltips *tt);
470 class VerbAction : public Gtk::Action {
471 public:
472     static Glib::RefPtr<VerbAction> create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
474     virtual ~VerbAction();
475     virtual void set_active(bool active = true);
477 protected:
478     virtual Gtk::Widget* create_menu_item_vfunc();
479     virtual Gtk::Widget* create_tool_item_vfunc();
481     virtual void connect_proxy_vfunc(Gtk::Widget* proxy);
482     virtual void disconnect_proxy_vfunc(Gtk::Widget* proxy);
484     virtual void on_activate();
486 private:
487     Inkscape::Verb* verb;
488     Inkscape::Verb* verb2;
489     Inkscape::UI::View::View *view;
490     GtkTooltips *tooltips;
491     bool active;
493     VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
494 };
497 Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips)
499     Glib::RefPtr<VerbAction> result;
500     SPAction *action = verb->get_action(view);
501     if ( action ) {
502         //SPAction* action2 = verb2 ? verb2->get_action(view) : 0;
503         result = Glib::RefPtr<VerbAction>(new VerbAction(verb, verb2, view, tooltips));
504     }
506     return result;
509 VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips) :
510     Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(GTK_STOCK_ABOUT), Glib::ustring(verb->get_name()), Glib::ustring(verb->get_tip())),
511     verb(verb),
512     verb2(verb2),
513     view(view),
514     tooltips(tooltips),
515     active(false)
519 VerbAction::~VerbAction()
523 Gtk::Widget* VerbAction::create_menu_item_vfunc()
525     Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
526 //     g_message("create_menu_item_vfunc() = %p  for '%s'", widg, verb->get_id());
527     return widg;
530 Gtk::Widget* VerbAction::create_tool_item_vfunc()
532 //     Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
533     Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
534     GtkWidget* toolbox = 0;
535     GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
536                                                                           SP_BUTTON_TYPE_TOGGLE,
537                                                                           verb,
538                                                                           verb2,
539                                                                           view,
540                                                                           tooltips );
541     if ( active ) {
542         sp_button_toggle_set_down( SP_BUTTON(button), active);
543     }
544     gtk_widget_show_all( button );
545     Gtk::Widget* wrapped = Glib::wrap(button);
546     Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
547     holder->add(*wrapped);
549 //     g_message("create_tool_item_vfunc() = %p  for '%s'", holder, verb->get_id());
550     return holder;
553 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
555 //     g_message("connect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
556     Gtk::Action::connect_proxy_vfunc(proxy);
559 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
561 //     g_message("disconnect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
562     Gtk::Action::disconnect_proxy_vfunc(proxy);
565 void VerbAction::set_active(bool active)
567     this->active = active;
568     Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
569     for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
570         Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
571         if (ti) {
572             // *should* have one child that is the SPButton
573             Gtk::Widget* child = ti->get_child();
574             if ( child && SP_IS_BUTTON(child->gobj()) ) {
575                 SPButton* button = SP_BUTTON(child->gobj());
576                 sp_button_toggle_set_down( button, active );
577             }
578         }
579     }
582 void VerbAction::on_activate()
584     if ( verb ) {
585         SPAction *action = verb->get_action(view);
586         if ( action ) {
587             sp_action_perform(action, 0);
588         }
589     }
592 /* Global text entry widgets necessary for update */
593 /* GtkWidget *dropper_rgb_entry,
594           *dropper_opacity_entry ; */
595 // should be made a private member once this is converted to class
597 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
598     connection->disconnect();
599     delete connection;
602 static void purge_repr_listener( GObject* obj, GObject* tbl )
604     (void)obj;
605     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
606     if (oldrepr) { // remove old listener
607         sp_repr_remove_listener_by_data(oldrepr, tbl);
608         Inkscape::GC::release(oldrepr);
609         oldrepr = 0;
610         g_object_set_data( tbl, "repr", NULL );
611     }
614 GtkWidget *
615 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
616                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
617                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
619     SPAction *action = verb->get_action(view);
620     if (!action) return NULL;
622     SPAction *doubleclick_action;
623     if (doubleclick_verb)
624         doubleclick_action = doubleclick_verb->get_action(view);
625     else
626         doubleclick_action = NULL;
628     /* fixme: Handle sensitive/unsensitive */
629     /* fixme: Implement sp_button_new_from_action */
630     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
631     gtk_widget_show(b);
634     unsigned int shortcut = sp_shortcut_get_primary(verb);
635     if (shortcut) {
636         gchar key[256];
637         sp_ui_shortcut_string(shortcut, key);
638         gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
639         if ( t ) {
640             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
641         }
642         g_free(tip);
643     } else {
644         if ( t ) {
645             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
646         }
647     }
649     return b;
653 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
655     SPAction* targetAction = SP_ACTION(user_data);
656     if ( targetAction ) {
657         sp_action_perform( targetAction, NULL );
658     }
661 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
663     if ( data ) {
664         GtkAction* act = GTK_ACTION(data);
665         gtk_action_set_sensitive( act, sensitive );
666     }
669 static SPActionEventVector action_event_vector = {
670     {NULL},
671     NULL,
672     NULL,
673     sp_action_action_set_sensitive,
674     NULL,
675     NULL
676 };
678 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
680     GtkAction* act = 0;
682     SPAction* targetAction = verb->get_action(view);
683     InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size  );
684     act = GTK_ACTION(inky);
685     gtk_action_set_sensitive( act, targetAction->sensitive );
687     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
689     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
690     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
692     return act;
695 Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
697     Inkscape::UI::View::View *view = desktop;
698     gint verbsToUse[] = {
699         // disabled until we have icons for them:
700         //find
701         //SP_VERB_EDIT_TILE,
702         //SP_VERB_EDIT_UNTILE,
703         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
704         SP_VERB_DIALOG_DISPLAY,
705         SP_VERB_DIALOG_FILL_STROKE,
706         SP_VERB_DIALOG_NAMEDVIEW,
707         SP_VERB_DIALOG_TEXT,
708         SP_VERB_DIALOG_XML_EDITOR,
709         SP_VERB_EDIT_CLONE,
710         SP_VERB_EDIT_COPY,
711         SP_VERB_EDIT_CUT,
712         SP_VERB_EDIT_DUPLICATE,
713         SP_VERB_EDIT_PASTE,
714         SP_VERB_EDIT_REDO,
715         SP_VERB_EDIT_UNDO,
716         SP_VERB_EDIT_UNLINK_CLONE,
717         SP_VERB_FILE_EXPORT,
718         SP_VERB_FILE_IMPORT,
719         SP_VERB_FILE_NEW,
720         SP_VERB_FILE_OPEN,
721         SP_VERB_FILE_PRINT,
722         SP_VERB_FILE_SAVE,
723         SP_VERB_OBJECT_TO_CURVE,
724         SP_VERB_SELECTION_GROUP,
725         SP_VERB_SELECTION_OUTLINE,
726         SP_VERB_SELECTION_UNGROUP,
727         SP_VERB_ZOOM_1_1,
728         SP_VERB_ZOOM_1_2,
729         SP_VERB_ZOOM_2_1,
730         SP_VERB_ZOOM_DRAWING,
731         SP_VERB_ZOOM_IN,
732         SP_VERB_ZOOM_NEXT,
733         SP_VERB_ZOOM_OUT,
734         SP_VERB_ZOOM_PAGE,
735         SP_VERB_ZOOM_PAGE_WIDTH,
736         SP_VERB_ZOOM_PREV,
737         SP_VERB_ZOOM_SELECTION,
738     };
740     Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
742     static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
743     Glib::RefPtr<Gtk::ActionGroup> mainActions;
744     if ( groups.find(desktop) != groups.end() ) {
745         mainActions = groups[desktop];
746     }
748     if ( !mainActions ) {
749         mainActions = Gtk::ActionGroup::create("main");
750         groups[desktop] = mainActions;
751     }
753     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
754         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
755         if ( verb ) {
756             if (!mainActions->get_action(verb->get_id())) {
757                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
758                 mainActions->add(Glib::wrap(act));
759             }
760         }
761     }
763     if ( !mainActions->get_action("ToolZoom") ) {
764         GtkTooltips *tt = gtk_tooltips_new();
765         for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
766             Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
767             if ( va ) {
768                 mainActions->add(va);
769                 if ( i == 0 ) {
770                     va->set_active(true);
771                 }
772             }
773         }
774     }
777     return mainActions;
781 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
783     gtk_widget_set_size_request( widget,
784                                  widget->allocation.width,
785                                  widget->allocation.height );
788 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
790     gtk_widget_set_size_request( widget, -1, -1 );
795 GtkWidget *
796 sp_tool_toolbox_new()
798     GtkTooltips *tt = gtk_tooltips_new();
799     GtkWidget* tb = gtk_toolbar_new();
800     gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
801     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
803     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
804     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
806     gtk_widget_set_sensitive(tb, FALSE);
808     GtkWidget *hb = gtk_handle_box_new();
809     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
810     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
811     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
813     gtk_container_add(GTK_CONTAINER(hb), tb);
814     gtk_widget_show(GTK_WIDGET(tb));
816     sigc::connection* conn = new sigc::connection;
817     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
819     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
820     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
822     return hb;
825 GtkWidget *
826 sp_aux_toolbox_new()
828     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
830     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
832     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
834     gtk_widget_set_sensitive(tb, FALSE);
836     GtkWidget *hb = gtk_handle_box_new();
837     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
838     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
839     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
841     gtk_container_add(GTK_CONTAINER(hb), tb);
842     gtk_widget_show(GTK_WIDGET(tb));
844     sigc::connection* conn = new sigc::connection;
845     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
847     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
848     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
850     return hb;
853 //####################################
854 //# Commands Bar
855 //####################################
857 GtkWidget *
858 sp_commands_toolbox_new()
860     GtkWidget *tb = gtk_toolbar_new();
862     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
863     gtk_widget_set_sensitive(tb, FALSE);
865     GtkWidget *hb = gtk_handle_box_new();
866     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
867     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
868     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
870     gtk_container_add(GTK_CONTAINER(hb), tb);
871     gtk_widget_show(GTK_WIDGET(tb));
873     sigc::connection* conn = new sigc::connection;
874     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
876     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
877     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
879     return hb;
883 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
884                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
885                                                        gchar const *path, gchar const *data, gdouble def,
886                                                        GtkWidget *focusTarget,
887                                                        GtkWidget *us,
888                                                        GObject *dataKludge,
889                                                        gboolean altx, gchar const *altx_mark,
890                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
891                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
892                                                        void (*callback)(GtkAdjustment *, GObject *),
893                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
895     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
896                                                              lower, upper, step, page, page ) );
897     if (us) {
898         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
899     }
901     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
903     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
904     if ( shortLabel ) {
905         g_object_set( act, "short_label", shortLabel, NULL );
906     }
908     if ( (descrCount > 0) && descrLabels && descrValues ) {
909         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
910     }
912     if ( focusTarget ) {
913         ege_adjustment_action_set_focuswidget( act, focusTarget );
914     }
916     if ( altx && altx_mark ) {
917         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
918     }
920     if ( dataKludge ) {
921         g_object_set_data( dataKludge, data, adj );
922     }
924     // Using a cast just to make sure we pass in the right kind of function pointer
925     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
927     return act;
931 //####################################
932 //# node editing callbacks
933 //####################################
935 /**
936  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
937  */
938 static ShapeEditor *get_current_shape_editor()
940     if (!SP_ACTIVE_DESKTOP) {
941         return NULL;
942     }
944     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
946     if (!SP_IS_NODE_CONTEXT(event_context)) {
947         return NULL;
948     }
950     return SP_NODE_CONTEXT(event_context)->shape_editor;
954 void
955 sp_node_path_edit_add(void)
957     ShapeEditor *shape_editor = get_current_shape_editor();
958     if (shape_editor) shape_editor->add_node();
961 void
962 sp_node_path_edit_delete(void)
964     ShapeEditor *shape_editor = get_current_shape_editor();
965     if (shape_editor) shape_editor->delete_nodes_preserving_shape();
968 void
969 sp_node_path_edit_delete_segment(void)
971     ShapeEditor *shape_editor = get_current_shape_editor();
972     if (shape_editor) shape_editor->delete_segment();
975 void
976 sp_node_path_edit_break(void)
978     ShapeEditor *shape_editor = get_current_shape_editor();
979     if (shape_editor) shape_editor->break_at_nodes();
982 void
983 sp_node_path_edit_join(void)
985     ShapeEditor *shape_editor = get_current_shape_editor();
986     if (shape_editor) shape_editor->join_nodes();
989 void
990 sp_node_path_edit_join_segment(void)
992     ShapeEditor *shape_editor = get_current_shape_editor();
993     if (shape_editor) shape_editor->join_segments();
996 void
997 sp_node_path_edit_toline(void)
999     ShapeEditor *shape_editor = get_current_shape_editor();
1000     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1003 void
1004 sp_node_path_edit_tocurve(void)
1006     ShapeEditor *shape_editor = get_current_shape_editor();
1007     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1010 void
1011 sp_node_path_edit_cusp(void)
1013     ShapeEditor *shape_editor = get_current_shape_editor();
1014     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1017 void
1018 sp_node_path_edit_smooth(void)
1020     ShapeEditor *shape_editor = get_current_shape_editor();
1021     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1024 void
1025 sp_node_path_edit_symmetrical(void)
1027     ShapeEditor *shape_editor = get_current_shape_editor();
1028     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1031 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1032     bool show = gtk_toggle_action_get_active( act );
1033     prefs_set_int_attribute ("tools.nodes", "show_handles",  show ? 1 : 0);
1034     ShapeEditor *shape_editor = get_current_shape_editor();
1035     if (shape_editor) shape_editor->show_handles(show);
1038 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1039     bool show = gtk_toggle_action_get_active( act );
1040     prefs_set_int_attribute ("tools.nodes", "show_helperpath",  show ? 1 : 0);
1041     ShapeEditor *shape_editor = get_current_shape_editor();
1042     if (shape_editor) shape_editor->show_helperpath(show);
1045 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1046     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1049 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1050     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1053 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1054     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1057 /* is called when the node selection is modified */
1058 static void
1059 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1061     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1062     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1063     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1064     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1066     // quit if run by the attr_changed listener
1067     if (g_object_get_data( tbl, "freeze" )) {
1068         return;
1069     }
1071     // in turn, prevent listener from responding
1072     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1074     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1075     SPUnit const *unit = tracker->getActiveUnit();
1077     ShapeEditor *shape_editor = get_current_shape_editor();
1078     if (shape_editor && shape_editor->has_nodepath()) {
1079         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1080         int n_selected = 0;
1081         if (nodepath) {
1082             n_selected = nodepath->numSelected();
1083         }
1085         if (n_selected == 0) {
1086             gtk_action_set_sensitive(xact, FALSE);
1087             gtk_action_set_sensitive(yact, FALSE);
1088         } else {
1089             gtk_action_set_sensitive(xact, TRUE);
1090             gtk_action_set_sensitive(yact, TRUE);
1091             NR::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1092             NR::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1094             if (n_selected == 1) {
1095                 NR::Point sel_node = nodepath->singleSelectedCoords();
1096                 if (oldx != sel_node[NR::X] || oldy != sel_node[NR::Y]) {
1097                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[NR::X], *unit));
1098                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[NR::Y], *unit));
1099                 }
1100             } else {
1101                 NR::Maybe<NR::Coord> x = sp_node_selected_common_coord(nodepath, NR::X);
1102                 NR::Maybe<NR::Coord> y = sp_node_selected_common_coord(nodepath, NR::Y);
1103                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1104                     /* Note: Currently x and y will always have a value, even if the coordinates of the
1105                        selected nodes don't coincide (in this case we use the coordinates of the center
1106                        of the bounding box). So the entries are never set to zero. */
1107                     // FIXME: Maybe we should clear the entry if several nodes are selected
1108                     //        instead of providing a kind of average value
1109                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1110                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1111                 }
1112             }
1113         }
1114     } else {
1115         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1116         gtk_action_set_sensitive(xact, FALSE);
1117         gtk_action_set_sensitive(yact, FALSE);
1118     }
1120     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1123 static void
1124 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1126     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1128     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1129     SPUnit const *unit = tracker->getActiveUnit();
1131     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1132         prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
1133     }
1135     // quit if run by the attr_changed listener
1136     if (g_object_get_data( tbl, "freeze" )) {
1137         return;
1138     }
1140     // in turn, prevent listener from responding
1141     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1143     ShapeEditor *shape_editor = get_current_shape_editor();
1144     if (shape_editor && shape_editor->has_nodepath()) {
1145         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1146         if (!strcmp(value_name, "x")) {
1147             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::X);
1148         }
1149         if (!strcmp(value_name, "y")) {
1150             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::Y);
1151         }
1152     }
1154     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1157 static void
1158 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1160     sp_node_path_value_changed(adj, tbl, "x");
1163 static void
1164 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1166     sp_node_path_value_changed(adj, tbl, "y");
1169 void
1170 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1172     {
1173     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1174     SPItem *item = selection->singleItem();
1175     if (item && SP_IS_LPE_ITEM(item)) {
1176        if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1177            gtk_action_set_sensitive(w, TRUE);
1178        } else {
1179            gtk_action_set_sensitive(w, FALSE);
1180        }
1181     } else {
1182        gtk_action_set_sensitive(w, FALSE);
1183     }
1184     }
1186     {
1187     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1188     SPItem *item = selection->singleItem();
1189     if (item && item->clip_ref && item->clip_ref->getObject()) {
1190        gtk_action_set_sensitive(w, TRUE);
1191     } else {
1192        gtk_action_set_sensitive(w, FALSE);
1193     }
1194     }
1196     {
1197     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1198     SPItem *item = selection->singleItem();
1199     if (item && item->mask_ref && item->mask_ref->getObject()) {
1200        gtk_action_set_sensitive(w, TRUE);
1201     } else {
1202        gtk_action_set_sensitive(w, FALSE);
1203     }
1204     }
1207 void
1208 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1210     sp_node_toolbox_sel_changed (selection, tbl);
1215 //################################
1216 //##    Node Editing Toolbox    ##
1217 //################################
1219 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1221     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1222     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1223     g_object_set_data( holder, "tracker", tracker );
1225     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
1227     {
1228         InkAction* inky = ink_action_new( "NodeInsertAction",
1229                                           _("Insert node"),
1230                                           _("Insert new nodes into selected segments"),
1231                                           "node_insert",
1232                                           secondarySize );
1233         g_object_set( inky, "short_label", _("Insert"), NULL );
1234         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1235         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1236     }
1238     {
1239         InkAction* inky = ink_action_new( "NodeDeleteAction",
1240                                           _("Delete node"),
1241                                           _("Delete selected nodes"),
1242                                           "node_delete",
1243                                           secondarySize );
1244         g_object_set( inky, "short_label", _("Delete"), NULL );
1245         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1246         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1247     }
1249     {
1250         InkAction* inky = ink_action_new( "NodeJoinAction",
1251                                           _("Join endnodes"),
1252                                           _("Join selected endnodes"),
1253                                           "node_join",
1254                                           secondarySize );
1255         g_object_set( inky, "short_label", _("Join"), NULL );
1256         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1257         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1258     }
1260     {
1261         InkAction* inky = ink_action_new( "NodeBreakAction",
1262                                           _("Break nodes"),
1263                                           _("Break path at selected nodes"),
1264                                           "node_break",
1265                                           secondarySize );
1266         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1267         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1268     }
1271     {
1272         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1273                                           _("Join with segment"),
1274                                           _("Join selected endnodes with a new segment"),
1275                                           "node_join_segment",
1276                                           secondarySize );
1277         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1278         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1279     }
1281     {
1282         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1283                                           _("Delete segment"),
1284                                           _("Delete segment between two non-endpoint nodes"),
1285                                           "node_delete_segment",
1286                                           secondarySize );
1287         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1288         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1289     }
1291     {
1292         InkAction* inky = ink_action_new( "NodeCuspAction",
1293                                           _("Node Cusp"),
1294                                           _("Make selected nodes corner"),
1295                                           "node_cusp",
1296                                           secondarySize );
1297         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1298         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1299     }
1301     {
1302         InkAction* inky = ink_action_new( "NodeSmoothAction",
1303                                           _("Node Smooth"),
1304                                           _("Make selected nodes smooth"),
1305                                           "node_smooth",
1306                                           secondarySize );
1307         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1308         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1309     }
1311     {
1312         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1313                                           _("Node Symmetric"),
1314                                           _("Make selected nodes symmetric"),
1315                                           "node_symmetric",
1316                                           secondarySize );
1317         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1318         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1319     }
1321     {
1322         InkAction* inky = ink_action_new( "NodeLineAction",
1323                                           _("Node Line"),
1324                                           _("Make selected segments lines"),
1325                                           "node_line",
1326                                           secondarySize );
1327         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1328         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1329     }
1331     {
1332         InkAction* inky = ink_action_new( "NodeCurveAction",
1333                                           _("Node Curve"),
1334                                           _("Make selected segments curves"),
1335                                           "node_curve",
1336                                           secondarySize );
1337         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1338         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1339     }
1341     {
1342         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1343                                                       _("Show Handles"),
1344                                                       _("Show the Bezier handles of selected nodes"),
1345                                                       "nodes_show_handles",
1346                                                       Inkscape::ICON_SIZE_DECORATION );
1347         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1348         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1349         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1350     }
1352     {
1353         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1354                                                       _("Show Outline"),
1355                                                       _("Show the outline of the path"),
1356                                                       "nodes_show_helperpath",
1357                                                       Inkscape::ICON_SIZE_DECORATION );
1358         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1359         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1360         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_helperpath", 0 ) );
1361     }
1363     {
1364         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1365                                           _("Next path effect parameter"),
1366                                           _("Show next path effect parameter for editing"),
1367                                           "edit_next_parameter",
1368                                           Inkscape::ICON_SIZE_DECORATION );
1369         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1370         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1371         g_object_set_data( holder, "nodes_lpeedit", inky);
1372     }
1374     {
1375         InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1376                                           _("Edit clipping path"),
1377                                           _("Edit the clipping path of the object"),
1378                                           "nodeedit-clippath",
1379                                           Inkscape::ICON_SIZE_DECORATION );
1380         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1381         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1382         g_object_set_data( holder, "nodes_clippathedit", inky);
1383     }
1385     {
1386         InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1387                                           _("Edit mask path"),
1388                                           _("Edit the mask of the object"),
1389                                           "nodeedit-mask",
1390                                           Inkscape::ICON_SIZE_DECORATION );
1391         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1392         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1393         g_object_set_data( holder, "nodes_maskedit", inky);
1394     }
1396     /* X coord of selected node(s) */
1397     {
1398         EgeAdjustmentAction* eact = 0;
1399         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1400         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1401         eact = create_adjustment_action( "NodeXAction",
1402                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1403                                          "tools.nodes", "Xcoord", 0,
1404                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1405                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1406                                          labels, values, G_N_ELEMENTS(labels),
1407                                          sp_node_path_x_value_changed );
1408         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1409         g_object_set_data( holder, "nodes_x_action", eact );
1410         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1411         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1412     }
1414     /* Y coord of selected node(s) */
1415     {
1416         EgeAdjustmentAction* eact = 0;
1417         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1418         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1419         eact = create_adjustment_action( "NodeYAction",
1420                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1421                                          "tools.nodes", "Ycoord", 0,
1422                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1423                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1424                                          labels, values, G_N_ELEMENTS(labels),
1425                                          sp_node_path_y_value_changed );
1426         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1427         g_object_set_data( holder, "nodes_y_action", eact );
1428         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1429         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1430     }
1432     // add the units menu
1433     {
1434         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1435         gtk_action_group_add_action( mainActions, act );
1436     }
1439     sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1441     //watch selection
1442     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1444     sigc::connection *c_selection_changed =
1445         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1446                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1447     pool->add_connection ("selection-changed", c_selection_changed);
1449     sigc::connection *c_selection_modified =
1450         new sigc::connection (sp_desktop_selection (desktop)->connectModified
1451                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1452     pool->add_connection ("selection-modified", c_selection_modified);
1454     sigc::connection *c_subselection_changed =
1455         new sigc::connection (desktop->connectToolSubselectionChanged
1456                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1457     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1459     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1461     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1462 } // end of sp_node_toolbox_prep()
1465 //########################
1466 //##    Zoom Toolbox    ##
1467 //########################
1469 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1471     // no custom GtkAction setup needed
1472 } // end of sp_zoom_toolbox_prep()
1474 void
1475 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1477     toolbox_set_desktop(toolbox,
1478                         desktop,
1479                         setup_tool_toolbox,
1480                         update_tool_toolbox,
1481                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1482                                                                          "event_context_connection")));
1486 void
1487 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1489     toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1490                         desktop,
1491                         setup_aux_toolbox,
1492                         update_aux_toolbox,
1493                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1494                                                                          "event_context_connection")));
1497 void
1498 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1500     toolbox_set_desktop(toolbox,
1501                         desktop,
1502                         setup_commands_toolbox,
1503                         update_commands_toolbox,
1504                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1505                                                                          "event_context_connection")));
1508 static void
1509 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1511     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1512     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1514     if (old_desktop) {
1515         GList *children, *iter;
1517         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1518         for ( iter = children ; iter ; iter = iter->next ) {
1519             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1520         }
1521         g_list_free(children);
1522     }
1524     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1526     if (desktop) {
1527         gtk_widget_set_sensitive(toolbox, TRUE);
1528         setup_func(toolbox, desktop);
1529         update_func(desktop, desktop->event_context, toolbox);
1530         *conn = desktop->connectEventContextChanged
1531             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1532     } else {
1533         gtk_widget_set_sensitive(toolbox, FALSE);
1534     }
1536 } // end of toolbox_set_desktop()
1539 static void
1540 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1542     gchar const * descr =
1543         "<ui>"
1544         "  <toolbar name='ToolToolbar'>"
1545         "    <toolitem action='ToolSelector' />"
1546         "    <toolitem action='ToolNode' />"
1547         "    <toolitem action='ToolTweak' />"
1548         "    <toolitem action='ToolZoom' />"
1549         "    <toolitem action='ToolRect' />"
1550         "    <toolitem action='Tool3DBox' />"
1551         "    <toolitem action='ToolArc' />"
1552         "    <toolitem action='ToolStar' />"
1553         "    <toolitem action='ToolSpiral' />"
1554         "    <toolitem action='ToolPencil' />"
1555         "    <toolitem action='ToolPen' />"
1556         "    <toolitem action='ToolCalligraphic' />"
1557         "    <toolitem action='ToolEraser' />"
1558         "    <toolitem action='ToolPaintBucket' />"
1559         "    <toolitem action='ToolText' />"
1560         "    <toolitem action='ToolConnector' />"
1561         "    <toolitem action='ToolGradient' />"
1562         "    <toolitem action='ToolDropper' />"
1563         "  </toolbar>"
1564         "</ui>";
1565     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1566     GtkUIManager* mgr = gtk_ui_manager_new();
1567     GError* errVal = 0;
1569     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1570     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1572     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" );
1573     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1574         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1575     }
1576     Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1577     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1579     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
1580     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1582     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1584     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1585     if ( child ) {
1586         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1587     }
1589     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1590 //     Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1594 static void
1595 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox )
1597     gchar const *const tname = ( eventcontext
1598                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1599                                  : NULL );
1600     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1602     for (int i = 0 ; tools[i].type_name ; i++ ) {
1603         Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1604         if ( act ) {
1605             bool setActive = tname && !strcmp(tname, tools[i].type_name);
1606             Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1607             if ( verbAct ) {
1608                 verbAct->set_active(setActive);
1609             }
1610         }
1611     }
1614 static void
1615 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1617     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1618     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1619     GtkUIManager* mgr = gtk_ui_manager_new();
1620     GError* errVal = 0;
1621     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1622     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1624     std::map<std::string, GtkWidget*> dataHolders;
1626     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1627         if ( aux_toolboxes[i].prep_func ) {
1628             // converted to GtkActions and UIManager
1630             GtkWidget* kludge = gtk_toolbar_new();
1631             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1632             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1633             dataHolders[aux_toolboxes[i].type_name] = kludge;
1634             aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1635         } else {
1637             GtkWidget *sub_toolbox = 0;
1638             if (aux_toolboxes[i].create_func == NULL)
1639                 sub_toolbox = sp_empty_toolbox_new(desktop);
1640             else {
1641                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1642             }
1644             gtk_size_group_add_widget( grouper, sub_toolbox );
1646             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1647             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1649         }
1650     }
1652     // Second pass to create toolbars *after* all GtkActions are created
1653     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1654         if ( aux_toolboxes[i].prep_func ) {
1655             // converted to GtkActions and UIManager
1657             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1659             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1660             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1662             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1663             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1664             g_free( tmp );
1665             tmp = 0;
1667             Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1668             if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1669                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1670             }
1671             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1674             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1676             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1677                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1678                 swatch->setDesktop( desktop );
1679                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1680                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1681                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1682                 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 );
1683             }
1685             gtk_widget_show_all( holder );
1686             sp_set_font_size_smaller( holder );
1688             gtk_size_group_add_widget( grouper, holder );
1690             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1691             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1692         }
1693     }
1695     g_object_unref( G_OBJECT(grouper) );
1698 static void
1699 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1701     gchar const *tname = ( eventcontext
1702                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1703                            : NULL );
1704     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1705         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1706         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1707             gtk_widget_show_all(sub_toolbox);
1708             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1709         } else {
1710             gtk_widget_hide(sub_toolbox);
1711         }
1712     }
1715 static void
1716 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1718     gchar const * descr =
1719         "<ui>"
1720         "  <toolbar name='CommandsToolbar'>"
1721         "    <toolitem action='FileNew' />"
1722         "    <toolitem action='FileOpen' />"
1723         "    <toolitem action='FileSave' />"
1724         "    <toolitem action='FilePrint' />"
1725         "    <separator />"
1726         "    <toolitem action='FileImport' />"
1727         "    <toolitem action='FileExport' />"
1728         "    <separator />"
1729         "    <toolitem action='EditUndo' />"
1730         "    <toolitem action='EditRedo' />"
1731         "    <separator />"
1732         "    <toolitem action='EditCopy' />"
1733         "    <toolitem action='EditCut' />"
1734         "    <toolitem action='EditPaste' />"
1735         "    <separator />"
1736         "    <toolitem action='ZoomSelection' />"
1737         "    <toolitem action='ZoomDrawing' />"
1738         "    <toolitem action='ZoomPage' />"
1739         "    <separator />"
1740         "    <toolitem action='EditDuplicate' />"
1741         "    <toolitem action='EditClone' />"
1742         "    <toolitem action='EditUnlinkClone' />"
1743         "    <separator />"
1744         "    <toolitem action='SelectionGroup' />"
1745         "    <toolitem action='SelectionUnGroup' />"
1746         "    <separator />"
1747         "    <toolitem action='DialogFillStroke' />"
1748         "    <toolitem action='DialogText' />"
1749         "    <toolitem action='DialogXMLEditor' />"
1750         "    <toolitem action='DialogAlignDistribute' />"
1751         "    <separator />"
1752         "    <toolitem action='DialogPreferences' />"
1753         "    <toolitem action='DialogDocumentProperties' />"
1754         "  </toolbar>"
1755         "</ui>";
1756     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1759     GtkUIManager* mgr = gtk_ui_manager_new();
1760     GError* errVal = 0;
1762     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1763     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1765     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1766     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1767         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1768     }
1770     Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1771     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1773     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1774     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1777     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1779     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1780     if ( child ) {
1781         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1782     }
1784     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1787 static void
1788 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1792 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1794     gtk_widget_show(toolbox_toplevel);
1795     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1797     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1798     if (!shown_toolbox) {
1799         return;
1800     }
1801     gtk_widget_show(toolbox);
1803     gtk_widget_show_all(shown_toolbox);
1806 static GtkWidget *
1807 sp_empty_toolbox_new(SPDesktop *desktop)
1809     GtkWidget *tbl = gtk_toolbar_new();
1810     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1811     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1813     gtk_widget_show_all(tbl);
1814     sp_set_font_size_smaller (tbl);
1816     return tbl;
1819 #define MODE_LABEL_WIDTH 70
1821 //########################
1822 //##       Star         ##
1823 //########################
1825 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1827     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1829     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1830         // do not remember prefs if this call is initiated by an undo change, because undoing object
1831         // creation sets bogus values to its attributes before it is deleted
1832         prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1833     }
1835     // quit if run by the attr_changed listener
1836     if (g_object_get_data( dataKludge, "freeze" )) {
1837         return;
1838     }
1840     // in turn, prevent listener from responding
1841     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1843     bool modmade = false;
1845     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1846     GSList const *items = selection->itemList();
1847     for (; items != NULL; items = items->next) {
1848         if (SP_IS_STAR((SPItem *) items->data)) {
1849             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1850             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1851             sp_repr_set_svg_double(repr, "sodipodi:arg2",
1852                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1853                                     + M_PI / (gint)adj->value));
1854             SP_OBJECT((SPItem *) items->data)->updateRepr();
1855             modmade = true;
1856         }
1857     }
1858     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1859                                    _("Star: Change number of corners"));
1861     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1864 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1866     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1868     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1869         prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1870     }
1872     // quit if run by the attr_changed listener
1873     if (g_object_get_data( dataKludge, "freeze" )) {
1874         return;
1875     }
1877     // in turn, prevent listener from responding
1878     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1880     bool modmade = false;
1881     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1882     GSList const *items = selection->itemList();
1883     for (; items != NULL; items = items->next) {
1884         if (SP_IS_STAR((SPItem *) items->data)) {
1885             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1887             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1888             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1889             if (r2 < r1) {
1890                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1891             } else {
1892                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1893             }
1895             SP_OBJECT((SPItem *) items->data)->updateRepr();
1896             modmade = true;
1897         }
1898     }
1900     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1901                                    _("Star: Change spoke ratio"));
1903     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1906 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1908     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1909     bool flat = ege_select_one_action_get_active( act ) == 0;
1911     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1912         prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1913                                     flat ? "true" : "false" );
1914     }
1916     // quit if run by the attr_changed listener
1917     if (g_object_get_data( dataKludge, "freeze" )) {
1918         return;
1919     }
1921     // in turn, prevent listener from responding
1922     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1924     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1925     GSList const *items = selection->itemList();
1926     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1927     bool modmade = false;
1929     if ( prop_action ) {
1930         gtk_action_set_sensitive( prop_action, !flat );
1931     }
1933     for (; items != NULL; items = items->next) {
1934         if (SP_IS_STAR((SPItem *) items->data)) {
1935             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1936             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1937             SP_OBJECT((SPItem *) items->data)->updateRepr();
1938             modmade = true;
1939         }
1940     }
1942     if (modmade) {
1943         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1944                          flat ? _("Make polygon") : _("Make star"));
1945     }
1947     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1950 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1952     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1954     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1955         prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1956     }
1958     // quit if run by the attr_changed listener
1959     if (g_object_get_data( dataKludge, "freeze" )) {
1960         return;
1961     }
1963     // in turn, prevent listener from responding
1964     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1966     bool modmade = false;
1968     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1969     GSList const *items = selection->itemList();
1970     for (; items != NULL; items = items->next) {
1971         if (SP_IS_STAR((SPItem *) items->data)) {
1972             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1973             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1974             SP_OBJECT(items->data)->updateRepr();
1975             modmade = true;
1976         }
1977     }
1978     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1979                                    _("Star: Change rounding"));
1981     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1984 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1986     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1988     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1989         prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1990     }
1992     // quit if run by the attr_changed listener
1993     if (g_object_get_data( dataKludge, "freeze" )) {
1994         return;
1995     }
1997     // in turn, prevent listener from responding
1998     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2000     bool modmade = false;
2002     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2003     GSList const *items = selection->itemList();
2004     for (; items != NULL; items = items->next) {
2005         if (SP_IS_STAR((SPItem *) items->data)) {
2006             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2007             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2008             SP_OBJECT(items->data)->updateRepr();
2009             modmade = true;
2010         }
2011     }
2012     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2013                                    _("Star: Change randomization"));
2015     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2019 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2020                                        gchar const */*old_value*/, gchar const */*new_value*/,
2021                                        bool /*is_interactive*/, gpointer data)
2023     GtkWidget *tbl = GTK_WIDGET(data);
2025     // quit if run by the _changed callbacks
2026     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2027         return;
2028     }
2030     // in turn, prevent callbacks from responding
2031     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2033     GtkAdjustment *adj = 0;
2035     gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2036     bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2038     if (!strcmp(name, "inkscape:randomized")) {
2039         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2040         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2041     } else if (!strcmp(name, "inkscape:rounded")) {
2042         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2043         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2044     } else if (!strcmp(name, "inkscape:flatsided")) {
2045         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2046         char const *flatsides = repr->attribute("inkscape:flatsided");
2047         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2048         if ( flatsides && !strcmp(flatsides,"false") ) {
2049             ege_select_one_action_set_active( flat_action, 1 );
2050             gtk_action_set_sensitive( prop_action, TRUE );
2051         } else {
2052             ege_select_one_action_set_active( flat_action, 0 );
2053             gtk_action_set_sensitive( prop_action, FALSE );
2054         }
2055     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2056         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2057         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2058         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2059         if (r2 < r1) {
2060             gtk_adjustment_set_value(adj, r2/r1);
2061         } else {
2062             gtk_adjustment_set_value(adj, r1/r2);
2063         }
2064     } else if (!strcmp(name, "sodipodi:sides")) {
2065         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2066         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2067     }
2069     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2073 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2075     NULL, /* child_added */
2076     NULL, /* child_removed */
2077     star_tb_event_attr_changed,
2078     NULL, /* content_changed */
2079     NULL  /* order_changed */
2080 };
2083 /**
2084  *  \param selection Should not be NULL.
2085  */
2086 static void
2087 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2089     int n_selected = 0;
2090     Inkscape::XML::Node *repr = NULL;
2092     purge_repr_listener( tbl, tbl );
2094     for (GSList const *items = selection->itemList();
2095          items != NULL;
2096          items = items->next)
2097     {
2098         if (SP_IS_STAR((SPItem *) items->data)) {
2099             n_selected++;
2100             repr = SP_OBJECT_REPR((SPItem *) items->data);
2101         }
2102     }
2104     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2106     if (n_selected == 0) {
2107         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2108     } else if (n_selected == 1) {
2109         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2111         if (repr) {
2112             g_object_set_data( tbl, "repr", repr );
2113             Inkscape::GC::anchor(repr);
2114             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2115             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2116         }
2117     } else {
2118         // FIXME: implement averaging of all parameters for multiple selected stars
2119         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2120         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2121     }
2125 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2127     // FIXME: in this and all other _default functions, set some flag telling the value_changed
2128     // callbacks to lump all the changes for all selected objects in one undo step
2130     GtkAdjustment *adj = 0;
2132     // fixme: make settable in prefs!
2133     gint mag = 5;
2134     gdouble prop = 0.5;
2135     gboolean flat = FALSE;
2136     gdouble randomized = 0;
2137     gdouble rounded = 0;
2139     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2140     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2142     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2143     gtk_action_set_sensitive( sb2, !flat );
2145     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2146     gtk_adjustment_set_value(adj, mag);
2147     gtk_adjustment_value_changed(adj);
2149     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2150     gtk_adjustment_set_value(adj, prop);
2151     gtk_adjustment_value_changed(adj);
2153     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2154     gtk_adjustment_set_value(adj, rounded);
2155     gtk_adjustment_value_changed(adj);
2157     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2158     gtk_adjustment_set_value(adj, randomized);
2159     gtk_adjustment_value_changed(adj);
2163 void
2164 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2166     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2167     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2168     GtkWidget *l = gtk_label_new(NULL);
2169     gtk_label_set_markup(GTK_LABEL(l), title);
2170     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2171     if ( GTK_IS_TOOLBAR(tbl) ) {
2172         gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2173     } else {
2174         gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2175     }
2176     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2180 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2182     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2184     {
2185         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2186         ege_output_action_set_use_markup( act, TRUE );
2187         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2188         g_object_set_data( holder, "mode_action", act );
2189     }
2191     {
2192         EgeAdjustmentAction* eact = 0;
2193         gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2194         bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2196         /* Flatsided checkbox */
2197         {
2198             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2200             GtkTreeIter iter;
2201             gtk_list_store_append( model, &iter );
2202             gtk_list_store_set( model, &iter,
2203                                 0, _("Polygon"),
2204                                 1, _("Regular polygon (with one handle) instead of a star"),
2205                                 2, "star_flat",
2206                                 -1 );
2208             gtk_list_store_append( model, &iter );
2209             gtk_list_store_set( model, &iter,
2210                                 0, _("Star"),
2211                                 1, _("Star instead of a regular polygon (with one handle)"),
2212                                 2, "star_angled",
2213                                 -1 );
2215             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2216             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2217             g_object_set_data( holder, "flat_action", act );
2219             ege_select_one_action_set_appearance( act, "full" );
2220             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2221             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2222             ege_select_one_action_set_icon_column( act, 2 );
2223             ege_select_one_action_set_icon_size( act, secondarySize );
2224             ege_select_one_action_set_tooltip_column( act, 1  );
2226             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2227             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2228         }
2230         /* Magnitude */
2231         {
2232         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2233         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2234         eact = create_adjustment_action( "MagnitudeAction",
2235                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2236                                          "tools.shapes.star", "magnitude", 3,
2237                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2238                                          3, 1024, 1, 5,
2239                                          labels, values, G_N_ELEMENTS(labels),
2240                                          sp_stb_magnitude_value_changed,
2241                                          1.0, 0 );
2242         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2243         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2244         }
2246         /* Spoke ratio */
2247         {
2248         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2249         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2250         eact = create_adjustment_action( "SpokeAction",
2251                                          _("Spoke ratio"), _("Spoke ratio:"),
2252                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2253                                          // Base radius is the same for the closest handle.
2254                                          _("Base radius to tip radius ratio"),
2255                                          "tools.shapes.star", "proportion", 0.5,
2256                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2257                                          0.01, 1.0, 0.01, 0.1,
2258                                          labels, values, G_N_ELEMENTS(labels),
2259                                          sp_stb_proportion_value_changed );
2260         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2261         g_object_set_data( holder, "prop_action", eact );
2262         }
2264         if ( !isFlatSided ) {
2265             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2266         } else {
2267             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2268         }
2270         /* Roundedness */
2271         {
2272         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2273         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2274         eact = create_adjustment_action( "RoundednessAction",
2275                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2276                                          "tools.shapes.star", "rounded", 0.0,
2277                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2278                                          -10.0, 10.0, 0.01, 0.1,
2279                                          labels, values, G_N_ELEMENTS(labels),
2280                                          sp_stb_rounded_value_changed );
2281         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2282         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2283         }
2285         /* Randomization */
2286         {
2287         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2288         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2289         eact = create_adjustment_action( "RandomizationAction",
2290                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2291                                          "tools.shapes.star", "randomized", 0.0,
2292                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2293                                          -10.0, 10.0, 0.001, 0.01,
2294                                          labels, values, G_N_ELEMENTS(labels),
2295                                          sp_stb_randomized_value_changed, 0.1, 3 );
2296         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2297         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2298         }
2299     }
2301     {
2302         /* Reset */
2303         {
2304             GtkAction* act = gtk_action_new( "StarResetAction",
2305                                              _("Defaults"),
2306                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2307                                              GTK_STOCK_CLEAR );
2308             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2309             gtk_action_group_add_action( mainActions, act );
2310             gtk_action_set_sensitive( act, TRUE );
2311         }
2312     }
2314     sigc::connection *connection = new sigc::connection(
2315         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2316         );
2317     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2318     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2322 //########################
2323 //##       Rect         ##
2324 //########################
2326 static void sp_rtb_sensitivize( GObject *tbl )
2328     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2329     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2330     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2332     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2333         gtk_action_set_sensitive( not_rounded, FALSE );
2334     } else {
2335         gtk_action_set_sensitive( not_rounded, TRUE );
2336     }
2340 static void
2341 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2342                           void (*setter)(SPRect *, gdouble))
2344     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2346     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2347     SPUnit const *unit = tracker->getActiveUnit();
2349     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2350         prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2351     }
2353     // quit if run by the attr_changed listener
2354     if (g_object_get_data( tbl, "freeze" )) {
2355         return;
2356     }
2358     // in turn, prevent listener from responding
2359     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2361     bool modmade = false;
2362     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2363     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2364         if (SP_IS_RECT(items->data)) {
2365             if (adj->value != 0) {
2366                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2367             } else {
2368                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2369             }
2370             modmade = true;
2371         }
2372     }
2374     sp_rtb_sensitivize( tbl );
2376     if (modmade) {
2377         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2378                                    _("Change rectangle"));
2379     }
2381     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2384 static void
2385 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2387     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2390 static void
2391 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2393     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2396 static void
2397 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2399     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2402 static void
2403 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2405     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2410 static void
2411 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2413     GtkAdjustment *adj = 0;
2415     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2416     gtk_adjustment_set_value(adj, 0.0);
2417     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2418     gtk_adjustment_value_changed(adj);
2420     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2421     gtk_adjustment_set_value(adj, 0.0);
2422     gtk_adjustment_value_changed(adj);
2424     sp_rtb_sensitivize( obj );
2427 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2428                                        gchar const */*old_value*/, gchar const */*new_value*/,
2429                                        bool /*is_interactive*/, gpointer data)
2431     GObject *tbl = G_OBJECT(data);
2433     // quit if run by the _changed callbacks
2434     if (g_object_get_data( tbl, "freeze" )) {
2435         return;
2436     }
2438     // in turn, prevent callbacks from responding
2439     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2441     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2442     SPUnit const *unit = tracker->getActiveUnit();
2444     gpointer item = g_object_get_data( tbl, "item" );
2445     if (item && SP_IS_RECT(item)) {
2446         {
2447             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2448             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2449             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2450         }
2452         {
2453             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2454             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2455             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2456         }
2458         {
2459             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2460             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2461             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2462         }
2464         {
2465             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2466             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2467             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2468         }
2469     }
2471     sp_rtb_sensitivize( tbl );
2473     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2477 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2478     NULL, /* child_added */
2479     NULL, /* child_removed */
2480     rect_tb_event_attr_changed,
2481     NULL, /* content_changed */
2482     NULL  /* order_changed */
2483 };
2485 /**
2486  *  \param selection should not be NULL.
2487  */
2488 static void
2489 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2491     int n_selected = 0;
2492     Inkscape::XML::Node *repr = NULL;
2493     SPItem *item = NULL;
2495     if ( g_object_get_data( tbl, "repr" ) ) {
2496         g_object_set_data( tbl, "item", NULL );
2497     }
2498     purge_repr_listener( tbl, tbl );
2500     for (GSList const *items = selection->itemList();
2501          items != NULL;
2502          items = items->next) {
2503         if (SP_IS_RECT((SPItem *) items->data)) {
2504             n_selected++;
2505             item = (SPItem *) items->data;
2506             repr = SP_OBJECT_REPR(item);
2507         }
2508     }
2510     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2512     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2514     if (n_selected == 0) {
2515         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2517         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2518         gtk_action_set_sensitive(w, FALSE);
2519         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2520         gtk_action_set_sensitive(h, FALSE);
2522     } else if (n_selected == 1) {
2523         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2524         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2526         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2527         gtk_action_set_sensitive(w, TRUE);
2528         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2529         gtk_action_set_sensitive(h, TRUE);
2531         if (repr) {
2532             g_object_set_data( tbl, "repr", repr );
2533             g_object_set_data( tbl, "item", item );
2534             Inkscape::GC::anchor(repr);
2535             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2536             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2537         }
2538     } else {
2539         // FIXME: implement averaging of all parameters for multiple selected
2540         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2541         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2542         sp_rtb_sensitivize( tbl );
2543     }
2547 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2549     EgeAdjustmentAction* eact = 0;
2550     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2552     {
2553         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2554         ege_output_action_set_use_markup( act, TRUE );
2555         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2556         g_object_set_data( holder, "mode_action", act );
2557     }
2559     // rx/ry units menu: create
2560     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2561     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2562     // fixme: add % meaning per cent of the width/height
2563     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2564     g_object_set_data( holder, "tracker", tracker );
2566     /* W */
2567     {
2568         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2569         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2570         eact = create_adjustment_action( "RectWidthAction",
2571                                          _("Width"), _("W:"), _("Width of rectangle"),
2572                                          "tools.shapes.rect", "width", 0,
2573                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2574                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2575                                          labels, values, G_N_ELEMENTS(labels),
2576                                          sp_rtb_width_value_changed );
2577         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2578         g_object_set_data( holder, "width_action", eact );
2579         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2580         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2581     }
2583     /* H */
2584     {
2585         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2586         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2587         eact = create_adjustment_action( "RectHeightAction",
2588                                          _("Height"), _("H:"), _("Height of rectangle"),
2589                                          "tools.shapes.rect", "height", 0,
2590                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2591                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2592                                          labels, values, G_N_ELEMENTS(labels),
2593                                          sp_rtb_height_value_changed );
2594         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2595         g_object_set_data( holder, "height_action", eact );
2596         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2597         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2598     }
2600     /* rx */
2601     {
2602         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2603         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2604         eact = create_adjustment_action( "RadiusXAction",
2605                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2606                                          "tools.shapes.rect", "rx", 0,
2607                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2608                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2609                                          labels, values, G_N_ELEMENTS(labels),
2610                                          sp_rtb_rx_value_changed);
2611         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2612         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2613     }
2615     /* ry */
2616     {
2617         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2618         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2619         eact = create_adjustment_action( "RadiusYAction",
2620                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2621                                          "tools.shapes.rect", "ry", 0,
2622                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2623                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2624                                          labels, values, G_N_ELEMENTS(labels),
2625                                          sp_rtb_ry_value_changed);
2626         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2627         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2628     }
2630     // add the units menu
2631     {
2632         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2633         gtk_action_group_add_action( mainActions, act );
2634     }
2636     /* Reset */
2637     {
2638         InkAction* inky = ink_action_new( "RectResetAction",
2639                                           _("Not rounded"),
2640                                           _("Make corners sharp"),
2641                                           "squared_corner",
2642                                           secondarySize );
2643         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2644         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2645         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2646         g_object_set_data( holder, "not_rounded", inky );
2647     }
2649     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2650     sp_rtb_sensitivize( holder );
2652     sigc::connection *connection = new sigc::connection(
2653         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2654         );
2655     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2656     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2659 //########################
2660 //##       3D Box       ##
2661 //########################
2663 // normalize angle so that it lies in the interval [0,360]
2664 static double box3d_normalize_angle (double a) {
2665     double angle = a + ((int) (a/360.0))*360;
2666     if (angle < 0) {
2667         angle += 360.0;
2668     }
2669     return angle;
2672 static void
2673 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2674                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2675     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2676     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2677     //       are reset).
2678     bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2680     if (is_infinite) {
2681         gtk_toggle_action_set_active(tact, TRUE);
2682         gtk_action_set_sensitive(act, TRUE);
2684         double angle = persp3d_get_infinite_angle(persp, axis);
2685         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2686             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2687         }
2688     } else {
2689         gtk_toggle_action_set_active(tact, FALSE);
2690         gtk_action_set_sensitive(act, FALSE);
2691     }
2694 static void
2695 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2696     if (!persp_repr) {
2697         g_print ("No perspective given to box3d_resync_toolbar().\n");
2698         return;
2699     }
2701     GtkWidget *tbl = GTK_WIDGET(data);
2702     GtkAdjustment *adj = 0;
2703     GtkAction *act = 0;
2704     GtkToggleAction *tact = 0;
2705     Persp3D *persp = persp3d_get_from_repr(persp_repr);
2706     {
2707         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2708         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2709         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2711         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2712     }
2713     {
2714         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2715         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2716         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2718         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2719     }
2720     {
2721         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2722         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2723         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2725         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2726     }
2729 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2730                                                   gchar const */*old_value*/, gchar const */*new_value*/,
2731                                                   bool /*is_interactive*/, gpointer data)
2733     GtkWidget *tbl = GTK_WIDGET(data);
2735     // quit if run by the attr_changed listener
2736     // note: it used to work without the differently called freeze_ attributes (here and in
2737     //       box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2738     if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2739         return;
2740     }
2742     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2743     // sp_document_maybe_done() when the document is undo insensitive)
2744     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2746     // TODO: Only update the appropriate part of the toolbar
2747 //    if (!strcmp(name, "inkscape:vp_z")) {
2748         box3d_resync_toolbar(repr, G_OBJECT(tbl));
2749 //    }
2751     Persp3D *persp = persp3d_get_from_repr(repr);
2752     persp3d_update_box_reprs(persp);
2754     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2757 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2759     NULL, /* child_added */
2760     NULL, /* child_removed */
2761     box3d_persp_tb_event_attr_changed,
2762     NULL, /* content_changed */
2763     NULL  /* order_changed */
2764 };
2766 /**
2767  *  \param selection Should not be NULL.
2768  */
2769 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2770 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2771 static void
2772 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2774     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2775     // disable the angle entry fields for this direction (otherwise entering a value in them should only
2776     // update the perspectives with infinite VPs and leave the other ones untouched).
2778     Inkscape::XML::Node *persp_repr = NULL;
2779     purge_repr_listener(tbl, tbl);
2781     SPItem *item = selection->singleItem();
2782     if (item && SP_IS_BOX3D(item)) {
2783         // FIXME: Also deal with multiple selected boxes
2784         SPBox3D *box = SP_BOX3D(item);
2785         Persp3D *persp = box3d_get_perspective(box);
2786         persp_repr = SP_OBJECT_REPR(persp);
2787         if (persp_repr) {
2788             g_object_set_data(tbl, "repr", persp_repr);
2789             Inkscape::GC::anchor(persp_repr);
2790             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2791             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2792         }
2794         inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2795         prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2797         box3d_resync_toolbar(persp_repr, tbl);
2798     }
2801 static void
2802 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2804     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2805     SPDocument *document = sp_desktop_document(desktop);
2807     // quit if run by the attr_changed listener
2808     // note: it used to work without the differently called freeze_ attributes (here and in
2809     //       box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2810     if (g_object_get_data( dataKludge, "freeze_attr" )) {
2811         return;
2812     }
2814     // in turn, prevent listener from responding
2815     g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2817     //Persp3D *persp = document->current_persp3d;
2818     std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
2819     if (sel_persps.empty()) {
2820         // this can happen when the document is created; we silently ignore it
2821         return;
2822     }
2823     Persp3D *persp = sel_persps.front();
2825     persp->tmat.set_infinite_direction (axis, adj->value);
2826     SP_OBJECT(persp)->updateRepr();
2828     // TODO: use the correct axis here, too
2829     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2831     g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2835 static void
2836 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2838     box3d_angle_value_changed(adj, dataKludge, Proj::X);
2841 static void
2842 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2844     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2847 static void
2848 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2850     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2854 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2856     // TODO: Take all selected perspectives into account
2857     std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
2858     if (sel_persps.empty()) {
2859         // this can happen when the document is created; we silently ignore it
2860         return;
2861     }
2862     Persp3D *persp = sel_persps.front();
2864     bool set_infinite = gtk_toggle_action_get_active(act);
2865     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2868 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2870     box3d_vp_state_changed(act, box3d_angle, Proj::X);
2873 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2875     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2878 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2880     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2883 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2885     EgeAdjustmentAction* eact = 0;
2886     SPDocument *document = sp_desktop_document (desktop);
2887     Persp3D *persp = document->current_persp3d;
2889     EgeAdjustmentAction* box3d_angle_x = 0;
2890     EgeAdjustmentAction* box3d_angle_y = 0;
2891     EgeAdjustmentAction* box3d_angle_z = 0;
2893     /* Angle X */
2894     {
2895         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2896         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2897         eact = create_adjustment_action( "3DBoxAngleXAction",
2898                                          _("Angle in X direction"), _("Angle X:"),
2899                                          // Translators: PL is short for 'perspective line'
2900                                          _("Angle of PLs in X direction"),
2901                                          "tools.shapes.3dbox", "box3d_angle_x", 30,
2902                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2903                                          -360.0, 360.0, 1.0, 10.0,
2904                                          labels, values, G_N_ELEMENTS(labels),
2905                                          box3d_angle_x_value_changed );
2906         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2907         g_object_set_data( holder, "box3d_angle_x_action", eact );
2908         box3d_angle_x = eact;
2909     }
2911     if (!persp3d_VP_is_finite(persp, Proj::X)) {
2912         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2913     } else {
2914         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2915     }
2918     /* VP X state */
2919     {
2920         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2921                                                       // Translators: VP is short for 'vanishing point'
2922                                                       _("State of VP in X direction"),
2923                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2924                                                       "toggle_vp_x",
2925                                                       Inkscape::ICON_SIZE_DECORATION );
2926         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2927         g_object_set_data( holder, "box3d_vp_x_state_action", act );
2928         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2929         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2930         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2931     }
2933     /* Angle Y */
2934     {
2935         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2936         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2937         eact = create_adjustment_action( "3DBoxAngleYAction",
2938                                          _("Angle in Y direction"), _("Angle Y:"),
2939                                          // Translators: PL is short for 'perspective line'
2940                                          _("Angle of PLs in Y direction"),
2941                                          "tools.shapes.3dbox", "box3d_angle_y", 30,
2942                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2943                                          -360.0, 360.0, 1.0, 10.0,
2944                                          labels, values, G_N_ELEMENTS(labels),
2945                                          box3d_angle_y_value_changed );
2946         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2947         g_object_set_data( holder, "box3d_angle_y_action", eact );
2948         box3d_angle_y = eact;
2949     }
2951     if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2952         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2953     } else {
2954         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2955     }
2957     /* VP Y state */
2958     {
2959         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2960                                                       // Translators: VP is short for 'vanishing point'
2961                                                       _("State of VP in Y direction"),
2962                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2963                                                       "toggle_vp_y",
2964                                                       Inkscape::ICON_SIZE_DECORATION );
2965         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2966         g_object_set_data( holder, "box3d_vp_y_state_action", act );
2967         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2968         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2969         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2970     }
2972     /* Angle Z */
2973     {
2974         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2975         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2976         eact = create_adjustment_action( "3DBoxAngleZAction",
2977                                          _("Angle in Z direction"), _("Angle Z:"),
2978                                          // Translators: PL is short for 'perspective line'
2979                                          _("Angle of PLs in Z direction"),
2980                                          "tools.shapes.3dbox", "box3d_angle_z", 30,
2981                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2982                                          -360.0, 360.0, 1.0, 10.0,
2983                                          labels, values, G_N_ELEMENTS(labels),
2984                                          box3d_angle_z_value_changed );
2985         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2986         g_object_set_data( holder, "box3d_angle_z_action", eact );
2987         box3d_angle_z = eact;
2988     }
2990     if (!persp3d_VP_is_finite(persp, Proj::Z)) {
2991         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2992     } else {
2993         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2994     }
2996     /* VP Z state */
2997     {
2998         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
2999                                                       // Translators: VP is short for 'vanishing point'
3000                                                       _("State of VP in Z direction"),
3001                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3002                                                       "toggle_vp_z",
3003                                                       Inkscape::ICON_SIZE_DECORATION );
3004         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3005         g_object_set_data( holder, "box3d_vp_z_state_action", act );
3006         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3007         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3008         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3009     }
3011     sigc::connection *connection = new sigc::connection(
3012         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3013        );
3014     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3015     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3018 //########################
3019 //##       Spiral       ##
3020 //########################
3022 static void
3023 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
3025     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3027     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3028         prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
3029     }
3031     // quit if run by the attr_changed listener
3032     if (g_object_get_data( tbl, "freeze" )) {
3033         return;
3034     }
3036     // in turn, prevent listener from responding
3037     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3039     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3041     bool modmade = false;
3042     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3043          items != NULL;
3044          items = items->next)
3045     {
3046         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3047             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3048             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3049             SP_OBJECT((SPItem *) items->data)->updateRepr();
3050             modmade = true;
3051         }
3052     }
3054     g_free(namespaced_name);
3056     if (modmade) {
3057         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3058                                    _("Change spiral"));
3059     }
3061     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3064 static void
3065 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3067     sp_spl_tb_value_changed(adj, tbl, "revolution");
3070 static void
3071 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3073     sp_spl_tb_value_changed(adj, tbl, "expansion");
3076 static void
3077 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3079     sp_spl_tb_value_changed(adj, tbl, "t0");
3082 static void
3083 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3085     GtkWidget *tbl = GTK_WIDGET(obj);
3087     GtkAdjustment *adj;
3089     // fixme: make settable
3090     gdouble rev = 5;
3091     gdouble exp = 1.0;
3092     gdouble t0 = 0.0;
3094     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3095     gtk_adjustment_set_value(adj, rev);
3096     gtk_adjustment_value_changed(adj);
3098     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3099     gtk_adjustment_set_value(adj, exp);
3100     gtk_adjustment_value_changed(adj);
3102     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3103     gtk_adjustment_set_value(adj, t0);
3104     gtk_adjustment_value_changed(adj);
3106     spinbutton_defocus(GTK_OBJECT(tbl));
3110 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3111                                          gchar const */*old_value*/, gchar const */*new_value*/,
3112                                          bool /*is_interactive*/, gpointer data)
3114     GtkWidget *tbl = GTK_WIDGET(data);
3116     // quit if run by the _changed callbacks
3117     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3118         return;
3119     }
3121     // in turn, prevent callbacks from responding
3122     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3124     GtkAdjustment *adj;
3125     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3126     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3128     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3129     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3131     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3132     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3134     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3138 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3139     NULL, /* child_added */
3140     NULL, /* child_removed */
3141     spiral_tb_event_attr_changed,
3142     NULL, /* content_changed */
3143     NULL  /* order_changed */
3144 };
3146 static void
3147 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3149     int n_selected = 0;
3150     Inkscape::XML::Node *repr = NULL;
3152     purge_repr_listener( tbl, tbl );
3154     for (GSList const *items = selection->itemList();
3155          items != NULL;
3156          items = items->next)
3157     {
3158         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3159             n_selected++;
3160             repr = SP_OBJECT_REPR((SPItem *) items->data);
3161         }
3162     }
3164     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3166     if (n_selected == 0) {
3167         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3168     } else if (n_selected == 1) {
3169         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3171         if (repr) {
3172             g_object_set_data( tbl, "repr", repr );
3173             Inkscape::GC::anchor(repr);
3174             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3175             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3176         }
3177     } else {
3178         // FIXME: implement averaging of all parameters for multiple selected
3179         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3180         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3181     }
3185 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3187     EgeAdjustmentAction* eact = 0;
3188     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3190     {
3191         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3192         ege_output_action_set_use_markup( act, TRUE );
3193         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3194         g_object_set_data( holder, "mode_action", act );
3195     }
3197     /* Revolution */
3198     {
3199         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3200         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3201         eact = create_adjustment_action( "SpiralRevolutionAction",
3202                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3203                                          "tools.shapes.spiral", "revolution", 3.0,
3204                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3205                                          0.01, 1024.0, 0.1, 1.0,
3206                                          labels, values, G_N_ELEMENTS(labels),
3207                                          sp_spl_tb_revolution_value_changed, 1, 2);
3208         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3209     }
3211     /* Expansion */
3212     {
3213         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3214         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3215         eact = create_adjustment_action( "SpiralExpansionAction",
3216                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3217                                          "tools.shapes.spiral", "expansion", 1.0,
3218                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3219                                          0.0, 1000.0, 0.01, 1.0,
3220                                          labels, values, G_N_ELEMENTS(labels),
3221                                          sp_spl_tb_expansion_value_changed);
3222         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3223     }
3225     /* T0 */
3226     {
3227         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3228         gdouble values[] = {0, 0.5, 0.9};
3229         eact = create_adjustment_action( "SpiralT0Action",
3230                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3231                                          "tools.shapes.spiral", "t0", 0.0,
3232                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3233                                          0.0, 0.999, 0.01, 1.0,
3234                                          labels, values, G_N_ELEMENTS(labels),
3235                                          sp_spl_tb_t0_value_changed);
3236         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3237     }
3239     /* Reset */
3240     {
3241         InkAction* inky = ink_action_new( "SpiralResetAction",
3242                                           _("Defaults"),
3243                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3244                                           GTK_STOCK_CLEAR,
3245                                           secondarySize );
3246         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3247         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3248     }
3251     sigc::connection *connection = new sigc::connection(
3252         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3253         );
3254     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3255     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3258 //########################
3259 //##     Pen/Pencil    ##
3260 //########################
3262 static void sp_pc_spiro_spline_mode_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
3264     prefs_set_int_attribute("tools.freehand", "spiro-spline-mode", ege_select_one_action_get_active(act));
3267 static void sp_add_spiro_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3269     // FIXME: No action is needed, we only want a simple label. But sp_toolbox_add_label() always
3270     //        adds the label at the end of the toolbar, whence this workarund. How to avoid this?
3271     {
3272         EgeOutputAction* act = ege_output_action_new(
3273             tool_is_pencil ?
3274             "FreehandModeActionPencilTemp" :
3275             "FreehandModeActionPenTemp",
3276             _("<b>Mode:</b>"), "", 0 );
3277         ege_output_action_set_use_markup( act, TRUE );
3278         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3279         g_object_set_data( holder, "freehand_mode_action", act );
3280     }
3282     /* Freehand mode toggle buttons */
3283     {
3284         //gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
3285         //bool isSpiroMode = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
3286         guint spiroMode = prefs_get_int_attribute("tools.freehand", "spiro-spline-mode", 0);
3287         Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3289         {
3290             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3292             GtkTreeIter iter;
3293             gtk_list_store_append( model, &iter );
3294             gtk_list_store_set( model, &iter,
3295                                 0, _("Bézier"),
3296                                 1, _("Regular Bézier mode"),
3297                                 2, "bezier_mode",
3298                                 -1 );
3300             gtk_list_store_append( model, &iter );
3301             gtk_list_store_set( model, &iter,
3302                                 0, _("Spiro"),
3303                                 1, _("Spiro splines mode"),
3304                                 2, "spiro_splines_mode",
3305                                 -1 );
3307             EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3308                                                                 "FreehandModeActionPencil" :
3309                                                                 "FreehandModeActionPen",
3310                                                                 (""), (""), NULL, GTK_TREE_MODEL(model) );
3311             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3312             g_object_set_data( holder, "freehande_mode_action", act );
3314             ege_select_one_action_set_appearance( act, "full" );
3315             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3316             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3317             ege_select_one_action_set_icon_column( act, 2 );
3318             ege_select_one_action_set_icon_size( act, secondarySize );
3319             ege_select_one_action_set_tooltip_column( act, 1  );
3321             ege_select_one_action_set_active( act, spiroMode);
3322             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_pc_spiro_spline_mode_changed), holder);
3323         }
3324     }
3327 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3329     sp_add_spiro_toggle(mainActions, holder, false);
3333 static void
3334 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3336     GtkWidget *tbl = GTK_WIDGET(obj);
3338     GtkAdjustment *adj;
3340     // fixme: make settable
3341     gdouble tolerance = 4;
3343     adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3344     gtk_adjustment_set_value(adj, tolerance);
3345     gtk_adjustment_value_changed(adj);
3347     spinbutton_defocus(GTK_OBJECT(tbl));
3350 static void
3351 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3354     // quit if run by the attr_changed listener
3355     if (g_object_get_data( tbl, "freeze" )) {
3356         return;
3357     }
3358     // in turn, prevent listener from responding
3359     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3360     prefs_set_double_attribute("tools.freehand.pencil", 
3361                                "tolerance", adj->value);
3362     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3363     
3368 static void
3369 sp_pencil_tb_tolerance_value_changed_external(Inkscape::XML::Node *repr, 
3370                                               const gchar *key, 
3371                                               const gchar *oldval, 
3372                                               const gchar *newval, 
3373                                               bool is_interactive, 
3374                                               void * data)
3376     GObject* tbl = G_OBJECT(data);
3377     if (g_object_get_data( tbl, "freeze" )) {
3378         return;
3379     }    
3381     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3383     GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl,
3384                                                             "tolerance");
3385     
3386     double v = prefs_get_double_attribute("tools.freehand.pencil", 
3387                                             "tolerance", adj->value);
3388     gtk_adjustment_set_value(adj, v);
3389     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3390     
3393 static Inkscape::XML::NodeEventVector pencil_node_events = 
3395     NULL, 
3396     NULL, 
3397     sp_pencil_tb_tolerance_value_changed_external,
3398     NULL, 
3399     NULL,
3400 };
3403 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3405     sp_add_spiro_toggle(mainActions, holder, true);
3407     EgeAdjustmentAction* eact = 0;
3409     /* Tolerance */
3410     {
3412         eact = create_adjustment_action( "PencilToleranceAction",
3413                  _("Number of pixels allowed in interpolating"), 
3414                                          _("Tolerance:"), _("Tolerance"),
3415                                          "tools.freehand.pencil", "tolerance", 
3416                                          3.0,
3417                                          GTK_WIDGET(desktop->canvas), NULL, 
3418                                          holder, TRUE, "altx-pencil",
3419                                          0.5, 100.0, 0.5, 1.0, 
3420                                          NULL, NULL, 0,
3421                                          sp_pencil_tb_tolerance_value_changed,
3422                                          1, 2);
3423         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3425         Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, 
3426                                                       "tools.freehand.pencil");
3427         repr->addListener(&pencil_node_events, G_OBJECT(holder));
3428         g_object_set_data(G_OBJECT(holder), "repr", repr);
3430     }
3431     /* Reset */
3432     {
3433         InkAction* inky = ink_action_new( "PencilResetAction",
3434                                           _("Defaults"),
3435                                           _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3436                                           GTK_STOCK_CLEAR,
3437                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3438         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
3439         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3440     }
3441     
3442     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3447 //########################
3448 //##       Tweak        ##
3449 //########################
3451 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3453     prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3456 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3458     prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3461 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3463     prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3466 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3468     int mode = ege_select_one_action_get_active( act );
3469     prefs_set_int_attribute("tools.tweak", "mode", mode);
3471     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3472     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3473     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3474     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3475     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3476     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3477     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3478         if (doh) gtk_action_set_sensitive (doh, TRUE);
3479         if (dos) gtk_action_set_sensitive (dos, TRUE);
3480         if (dol) gtk_action_set_sensitive (dol, TRUE);
3481         if (doo) gtk_action_set_sensitive (doo, TRUE);
3482         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3483         if (fid) gtk_action_set_sensitive (fid, FALSE);
3484     } else {
3485         if (doh) gtk_action_set_sensitive (doh, FALSE);
3486         if (dos) gtk_action_set_sensitive (dos, FALSE);
3487         if (dol) gtk_action_set_sensitive (dol, FALSE);
3488         if (doo) gtk_action_set_sensitive (doo, FALSE);
3489         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3490         if (fid) gtk_action_set_sensitive (fid, TRUE);
3491     }
3494 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3496     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3499 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3500     bool show = gtk_toggle_action_get_active( act );
3501     prefs_set_int_attribute ("tools.tweak", "doh",  show ? 1 : 0);
3503 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3504     bool show = gtk_toggle_action_get_active( act );
3505     prefs_set_int_attribute ("tools.tweak", "dos",  show ? 1 : 0);
3507 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3508     bool show = gtk_toggle_action_get_active( act );
3509     prefs_set_int_attribute ("tools.tweak", "dol",  show ? 1 : 0);
3511 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3512     bool show = gtk_toggle_action_get_active( act );
3513     prefs_set_int_attribute ("tools.tweak", "doo",  show ? 1 : 0);
3516 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3518     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3520     {
3521         /* Width */
3522         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3523         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3524         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3525                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3526                                                               "tools.tweak", "width", 15,
3527                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3528                                                               1, 100, 1.0, 10.0,
3529                                                               labels, values, G_N_ELEMENTS(labels),
3530                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
3531         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3532         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3533     }
3536     {
3537         /* Force */
3538         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3539         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3540         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3541                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
3542                                                               "tools.tweak", "force", 20,
3543                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3544                                                               1, 100, 1.0, 10.0,
3545                                                               labels, values, G_N_ELEMENTS(labels),
3546                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
3547         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3548         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3549     }
3551     /* Mode */
3552     {
3553         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3555         GtkTreeIter iter;
3556         gtk_list_store_append( model, &iter );
3557         gtk_list_store_set( model, &iter,
3558                             0, _("Push mode"),
3559                             1, _("Push parts of paths in any direction"),
3560                             2, "tweak_push_mode",
3561                             -1 );
3563         gtk_list_store_append( model, &iter );
3564         gtk_list_store_set( model, &iter,
3565                             0, _("Shrink mode"),
3566                             1, _("Shrink (inset) parts of paths"),
3567                             2, "tweak_shrink_mode",
3568                             -1 );
3570         gtk_list_store_append( model, &iter );
3571         gtk_list_store_set( model, &iter,
3572                             0, _("Grow mode"),
3573                             1, _("Grow (outset) parts of paths"),
3574                             2, "tweak_grow_mode",
3575                             -1 );
3577         gtk_list_store_append( model, &iter );
3578         gtk_list_store_set( model, &iter,
3579                             0, _("Attract mode"),
3580                             1, _("Attract parts of paths towards cursor"),
3581                             2, "tweak_attract_mode",
3582                             -1 );
3584         gtk_list_store_append( model, &iter );
3585         gtk_list_store_set( model, &iter,
3586                             0, _("Repel mode"),
3587                             1, _("Repel parts of paths from cursor"),
3588                             2, "tweak_repel_mode",
3589                             -1 );
3591         gtk_list_store_append( model, &iter );
3592         gtk_list_store_set( model, &iter,
3593                             0, _("Roughen mode"),
3594                             1, _("Roughen parts of paths"),
3595                             2, "tweak_roughen_mode",
3596                             -1 );
3598         gtk_list_store_append( model, &iter );
3599         gtk_list_store_set( model, &iter,
3600                             0, _("Color paint mode"),
3601                             1, _("Paint the tool's color upon selected objects"),
3602                             2, "tweak_colorpaint_mode",
3603                             -1 );
3605         gtk_list_store_append( model, &iter );
3606         gtk_list_store_set( model, &iter,
3607                             0, _("Color jitter mode"),
3608                             1, _("Jitter the colors of selected objects"),
3609                             2, "tweak_colorjitter_mode",
3610                             -1 );
3612         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3613         g_object_set( act, "short_label", _("Mode:"), NULL );
3614         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3615         g_object_set_data( holder, "mode_action", act );
3617         ege_select_one_action_set_appearance( act, "full" );
3618         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3619         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3620         ege_select_one_action_set_icon_column( act, 2 );
3621         ege_select_one_action_set_icon_size( act, secondarySize );
3622         ege_select_one_action_set_tooltip_column( act, 1  );
3624         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3625         ege_select_one_action_set_active( act, mode );
3626         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3628         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3629     }
3631     guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3633     {
3634         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3635         ege_output_action_set_use_markup( act, TRUE );
3636         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3637         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3638             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3639         g_object_set_data( holder, "tweak_channels_label", act);
3640     }
3642     {
3643         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3644                                                       _("Hue"),
3645                                                       _("In color mode, act on objects' hue"),
3646                                                       NULL,
3647                                                       Inkscape::ICON_SIZE_DECORATION );
3648         //TRANSLATORS:  "H" here stands for hue
3649         g_object_set( act, "short_label", _("H"), NULL );
3650         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3651         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3652         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3653         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3654             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3655         g_object_set_data( holder, "tweak_doh", act);
3656     }
3657     {
3658         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3659                                                       _("Saturation"),
3660                                                       _("In color mode, act on objects' saturation"),
3661                                                       NULL,
3662                                                       Inkscape::ICON_SIZE_DECORATION );
3663         //TRANSLATORS: "S" here stands for Saturation
3664         g_object_set( act, "short_label", _("S"), NULL );
3665         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3666         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3667         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3668         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3669             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3670         g_object_set_data( holder, "tweak_dos", act );
3671     }
3672     {
3673         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3674                                                       _("Lightness"),
3675                                                       _("In color mode, act on objects' lightness"),
3676                                                       NULL,
3677                                                       Inkscape::ICON_SIZE_DECORATION );
3678         //TRANSLATORS: "L" here stands for Lightness
3679         g_object_set( act, "short_label", _("L"), NULL );
3680         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3681         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3682         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3683         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3684             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3685         g_object_set_data( holder, "tweak_dol", act );
3686     }
3687     {
3688         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3689                                                       _("Opacity"),
3690                                                       _("In color mode, act on objects' opacity"),
3691                                                       NULL,
3692                                                       Inkscape::ICON_SIZE_DECORATION );
3693         //TRANSLATORS: "O" here stands for Opacity
3694         g_object_set( act, "short_label", _("O"), NULL );
3695         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3696         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3697         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
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_doo", act );
3701     }
3703     {   /* Fidelity */
3704         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3705         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3706         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3707                                                               _("Fidelity"), _("Fidelity:"),
3708                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3709                                                               "tools.tweak", "fidelity", 50,
3710                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3711                                                               1, 100, 1.0, 10.0,
3712                                                               labels, values, G_N_ELEMENTS(labels),
3713                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
3714         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3715         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3716         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3717             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3718         g_object_set_data( holder, "tweak_fidelity", eact );
3719     }
3722     /* Use Pressure button */
3723     {
3724         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3725                                                       _("Pressure"),
3726                                                       _("Use the pressure of the input device to alter the force of tweak action"),
3727                                                       "use_pressure",
3728                                                       Inkscape::ICON_SIZE_DECORATION );
3729         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3730         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3731         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3732     }
3737 //########################
3738 //##     Calligraphy    ##
3739 //########################
3740 static void update_presets_list(GObject *dataKludge ){
3741     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3742     if (sel) {
3743         ege_select_one_action_set_active(sel, 0 );
3744     }
3747 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3749     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
3750     update_presets_list(tbl);
3753 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3755     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
3756     update_presets_list(tbl);
3759 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3761     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3762     update_presets_list(tbl);
3765 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3767     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3768     update_presets_list(tbl);
3771 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3773     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
3774     update_presets_list(tbl);
3777 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3779     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3780     update_presets_list(tbl);
3783 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3785     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3786     update_presets_list(tbl);
3789 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3791     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3792     update_presets_list(tbl);
3795 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
3797     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3798     update_presets_list(tbl);
3801 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
3803     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3804     update_presets_list(tbl);
3807 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
3809     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle"));
3810     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3811     update_presets_list(tbl);
3812     if (calligraphy_angle )
3813         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3817 #define PROFILE_FLOAT_SIZE 7
3818 #define PROFILE_INT_SIZE 4
3819 struct ProfileFloatElement {
3820     char const *name;
3821     double def;
3822     double min;
3823     double max;
3824 };
3825 struct ProfileIntElement {
3826     char const *name;
3827     int def;
3828     int min;
3829     int max;
3830 };
3834 static ProfileFloatElement f_profile[PROFILE_FLOAT_SIZE] = {
3835     {"mass",0.02, 0.0, 1.0},
3836     {"wiggle",0.0, 0.0, 1.0},
3837     {"angle",30.0, -90.0, 90.0},
3838     {"thinning",0.1, -1.0, 1.0},
3839     {"tremor",0.0, 0.0, 1.0},
3840     {"flatness",0.9, 0.0, 1.0},
3841     {"cap_rounding",0.0, 0.0, 5.0}
3842 };
3843 static ProfileIntElement i_profile[PROFILE_INT_SIZE] = {
3844     {"width",15, 1, 100},
3845     {"usepressure",1,0,1},
3846     {"tracebackground",0,0,1},
3847     {"usetilt",1,0,1},
3848 };
3852 static void sp_dcc_save_profile( GtkWidget */*widget*/, GObject *dataKludge ){
3853     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3854     if (! desktop) return;
3856     Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
3857     if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) return;
3858     Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
3860     unsigned int new_index = pref_path_number_of_children("tools.calligraphic.preset") +1;
3861     gchar *profile_id = g_strdup_printf("dcc%d", new_index);
3862     gchar *pref_path = create_pref("tools.calligraphic.preset",profile_id);    
3863     
3864     for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3865         ProfileFloatElement const &pe = f_profile[i];
3866         double v = prefs_get_double_attribute_limited("tools.calligraphic",pe.name, pe.def, pe.min, pe.max);
3867         prefs_set_double_attribute(pref_path,pe.name,v);
3868     }    
3869     for (unsigned i = 0; i < PROFILE_INT_SIZE; ++i) {
3870         ProfileIntElement const &pe = i_profile[i];
3871         int v = prefs_get_int_attribute_limited("tools.calligraphic",pe.name, pe.def,pe.min, pe.max);
3872         prefs_set_int_attribute(pref_path,pe.name,v);
3873     }
3874     prefs_set_string_attribute(pref_path,"name",profile_name.c_str());
3876     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3877     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
3878     GtkTreeIter iter;
3879     gtk_list_store_append( model, &iter );
3880     gtk_list_store_set( model, &iter, 0, profile_name.c_str(), 1, new_index, -1 );
3882     free(profile_id);
3883     free(pref_path);
3885     ege_select_one_action_set_active(selector, new_index);
3889 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject *dataKludge) {
3890     
3891     gint preset_index = ege_select_one_action_get_active( act ); 
3892     gchar *profile_name = get_pref_nth_child("tools.calligraphic.preset", preset_index);
3893        
3894     if ( profile_name) {        
3895         g_object_set_data(dataKludge, "profile_selector",NULL); //temporary hides the selector so no one will updadte it
3896         for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3897             ProfileFloatElement const &pe = f_profile[i];
3898             double v = prefs_get_double_attribute_limited(profile_name, pe.name, pe.def, pe.min, pe.max);
3899             GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, pe.name));
3900             if ( adj ) {
3901                 gtk_adjustment_set_value(adj, v);
3902             }
3903         } 
3904         for (unsigned i = 0; i < PROFILE_INT_SIZE; ++i) {
3905             ProfileIntElement const &pe = i_profile[i];
3906             int v = prefs_get_int_attribute_limited(profile_name, pe.name, pe.def, pe.min, pe.max);
3907             GtkToggleAction* toggle = static_cast<GtkToggleAction *>(g_object_get_data(dataKludge, pe.name));
3908             if ( toggle ) {
3909                 gtk_toggle_action_set_active(toggle, v);
3910             } else printf("No toggle");
3911         }
3912         free(profile_name);
3913         g_object_set_data(dataKludge, "profile_selector",act); //restore selector visibility
3914     }
3919 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3921     {
3922         EgeAdjustmentAction* calligraphy_angle = 0;
3924         {
3925         /* Width */
3926         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3927         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3928         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3929                                                               _("Pen Width"), _("Width:"),
3930                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
3931                                                               "tools.calligraphic", "width", 15,
3932                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3933                                                               1, 100, 1.0, 10.0,
3934                                                               labels, values, G_N_ELEMENTS(labels),
3935                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
3936         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3937         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3938         }
3940         {
3941         /* Thinning */
3942             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3943             gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3944         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3945                                                               _("Stroke Thinning"), _("Thinning:"),
3946                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3947                                                               "tools.calligraphic", "thinning", 0.1,
3948                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3949                                                               -1.0, 1.0, 0.01, 0.1,
3950                                                               labels, values, G_N_ELEMENTS(labels),
3951                                                               sp_ddc_velthin_value_changed, 0.01, 2);
3952         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3953         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3954         }
3956         {
3957         /* Angle */
3958         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3959         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3960         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3961                                                               _("Pen Angle"), _("Angle:"),
3962                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3963                                                               "tools.calligraphic", "angle", 30,
3964                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3965                                                               -90.0, 90.0, 1.0, 10.0,
3966                                                               labels, values, G_N_ELEMENTS(labels),
3967                                                               sp_ddc_angle_value_changed, 1, 0 );
3968         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3969         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3970         g_object_set_data( holder, "angle", eact );
3971         calligraphy_angle = eact;
3972         }
3974         {
3975         /* Fixation */
3976             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3977         gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3978         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3979                                                               _("Fixation"), _("Fixation:"),
3980                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3981                                                               "tools.calligraphic", "flatness", 0.9,
3982                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3983                                                               0.0, 1.0, 0.01, 0.1,
3984                                                               labels, values, G_N_ELEMENTS(labels),
3985                                                               sp_ddc_flatness_value_changed, 0.01, 2 );
3986         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3987         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3988         }
3990         {
3991         /* Cap Rounding */
3992             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3993         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3994         // TRANSLATORS: "cap" means "end" (both start and finish) here
3995         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
3996                                                               _("Cap rounding"), _("Caps:"),
3997                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
3998                                                               "tools.calligraphic", "cap_rounding", 0.0,
3999                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4000                                                               0.0, 5.0, 0.01, 0.1,
4001                                                               labels, values, G_N_ELEMENTS(labels),
4002                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4003         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4004         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4005         }
4007         {
4008         /* Tremor */
4009             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4010         gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
4011         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4012                                                               _("Stroke Tremor"), _("Tremor:"),
4013                                                               _("Increase to make strokes rugged and trembling"),
4014                                                               "tools.calligraphic", "tremor", 0.0,
4015                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4016                                                               0.0, 1.0, 0.01, 0.1,
4017                                                               labels, values, G_N_ELEMENTS(labels),
4018                                                               sp_ddc_tremor_value_changed, 0.01, 2 );
4020         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4021         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4022         }
4024         {
4025         /* Wiggle */
4026         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4027         gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
4028         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4029                                                               _("Pen Wiggle"), _("Wiggle:"),
4030                                                               _("Increase to make the pen waver and wiggle"),
4031                                                               "tools.calligraphic", "wiggle", 0.0,
4032                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4033                                                               0.0, 1.0, 0.01, 0.1,
4034                                                               labels, values, G_N_ELEMENTS(labels),
4035                                                               sp_ddc_wiggle_value_changed, 0.01, 2 );
4036         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4037         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4038         }
4040         {
4041         /* Mass */
4042             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4043         gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
4044         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4045                                                               _("Pen Mass"), _("Mass:"),
4046                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
4047                                                               "tools.calligraphic", "mass", 0.02,
4048                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4049                                                               0.0, 1.0, 0.01, 0.1,
4050                                                               labels, values, G_N_ELEMENTS(labels),
4051                                                               sp_ddc_mass_value_changed, 0.01, 2 );
4052         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4053         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4054         }
4057         /* Trace Background button */
4058         {
4059             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4060                                                           _("Trace Background"),
4061                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4062                                                           "trace_background",
4063                                                           Inkscape::ICON_SIZE_DECORATION );
4064             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4065             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4066             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
4067             g_object_set_data( holder, "tracebackground", act );
4068         }
4070         /* Use Pressure button */
4071         {
4072             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4073                                                           _("Pressure"),
4074                                                           _("Use the pressure of the input device to alter the width of the pen"),
4075                                                           "use_pressure",
4076                                                           Inkscape::ICON_SIZE_DECORATION );
4077             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4078             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4079             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
4080             g_object_set_data( holder, "usepressure", act );
4081         }
4083         /* Use Tilt button */
4084         {
4085             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4086                                                           _("Tilt"),
4087                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
4088                                                           "use_tilt",
4089                                                           Inkscape::ICON_SIZE_DECORATION );
4090             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4091             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4092             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4093             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4094             g_object_set_data( holder, "usetilt", act );
4095         }
4097         /*calligraphic profile */
4098         {
4099             GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );            
4100             gchar *pref_path;
4103             GtkTreeIter iter;
4104             gtk_list_store_append( model, &iter );
4105             gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4107             //TODO: switch back to prefs API
4108             Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
4109             Inkscape::XML::Node *child_repr = sp_repr_children(repr);
4110             int ii=1;
4111             while (child_repr) {
4112                 GtkTreeIter iter;
4113                 char *preset_name = (char *) child_repr->attribute("name");
4114                 gtk_list_store_append( model, &iter );                
4115                 gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );                
4116                 child_repr = sp_repr_next(child_repr); 
4117             }
4119             pref_path = NULL;
4120             EgeSelectOneAction* act1 = ege_select_one_action_new( "SetProfileAction", "" , (_("Change calligraphic profile")), NULL, GTK_TREE_MODEL(model) );
4121             ege_select_one_action_set_appearance( act1, "compact" );
4122             g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder );
4123             gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
4124             g_object_set_data( holder, "profile_selector", act1 );
4125         
4126         }
4128         /*Save or delete calligraphic profile */
4129         {
4130             GtkAction* act = gtk_action_new( "SaveDeleteProfileAction",
4131                                              _("Defaults"),
4132                                              _("Save current settings as new profile"),
4133                                              GTK_STOCK_SAVE );
4134             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_dcc_save_profile), holder );
4137             gtk_action_group_add_action( mainActions, act );
4138             gtk_action_set_sensitive( act, TRUE );
4139             g_object_set_data( holder, "profile_save_delete", act );
4140         }
4141     }
4145 //########################
4146 //##    Circle / Arc    ##
4147 //########################
4149 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4151     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4152     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4154     if (v1 == 0 && v2 == 0) {
4155         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4156             gtk_action_set_sensitive( ocb, FALSE );
4157             gtk_action_set_sensitive( make_whole, FALSE );
4158         }
4159     } else {
4160         gtk_action_set_sensitive( ocb, TRUE );
4161         gtk_action_set_sensitive( make_whole, TRUE );
4162     }
4165 static void
4166 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4168     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4170     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4171         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
4172     }
4174     // quit if run by the attr_changed listener
4175     if (g_object_get_data( tbl, "freeze" )) {
4176         return;
4177     }
4179     // in turn, prevent listener from responding
4180     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4182     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4184     bool modmade = false;
4185     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4186          items != NULL;
4187          items = items->next)
4188     {
4189         SPItem *item = SP_ITEM(items->data);
4191         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4193             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4194             SPArc *arc = SP_ARC(item);
4196             if (!strcmp(value_name, "start"))
4197                 ge->start = (adj->value * M_PI)/ 180;
4198             else
4199                 ge->end = (adj->value * M_PI)/ 180;
4201             sp_genericellipse_normalize(ge);
4202             ((SPObject *)arc)->updateRepr();
4203             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4205             modmade = true;
4206         }
4207     }
4209     g_free(namespaced_name);
4211     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4213     sp_arctb_sensitivize( tbl, adj->value, other->value );
4215     if (modmade) {
4216         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4217                                    _("Arc: Change start/end"));
4218     }
4220     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4224 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
4226     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
4229 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4231     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
4235 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4237     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4238     gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
4239     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4240         prefs_set_int_attribute( "tools.eraser", "mode", eraserMode );
4241     }
4243     // only take action if run by the attr_changed listener
4244     if (!g_object_get_data( tbl, "freeze" )) {
4245         // in turn, prevent listener from responding
4246         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4248         if ( eraserMode != 0 ) {
4249         } else {
4250         }
4251         // TODO finish implementation
4253         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4254     }
4257 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4259     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4260     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4261         if ( ege_select_one_action_get_active( act ) != 0 ) {
4262             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
4263         } else {
4264             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
4265         }
4266     }
4268     // quit if run by the attr_changed listener
4269     if (g_object_get_data( tbl, "freeze" )) {
4270         return;
4271     }
4273     // in turn, prevent listener from responding
4274     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4276     bool modmade = false;
4278     if ( ege_select_one_action_get_active(act) != 0 ) {
4279         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4280              items != NULL;
4281              items = items->next)
4282         {
4283             if (SP_IS_ARC((SPItem *) items->data)) {
4284                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4285                 repr->setAttribute("sodipodi:open", "true");
4286                 SP_OBJECT((SPItem *) items->data)->updateRepr();
4287                 modmade = true;
4288             }
4289         }
4290     } else {
4291         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4292              items != NULL;
4293              items = items->next)
4294         {
4295             if (SP_IS_ARC((SPItem *) items->data))    {
4296                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4297                 repr->setAttribute("sodipodi:open", NULL);
4298                 SP_OBJECT((SPItem *) items->data)->updateRepr();
4299                 modmade = true;
4300             }
4301         }
4302     }
4304     if (modmade) {
4305         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4306                                    _("Arc: Change open/closed"));
4307     }
4309     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4312 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4314     GtkAdjustment *adj;
4315     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
4316     gtk_adjustment_set_value(adj, 0.0);
4317     gtk_adjustment_value_changed(adj);
4319     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
4320     gtk_adjustment_set_value(adj, 0.0);
4321     gtk_adjustment_value_changed(adj);
4323     spinbutton_defocus( GTK_OBJECT(obj) );
4326 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
4327                                       gchar const */*old_value*/, gchar const */*new_value*/,
4328                                       bool /*is_interactive*/, gpointer data)
4330     GObject *tbl = G_OBJECT(data);
4332     // quit if run by the _changed callbacks
4333     if (g_object_get_data( tbl, "freeze" )) {
4334         return;
4335     }
4337     // in turn, prevent callbacks from responding
4338     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4340     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4341     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4343     GtkAdjustment *adj1,*adj2;
4344     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4345     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4346     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4347     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4349     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4351     char const *openstr = NULL;
4352     openstr = repr->attribute("sodipodi:open");
4353     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4355     if (openstr) {
4356         ege_select_one_action_set_active( ocb, 1 );
4357     } else {
4358         ege_select_one_action_set_active( ocb, 0 );
4359     }
4361     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4364 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4365     NULL, /* child_added */
4366     NULL, /* child_removed */
4367     arc_tb_event_attr_changed,
4368     NULL, /* content_changed */
4369     NULL  /* order_changed */
4370 };
4373 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4375     int n_selected = 0;
4376     Inkscape::XML::Node *repr = NULL;
4378     purge_repr_listener( tbl, tbl );
4380     for (GSList const *items = selection->itemList();
4381          items != NULL;
4382          items = items->next)
4383     {
4384         if (SP_IS_ARC((SPItem *) items->data)) {
4385             n_selected++;
4386             repr = SP_OBJECT_REPR((SPItem *) items->data);
4387         }
4388     }
4390     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4392     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4393     if (n_selected == 0) {
4394         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4395     } else if (n_selected == 1) {
4396         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4397         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4399         if (repr) {
4400             g_object_set_data( tbl, "repr", repr );
4401             Inkscape::GC::anchor(repr);
4402             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4403             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4404         }
4405     } else {
4406         // FIXME: implement averaging of all parameters for multiple selected
4407         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4408         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4409         sp_arctb_sensitivize( tbl, 1, 0 );
4410     }
4414 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4416     EgeAdjustmentAction* eact = 0;
4417     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
4420     {
4421         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4422         ege_output_action_set_use_markup( act, TRUE );
4423         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4424         g_object_set_data( holder, "mode_action", act );
4425     }
4427     /* Start */
4428     {
4429         eact = create_adjustment_action( "ArcStartAction",
4430                                          _("Start"), _("Start:"),
4431                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
4432                                          "tools.shapes.arc", "start", 0.0,
4433                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4434                                          -360.0, 360.0, 1.0, 10.0,
4435                                          0, 0, 0,
4436                                          sp_arctb_start_value_changed);
4437         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4438     }
4440     /* End */
4441     {
4442         eact = create_adjustment_action( "ArcEndAction",
4443                                          _("End"), _("End:"),
4444                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
4445                                          "tools.shapes.arc", "end", 0.0,
4446                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4447                                          -360.0, 360.0, 1.0, 10.0,
4448                                          0, 0, 0,
4449                                          sp_arctb_end_value_changed);
4450         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4451     }
4453     /* Segments / Pie checkbox */
4454     {
4455         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4457         GtkTreeIter iter;
4458         gtk_list_store_append( model, &iter );
4459         gtk_list_store_set( model, &iter,
4460                             0, _("Closed arc"),
4461                             1, _("Switch to segment (closed shape with two radii)"),
4462                             2, "circle_closed_arc",
4463                             -1 );
4465         gtk_list_store_append( model, &iter );
4466         gtk_list_store_set( model, &iter,
4467                             0, _("Open Arc"),
4468                             1, _("Switch to arc (unclosed shape)"),
4469                             2, "circle_open_arc",
4470                             -1 );
4472         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4473         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4474         g_object_set_data( holder, "open_action", act );
4476         ege_select_one_action_set_appearance( act, "full" );
4477         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4478         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4479         ege_select_one_action_set_icon_column( act, 2 );
4480         ege_select_one_action_set_icon_size( act, secondarySize );
4481         ege_select_one_action_set_tooltip_column( act, 1  );
4483         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4484         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4485         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4486         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4487     }
4489     /* Make Whole */
4490     {
4491         InkAction* inky = ink_action_new( "ArcResetAction",
4492                                           _("Make whole"),
4493                                           _("Make the shape a whole ellipse, not arc or segment"),
4494                                           "reset_circle",
4495                                           secondarySize );
4496         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4497         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4498         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4499         g_object_set_data( holder, "make_whole", inky );
4500     }
4502     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4503     // sensitivize make whole and open checkbox
4504     {
4505         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4506         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4507         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4508     }
4511     sigc::connection *connection = new sigc::connection(
4512         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4513         );
4514     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4515     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4521 // toggle button callbacks and updaters
4523 //########################
4524 //##      Dropper       ##
4525 //########################
4527 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4528     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4529     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4530     if ( set_action ) {
4531         if ( gtk_toggle_action_get_active( act ) ) {
4532             gtk_action_set_sensitive( set_action, TRUE );
4533         } else {
4534             gtk_action_set_sensitive( set_action, FALSE );
4535         }
4536     }
4538     spinbutton_defocus(GTK_OBJECT(tbl));
4541 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4542     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4543     spinbutton_defocus(GTK_OBJECT(tbl));
4547 /**
4548  * Dropper auxiliary toolbar construction and setup.
4549  *
4550  * TODO: Would like to add swatch of current color.
4551  * TODO: Add queue of last 5 or so colors selected with new swatches so that
4552  *       can drag and drop places. Will provide a nice mixing palette.
4553  */
4554 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4556     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4558     {
4559         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4560         ege_output_action_set_use_markup( act, TRUE );
4561         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4562     }
4564     {
4565         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4566                                                       _("Pick opacity"),
4567                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4568                                                       NULL,
4569                                                       Inkscape::ICON_SIZE_DECORATION );
4570         g_object_set( act, "short_label", _("Pick"), NULL );
4571         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4572         g_object_set_data( holder, "pick_action", act );
4573         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4574         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4575     }
4577     {
4578         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4579                                                       _("Assign opacity"),
4580                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4581                                                       NULL,
4582                                                       Inkscape::ICON_SIZE_DECORATION );
4583         g_object_set( act, "short_label", _("Assign"), NULL );
4584         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4585         g_object_set_data( holder, "set_action", act );
4586         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4587         // make sure it's disabled if we're not picking alpha
4588         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4589         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4590     }
4595 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4597     {
4598         /* Width */
4599         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4600         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4601         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
4602                                                               _("Pen Width"), _("Width:"),
4603                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
4604                                                               "tools.eraser", "width", 15,
4605                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
4606                                                               1, 100, 1.0, 10.0,
4607                                                               labels, values, G_N_ELEMENTS(labels),
4608                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
4609         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4610         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4611     }
4613     {
4614         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4616         GtkTreeIter iter;
4617         gtk_list_store_append( model, &iter );
4618         gtk_list_store_set( model, &iter,
4619                             0, _("Delete"),
4620                             1, _("Delete objects touched by the eraser"),
4621                             2, "delete_object",
4622                             -1 );
4624         gtk_list_store_append( model, &iter );
4625         gtk_list_store_set( model, &iter,
4626                             0, _("Cut"),
4627                             1, _("Cut out from objects"),
4628                             2, "difference",
4629                             -1 );
4631         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4632         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4633         g_object_set_data( holder, "eraser_mode_action", act );
4635         ege_select_one_action_set_appearance( act, "full" );
4636         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4637         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4638         ege_select_one_action_set_icon_column( act, 2 );
4639         ege_select_one_action_set_tooltip_column( act, 1  );
4641         gint eraserMode = (prefs_get_int_attribute("tools.eraser", "mode", 0) != 0) ? 1 : 0;
4642         ege_select_one_action_set_active( act, eraserMode );
4643         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
4644     }
4645     
4648 //########################
4649 //##    Text Toolbox    ##
4650 //########################
4651 /*
4652 static void
4653 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
4655     //Call back for letter sizing spinbutton
4658 static void
4659 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
4661     //Call back for line height spinbutton
4664 static void
4665 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4667     //Call back for horizontal kerning spinbutton
4670 static void
4671 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4673     //Call back for vertical kerning spinbutton
4676 static void
4677 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
4679     //Call back for letter rotation spinbutton
4680 }*/
4682 namespace {
4684 bool popdown_visible = false;
4685 bool popdown_hasfocus = false;
4687 void
4688 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
4690     SPStyle *query =
4691         sp_style_new (SP_ACTIVE_DOCUMENT);
4693 //    int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4695     int result_family =
4696         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4698     int result_style =
4699         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4701     int result_numbers =
4702         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4704     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4706     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4707     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
4708     {
4709         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
4711         if (repr)
4712         {
4713             sp_style_read_from_repr (query, repr);
4714         }
4715         else
4716         {
4717             return;
4718         }
4719     }
4721     if (query->text)
4722     {
4723         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4724             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4725             gtk_entry_set_text (GTK_ENTRY (entry), "");
4727         } else if (query->text->font_specification.value || query->text->font_family.value) {
4729             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4731             // Get the font that corresponds
4732             Glib::ustring familyName;
4734             font_instance * font = font_factory::Default()->FaceFromStyle(query);
4735             if (font) {
4736                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4737                 font->Unref();
4738                 font = NULL;
4739             }
4741             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4743             Gtk::TreePath path;
4744             try {
4745                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4746             } catch (...) {
4747                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4748                 return;
4749             }
4751             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4752             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4754             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4756             gtk_tree_selection_select_path (tselection, path.gobj());
4757             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4759             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4760         }
4762         //Size
4763         GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4764         char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4765         g_object_set_data (tbl, "size-block", gpointer(1));
4766         gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4767         g_object_set_data (tbl, "size-block", gpointer(0));
4768         free (str);
4770         //Anchor
4771         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4772         {
4773             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4774             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4775             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4776             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4777         }
4778         else
4779         {
4780             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4781             {
4782                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4783                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4784                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4785                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4786             }
4787             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4788             {
4789                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4790                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4791                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4792                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4793             }
4794             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4795             {
4796                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4797                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4798                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4799                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4800             }
4801         }
4803         //Style
4804         {
4805             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4807             gboolean active = gtk_toggle_button_get_active (button);
4808             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4810             if (active != check)
4811             {
4812                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4813                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4814                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4815             }
4816         }
4818         {
4819             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4821             gboolean active = gtk_toggle_button_get_active (button);
4822             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4824             if (active != check)
4825             {
4826                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4827                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4828                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4829             }
4830         }
4832         //Orientation
4833         //locking both buttons, changing one affect all group (both)
4834         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4835         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4837         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4838         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4840         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4841         {
4842             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4843         }
4844         else
4845         {
4846             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4847         }
4848         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4849         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4850     }
4852     sp_style_unref(query);
4855 void
4856 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4858     sp_text_toolbox_selection_changed (selection, tbl);
4861 void
4862 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4864     sp_text_toolbox_selection_changed (NULL, tbl);
4867 void
4868 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
4869                                 GObject             *tbl)
4871     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
4872     GtkTreeModel *model = 0;
4873     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4874     GtkTreeIter   iter;
4875     char         *family = 0;
4877     gdk_pointer_ungrab (GDK_CURRENT_TIME);
4878     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4880     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4881         return;
4882     }
4884     gtk_tree_model_get (model, &iter, 0, &family, -1);
4886     if (g_object_get_data (G_OBJECT (selection), "block"))
4887     {
4888         gtk_entry_set_text (GTK_ENTRY (entry), family);
4889         return;
4890     }
4892     gtk_entry_set_text (GTK_ENTRY (entry), family);
4894     SPStyle *query =
4895         sp_style_new (SP_ACTIVE_DOCUMENT);
4897     int result_fontspec =
4898         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4900     //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4902     SPCSSAttr *css = sp_repr_css_attr_new ();
4905     // First try to get the font spec from the stored value
4906     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
4908     if (fontSpec.empty()) {
4909         // Construct a new font specification if it does not yet exist
4910         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4911         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4912         fontFromStyle->Unref();
4913     }
4915     if (!fontSpec.empty()) {
4916         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
4917         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
4918             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
4919             if (font) {
4920                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4922                 // Set all the these just in case they were altered when finding the best
4923                 // match for the new family and old style...
4925                 gchar c[256];
4927                 font->Family(c, 256);
4928                 sp_repr_css_set_property (css, "font-family", c);
4930                 font->Attribute( "weight", c, 256);
4931                 sp_repr_css_set_property (css, "font-weight", c);
4933                 font->Attribute("style", c, 256);
4934                 sp_repr_css_set_property (css, "font-style", c);
4936                 font->Attribute("stretch", c, 256);
4937                 sp_repr_css_set_property (css, "font-stretch", c);
4939                 font->Attribute("variant", c, 256);
4940                 sp_repr_css_set_property (css, "font-variant", c);
4942                 font->Unref();
4943             }
4944         }
4945     }
4947     // If querying returned nothing, set the default style of the tool (for new texts)
4948     if (result_fontspec == QUERY_STYLE_NOTHING)
4949     {
4950         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4951         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
4952     }
4953     else
4954     {
4955         sp_desktop_set_style (desktop, css, true, true);
4956     }
4958     sp_style_unref(query);
4960     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4961                                    _("Text: Change font family"));
4962     sp_repr_css_attr_unref (css);
4963     free (family);
4964     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4966     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4969 void
4970 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
4971                                        GObject      *tbl)
4973     const char *family = gtk_entry_get_text (entry);
4975     try {
4976         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
4977         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4978         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4979         gtk_tree_selection_select_path (selection, path.gobj());
4980         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4981         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4982     } catch (...) {
4983         if (family && strlen (family))
4984         {
4985             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4986         }
4987     }
4990 void
4991 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
4992                                    gpointer          data)
4994     if (g_object_get_data (G_OBJECT (button), "block")) return;
4995     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
4996     int prop = GPOINTER_TO_INT(data);
4998     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4999     SPCSSAttr *css = sp_repr_css_attr_new ();
5001     switch (prop)
5002     {
5003         case 0:
5004         {
5005             sp_repr_css_set_property (css, "text-anchor", "start");
5006             sp_repr_css_set_property (css, "text-align", "start");
5007             break;
5008         }
5009         case 1:
5010         {
5011             sp_repr_css_set_property (css, "text-anchor", "middle");
5012             sp_repr_css_set_property (css, "text-align", "center");
5013             break;
5014         }
5016         case 2:
5017         {
5018             sp_repr_css_set_property (css, "text-anchor", "end");
5019             sp_repr_css_set_property (css, "text-align", "end");
5020             break;
5021         }
5023         case 3:
5024         {
5025             sp_repr_css_set_property (css, "text-anchor", "start");
5026             sp_repr_css_set_property (css, "text-align", "justify");
5027             break;
5028         }
5029     }
5031     SPStyle *query =
5032         sp_style_new (SP_ACTIVE_DOCUMENT);
5033     int result_numbers =
5034         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5036     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5037     if (result_numbers == QUERY_STYLE_NOTHING)
5038     {
5039         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5040     }
5042     sp_style_unref(query);
5044     sp_desktop_set_style (desktop, css, true, true);
5045     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5046                                    _("Text: Change alignment"));
5047     sp_repr_css_attr_unref (css);
5049     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5052 void
5053 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
5054                                gpointer          data)
5056     if (g_object_get_data (G_OBJECT (button), "block")) return;
5058     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5059     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5060     int          prop       = GPOINTER_TO_INT(data);
5061     bool         active     = gtk_toggle_button_get_active (button);
5063     SPStyle *query =
5064         sp_style_new (SP_ACTIVE_DOCUMENT);
5066     int result_fontspec =
5067         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5069     //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5070     //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5071     //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5073     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
5074     Glib::ustring newFontSpec = "";
5076     if (fontSpec.empty()) {
5077         // Construct a new font specification if it does not yet exist
5078         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5079         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5080         fontFromStyle->Unref();
5081     }
5083     switch (prop)
5084     {
5085         case 0:
5086         {
5087             if (!fontSpec.empty()) {
5088                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
5089             }
5090             if (fontSpec != newFontSpec) {
5091                 // Don't even set the bold if the font didn't exist on the system
5092                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
5093             }
5094             break;
5095         }
5097         case 1:
5098         {
5099             if (!fontSpec.empty()) {
5100                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
5101             }
5102             if (fontSpec != newFontSpec) {
5103                 // Don't even set the italic if the font didn't exist on the system
5104                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
5105             }
5106             break;
5107         }
5108     }
5110     if (!newFontSpec.empty()) {
5111         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5112     }
5114     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5115     if (result_fontspec == QUERY_STYLE_NOTHING)
5116     {
5117         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5118     }
5120     sp_style_unref(query);
5122     sp_desktop_set_style (desktop, css, true, true);
5123     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5124                                    _("Text: Change font style"));
5125     sp_repr_css_attr_unref (css);
5127     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5130 void
5131 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
5132                                      gpointer         data)
5134     if (g_object_get_data (G_OBJECT (button), "block")) {
5135         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5136         return;
5137     }
5139     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5140     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5141     int          prop       = GPOINTER_TO_INT(data);
5143     switch (prop)
5144     {
5145         case 0:
5146         {
5147             sp_repr_css_set_property (css, "writing-mode", "lr");
5148             break;
5149         }
5151         case 1:
5152         {
5153             sp_repr_css_set_property (css, "writing-mode", "tb");
5154             break;
5155         }
5156     }
5158     SPStyle *query =
5159         sp_style_new (SP_ACTIVE_DOCUMENT);
5160     int result_numbers =
5161         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5163     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5164     if (result_numbers == QUERY_STYLE_NOTHING)
5165     {
5166         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5167     }
5169     sp_desktop_set_style (desktop, css, true, true);
5170     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5171                                    _("Text: Change orientation"));
5172     sp_repr_css_attr_unref (css);
5174     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5177 gboolean
5178 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5180     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5181     if (!desktop) return FALSE;
5183     switch (get_group0_keyval (event)) {
5184         case GDK_Escape: // defocus
5185             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5186             sp_text_toolbox_selection_changed (NULL, tbl); // update
5187             return TRUE; // I consumed the event
5188             break;
5189     }
5190     return FALSE;
5193 gboolean
5194 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5196     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5197     if (!desktop) return FALSE;
5199     switch (get_group0_keyval (event)) {
5200         case GDK_KP_Enter:
5201         case GDK_Return:
5202         case GDK_Escape: // defocus
5203             gtk_widget_hide (w);
5204             popdown_visible = false;
5205             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5206             return TRUE; // I consumed the event
5207             break;
5208         case GDK_w:
5209         case GDK_W:
5210             if (event->state & GDK_CONTROL_MASK) {
5211                 gtk_widget_hide (w);
5212                 popdown_visible = false;
5213                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5214                 return TRUE; // I consumed the event
5215             }
5216             break;
5217     }
5218     return FALSE;
5222 void
5223 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
5224                                GObject     *tbl)
5226     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5228     if (g_object_get_data (tbl, "size-block")) return;
5230     // If this is not from selecting a size in the list (in which case get_active will give the
5231     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5232     // process this event. This fixes GTK's stupid insistence on sending an activate change every
5233     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5234     if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5235         return;
5237     gchar *endptr;
5238     gdouble value = -1;
5239     char *text = gtk_combo_box_get_active_text (cbox);
5240     if (text) {
5241         value = g_strtod (text, &endptr);
5242         if (endptr == text) // conversion failed, non-numeric input
5243             value = -1;
5244         free (text);
5245     }
5246     if (value <= 0) {
5247         return; // could not parse value
5248     }
5250     SPCSSAttr *css = sp_repr_css_attr_new ();
5251     Inkscape::CSSOStringStream osfs;
5252     osfs << value;
5253     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5255     SPStyle *query =
5256         sp_style_new (SP_ACTIVE_DOCUMENT);
5257     int result_numbers =
5258         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5260     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5261     if (result_numbers == QUERY_STYLE_NOTHING)
5262     {
5263         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5264     }
5266     sp_style_unref(query);
5268     sp_desktop_set_style (desktop, css, true, true);
5269     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5270                                    _("Text: Change font size"));
5271     sp_repr_css_attr_unref (css);
5273     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5276 gboolean
5277 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5279     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5280     if (!desktop) return FALSE;
5282     if (!g_object_get_data (tbl, "esc-pressed")) {
5283         g_object_set_data (tbl, "enter-pressed", gpointer(1));
5284         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5285         sp_text_toolbox_size_changed (cbox, tbl);
5286         g_object_set_data (tbl, "enter-pressed", gpointer(0));
5287     }
5288     return FALSE; // I consumed the event
5292 gboolean
5293 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5295     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5296     if (!desktop) return FALSE;
5298     switch (get_group0_keyval (event)) {
5299         case GDK_Escape: // defocus
5300             g_object_set_data (tbl, "esc-pressed", gpointer(1));
5301             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5302             g_object_set_data (tbl, "esc-pressed", gpointer(0));
5303             return TRUE; // I consumed the event
5304             break;
5305         case GDK_Return: // defocus
5306         case GDK_KP_Enter:
5307             g_object_set_data (tbl, "enter-pressed", gpointer(1));
5308             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5309             sp_text_toolbox_size_changed (cbox, tbl);
5310             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5311             g_object_set_data (tbl, "enter-pressed", gpointer(0));
5312             return TRUE; // I consumed the event
5313             break;
5314     }
5315     return FALSE;
5318 void
5319 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
5320                                          GObject            *tbl)
5322     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
5323     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5324     int x, y;
5326     if (!popdown_visible)
5327     {
5328         gdk_window_get_origin (widget->window, &x, &y);
5329         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
5330         gtk_widget_show_all (popdown);
5331         //sp_transientize (popdown);
5333         gdk_pointer_grab (widget->window, TRUE,
5334                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
5335                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
5336                                         GDK_POINTER_MOTION_MASK),
5337                           NULL, NULL, GDK_CURRENT_TIME);
5339         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
5341         popdown_visible = true;
5342     }
5343     else
5344     {
5345         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5346         gdk_pointer_ungrab (GDK_CURRENT_TIME);
5347         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5348         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5349         gtk_widget_hide (popdown);
5350         popdown_visible = false;
5351     }
5354 gboolean
5355 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
5356                                  GdkEventFocus    */*event*/,
5357                                  GObject          */*tbl*/)
5359     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
5360     return FALSE;
5363 gboolean
5364 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
5365                                    GdkEventFocus    */*event*/,
5366                                    GObject          */*tbl*/)
5368     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5370     if (popdown_hasfocus) {
5371         gtk_widget_hide (popdown);
5372         popdown_hasfocus = false;
5373         popdown_visible = false;
5374         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5375         return TRUE;
5376     }
5377     return FALSE;
5380 gboolean
5381 sp_text_toolbox_popdown_focus_in (GtkWidget        */*popdown*/,
5382                                    GdkEventFocus    */*event*/,
5383                                    GObject          */*tbl*/)
5385     popdown_hasfocus = true;
5386     return TRUE;
5390 void
5391 cell_data_func  (GtkTreeViewColumn */*column*/,
5392                  GtkCellRenderer   *cell,
5393                  GtkTreeModel      *tree_model,
5394                  GtkTreeIter       *iter,
5395                  gpointer           /*data*/)
5397     char        *family,
5398         *family_escaped,
5399         *sample_escaped;
5401     static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
5403     gtk_tree_model_get (tree_model, iter, 0, &family, -1);
5405     family_escaped = g_markup_escape_text (family, -1);
5406     sample_escaped = g_markup_escape_text (sample, -1);
5408     std::stringstream markup;
5409     markup << family_escaped << "  <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
5410     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
5412     free (family);
5413     free (family_escaped);
5414     free (sample_escaped);
5417 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
5418     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
5419     if (completion) {
5420         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
5421         g_object_unref (completion);
5422     }
5425 GtkWidget*
5426 sp_text_toolbox_new (SPDesktop *desktop)
5428     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
5429     GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
5431     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
5432     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
5434     GtkTooltips *tt = gtk_tooltips_new();
5435     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
5437     ////////////Family
5438     //Window
5439     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5440     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
5442     //Entry
5443     GtkWidget           *entry = gtk_entry_new ();
5444     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
5445     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
5446     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
5447     gtk_entry_completion_set_text_column (completion, 0);
5448     gtk_entry_completion_set_minimum_key_length (completion, 1);
5449     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
5450     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
5451     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
5452     gtk_toolbar_append_widget( tbl, entry, "", "" );
5453     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
5455     //Button
5456     GtkWidget   *button = gtk_button_new ();
5457     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
5458     gtk_toolbar_append_widget( tbl, button, "", "");
5460     //Popdown
5461     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
5462     GtkWidget           *treeview = gtk_tree_view_new ();
5464     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
5465     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
5466     gtk_tree_view_column_pack_start (column, cell, FALSE);
5467     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
5468     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5469     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5471     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5472     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5473     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5475     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5477     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5478     gtk_container_add (GTK_CONTAINER (sw), treeview);
5480     gtk_container_add (GTK_CONTAINER (window), sw);
5481     gtk_widget_set_size_request (window, 300, 450);
5483     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5484     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5485     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5487     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5489     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5490     g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5491     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5493     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5494     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5496     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5497     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5498     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5499     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5500     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5502     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
5503     GtkWidget *box = gtk_event_box_new ();
5504     gtk_container_add (GTK_CONTAINER (box), image);
5505     gtk_toolbar_append_widget( tbl, box, "", "");
5506     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5507     GtkTooltips *tooltips = gtk_tooltips_new ();
5508     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5509     gtk_widget_hide (GTK_WIDGET (box));
5510     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
5512     ////////////Size
5513     const char *sizes[] = {
5514         "4", "6", "8", "9", "10", "11", "12", "13", "14",
5515         "16", "18", "20", "22", "24", "28",
5516         "32", "36", "40", "48", "56", "64", "72", "144"
5517     };
5519     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
5520     for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
5521     gtk_widget_set_size_request (cbox, 80, -1);
5522     gtk_toolbar_append_widget( tbl, cbox, "", "");
5523     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
5524     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
5525     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
5526     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
5528     ////////////Text anchor
5529     GtkWidget *group   = gtk_radio_button_new (NULL);
5530     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
5531     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
5533     // left
5534     GtkWidget *rbutton = group;
5535     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5536     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
5537     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5539     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5540     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
5541     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
5542     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
5544     // center
5545     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5546     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5547     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
5548     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5550     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5551     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
5552     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5553     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5555     // right
5556     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5557     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5558     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
5559     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5561     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5562     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
5563     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5564     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5566     // fill
5567     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5568     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5569     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
5570     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5572     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5573     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
5574     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
5575     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
5577     gtk_toolbar_append_widget( tbl, row, "", "");
5579     //spacer
5580     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5582     ////////////Text style
5583     row = gtk_hbox_new (FALSE, 4);
5585     // bold
5586     rbutton = gtk_toggle_button_new ();
5587     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5588     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
5589     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5590     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
5592     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5593     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
5594     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
5596     // italic
5597     rbutton = gtk_toggle_button_new ();
5598     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5599     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
5600     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5601     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
5603     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5604     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
5605     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
5607     gtk_toolbar_append_widget( tbl, row, "", "");
5609     //spacer
5610     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5612     ////////////Text orientation
5613     group   = gtk_radio_button_new (NULL);
5614     row     = gtk_hbox_new (FALSE, 4);
5615     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
5617     // horizontal
5618     rbutton = group;
5619     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5620     gtk_container_add           (GTK_CONTAINER (rbutton),
5621                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
5622     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5623     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
5625     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5626     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
5627     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
5629     // vertical
5630     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5631     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5632     gtk_container_add           (GTK_CONTAINER (rbutton),
5633                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
5634     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5635     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
5637     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5638     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
5639     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
5640     gtk_toolbar_append_widget( tbl, row, "", "" );
5643     //watch selection
5644     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
5646     sigc::connection *c_selection_changed =
5647         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5648                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
5649     pool->add_connection ("selection-changed", c_selection_changed);
5651     sigc::connection *c_selection_modified =
5652         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5653                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
5654     pool->add_connection ("selection-modified", c_selection_modified);
5656     sigc::connection *c_subselection_changed =
5657         new sigc::connection (desktop->connectToolSubselectionChanged
5658                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
5659     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
5661     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
5664     gtk_widget_show_all( GTK_WIDGET(tbl) );
5666     return GTK_WIDGET(tbl);
5667 } // end of sp_text_toolbox_new()
5669 }//<unnamed> namespace
5672 //#########################
5673 //##      Connector      ##
5674 //#########################
5676 static void sp_connector_path_set_avoid(void)
5678     cc_selection_set_avoid(true);
5682 static void sp_connector_path_set_ignore(void)
5684     cc_selection_set_avoid(false);
5689 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
5691     // quit if run by the _changed callbacks
5692     if (g_object_get_data( tbl, "freeze" )) {
5693         return;
5694     }
5696     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5697     SPDocument *doc = sp_desktop_document(desktop);
5699     if (!sp_document_get_undo_sensitive(doc))
5700     {
5701         return;
5702     }
5704     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5706     if ( repr->attribute("inkscape:connector-spacing") ) {
5707         gdouble priorValue = gtk_adjustment_get_value(adj);
5708         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
5709         if ( priorValue == gtk_adjustment_get_value(adj) ) {
5710             return;
5711         }
5712     } else if ( adj->value == defaultConnSpacing ) {
5713         return;
5714     }
5716     // in turn, prevent callbacks from responding
5717     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5719     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
5720     SP_OBJECT(desktop->namedview)->updateRepr();
5722     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
5723     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
5724         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
5725         NR::Matrix m = NR::identity();
5726         avoid_item_move(&m, item);
5727     }
5729     if (items) {
5730         g_slist_free(items);
5731     }
5733     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
5734             _("Change connector spacing"));
5736     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5738     spinbutton_defocus(GTK_OBJECT(tbl));
5741 static void sp_connector_graph_layout(void)
5743     if (!SP_ACTIVE_DESKTOP) return;
5745     // hack for clones, see comment in align-and-distribute.cpp
5746     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5747     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5749     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5751     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5753     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5756 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5758     if ( gtk_toggle_action_get_active( act ) ) {
5759         prefs_set_string_attribute("tools.connector", "directedlayout",
5760                 "true");
5761     } else {
5762         prefs_set_string_attribute("tools.connector", "directedlayout",
5763                 "false");
5764     }
5767 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5769     if ( gtk_toggle_action_get_active( act ) ) {
5770         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5771                 "true");
5772     } else {
5773         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5774                 "false");
5775     }
5779 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5781     prefs_set_double_attribute("tools.connector", "length", adj->value);
5782     spinbutton_defocus(GTK_OBJECT(tbl));
5785 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5786                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5787                                             bool /*is_interactive*/, gpointer data)
5789     GtkWidget *tbl = GTK_WIDGET(data);
5791     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5792         return;
5793     }
5794     if (strcmp(name, "inkscape:connector-spacing") != 0) {
5795         return;
5796     }
5798     GtkAdjustment *adj = (GtkAdjustment*)
5799             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5800     gdouble spacing = defaultConnSpacing;
5801     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5803     gtk_adjustment_set_value(adj, spacing);
5807 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5808     NULL, /* child_added */
5809     NULL, /* child_removed */
5810     connector_tb_event_attr_changed,
5811     NULL, /* content_changed */
5812     NULL  /* order_changed */
5813 };
5816 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5818     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
5820     {
5821         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5822                                           _("Avoid"),
5823                                           _("Make connectors avoid selected objects"),
5824                                           "connector_avoid",
5825                                           secondarySize );
5826         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5827         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5828     }
5830     {
5831         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5832                                           _("Ignore"),
5833                                           _("Make connectors ignore selected objects"),
5834                                           "connector_ignore",
5835                                           secondarySize );
5836         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5837         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5838     }
5840     EgeAdjustmentAction* eact = 0;
5842     // Spacing spinbox
5843     eact = create_adjustment_action( "ConnectorSpacingAction",
5844                                      _("Connector Spacing"), _("Spacing:"),
5845                                      _("The amount of space left around objects by auto-routing connectors"),
5846                                      "tools.connector", "spacing", defaultConnSpacing,
5847                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5848                                      0, 100, 1.0, 10.0,
5849                                      0, 0, 0,
5850                                      connector_spacing_changed, 1, 0 );
5851     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5853     // Graph (connector network) layout
5854     {
5855         InkAction* inky = ink_action_new( "ConnectorGraphAction",
5856                                           _("Graph"),
5857                                           _("Nicely arrange selected connector network"),
5858                                           "graph_layout",
5859                                           secondarySize );
5860         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5861         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5862     }
5864     // Default connector length spinbox
5865     eact = create_adjustment_action( "ConnectorLengthAction",
5866                                      _("Connector Length"), _("Length:"),
5867                                      _("Ideal length for connectors when layout is applied"),
5868                                      "tools.connector", "length", 100,
5869                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5870                                      10, 1000, 10.0, 100.0,
5871                                      0, 0, 0,
5872                                      connector_length_changed, 1, 0 );
5873     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5876     // Directed edges toggle button
5877     {
5878         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5879                                                       _("Downwards"),
5880                                                       _("Make connectors with end-markers (arrows) point downwards"),
5881                                                       "directed_graph",
5882                                                       Inkscape::ICON_SIZE_DECORATION );
5883         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5885         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5886         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5887                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5889         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5890     }
5892     // Avoid overlaps toggle button
5893     {
5894         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5895                                                       _("Remove overlaps"),
5896                                                       _("Do not allow overlapping shapes"),
5897                                                       "remove_overlaps",
5898                                                       Inkscape::ICON_SIZE_DECORATION );
5899         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5901         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5902         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5903                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5905         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5906     }
5908     // Code to watch for changes to the connector-spacing attribute in
5909     // the XML.
5910     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5911     g_assert(repr != NULL);
5913     purge_repr_listener( holder, holder );
5915     if (repr) {
5916         g_object_set_data( holder, "repr", repr );
5917         Inkscape::GC::anchor(repr);
5918         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5919         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5920     }
5921 } // end of sp_connector_toolbox_prep()
5924 //#########################
5925 //##     Paintbucket     ##
5926 //#########################
5928 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5930     gint channels = ege_select_one_action_get_active( act );
5931     flood_channels_set_channels( channels );
5934 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
5936     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
5939 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
5941     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
5944 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
5946     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
5947     SPUnit const *unit = tracker->getActiveUnit();
5949     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
5951     prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
5954 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
5956     // FIXME: make defaults settable via Inkscape Options
5957     struct KeyValue {
5958         char const *key;
5959         double value;
5960     } const key_values[] = {
5961         {"threshold", 15},
5962         {"offset", 0.0}
5963     };
5965     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
5966         KeyValue const &kv = key_values[i];
5967         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
5968         if ( adj ) {
5969             gtk_adjustment_set_value(adj, kv.value);
5970         }
5971     }
5973     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
5974     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
5975     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
5976     ege_select_one_action_set_active( autogap_action, 0 );
5979 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5981     EgeAdjustmentAction* eact = 0;
5983     {
5984         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5986         GList* items = 0;
5987         gint count = 0;
5988         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5989         {
5990             GtkTreeIter iter;
5991             gtk_list_store_append( model, &iter );
5992             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5993             count++;
5994         }
5995         g_list_free( items );
5996         items = 0;
5997         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
5998         g_object_set( act1, "short_label", _("Fill by:"), NULL );
5999         ege_select_one_action_set_appearance( act1, "compact" );
6000         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
6001         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
6002         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
6003         g_object_set_data( holder, "channels_action", act1 );
6004     }
6006     // Spacing spinbox
6007     {
6008         eact = create_adjustment_action(
6009             "ThresholdAction",
6010             _("Fill Threshold"), _("Threshold:"),
6011             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
6012             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
6013             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
6014             0, 0, 0,
6015             paintbucket_threshold_changed, 1, 0 );
6017         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6018     }
6020     // Create the units menu.
6021     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
6022     const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
6023     if (stored_unit)
6024         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
6025     g_object_set_data( holder, "tracker", tracker );
6026     {
6027         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
6028         gtk_action_group_add_action( mainActions, act );
6029     }
6031     // Offset spinbox
6032     {
6033         eact = create_adjustment_action(
6034             "OffsetAction",
6035             _("Grow/shrink by"), _("Grow/shrink by:"),
6036             _("The amount to grow (positive) or shrink (negative) the created fill path"),
6037             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
6038             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
6039             0, 0, 0,
6040             paintbucket_offset_changed, 1, 2);
6041         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
6043         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6044     }
6046     /* Auto Gap */
6047     {
6048         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6050         GList* items = 0;
6051         gint count = 0;
6052         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
6053         {
6054             GtkTreeIter iter;
6055             gtk_list_store_append( model, &iter );
6056             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6057             count++;
6058         }
6059         g_list_free( items );
6060         items = 0;
6061         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
6062         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
6063         ege_select_one_action_set_appearance( act2, "compact" );
6064         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
6065         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
6066         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
6067         g_object_set_data( holder, "autogap_action", act2 );
6068     }
6070     /* Reset */
6071     {
6072         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
6073                                           _("Defaults"),
6074                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
6075                                           GTK_STOCK_CLEAR );
6076         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
6077         gtk_action_group_add_action( mainActions, act );
6078         gtk_action_set_sensitive( act, TRUE );
6079     }
6083 /*
6084   Local Variables:
6085   mode:c++
6086   c-file-style:"stroustrup"
6087   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
6088   indent-tabs-mode:nil
6089   fill-column:99
6090   End:
6091 */
6092 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :