Code

8fa52c2815ab29a01a8f8e8db5101c99027a09a7
[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 };
223 #define TOOLBAR_SLIDER_HINT "full"
225 static gchar const * ui_descr =
226         "<ui>"
227         "  <toolbar name='SelectToolbar'>"
228         "    <toolitem action='EditSelectAll' />"
229         "    <toolitem action='EditSelectAllInAllLayers' />"
230         "    <toolitem action='EditDeselect' />"
231         "    <separator />"
232         "    <toolitem action='ObjectRotate90CCW' />"
233         "    <toolitem action='ObjectRotate90' />"
234         "    <toolitem action='ObjectFlipHorizontally' />"
235         "    <toolitem action='ObjectFlipVertically' />"
236         "    <separator />"
237         "    <toolitem action='SelectionToBack' />"
238         "    <toolitem action='SelectionLower' />"
239         "    <toolitem action='SelectionRaise' />"
240         "    <toolitem action='SelectionToFront' />"
241         "    <separator />"
242         "    <toolitem action='XAction' />"
243         "    <toolitem action='YAction' />"
244         "    <toolitem action='WidthAction' />"
245         "    <toolitem action='LockAction' />"
246         "    <toolitem action='HeightAction' />"
247         "    <toolitem action='UnitsAction' />"
248         "    <separator />"
249         "    <toolitem action='transform_affect_label' />"
250         "    <toolitem action='transform_stroke' />"
251         "    <toolitem action='transform_corners' />"
252         "    <toolitem action='transform_gradient' />"
253         "    <toolitem action='transform_pattern' />"
254         "  </toolbar>"
256         "  <toolbar name='NodeToolbar'>"
257         "    <toolitem action='NodeInsertAction' />"
258         "    <toolitem action='NodeDeleteAction' />"
259         "    <separator />"
260         "    <toolitem action='NodeJoinAction' />"
261         "    <toolitem action='NodeBreakAction' />"
262         "    <separator />"
263         "    <toolitem action='NodeJoinSegmentAction' />"
264         "    <toolitem action='NodeDeleteSegmentAction' />"
265         "    <separator />"
266         "    <toolitem action='NodeCuspAction' />"
267         "    <toolitem action='NodeSmoothAction' />"
268         "    <toolitem action='NodeSymmetricAction' />"
269         "    <separator />"
270         "    <toolitem action='NodeLineAction' />"
271         "    <toolitem action='NodeCurveAction' />"
272         "    <separator />"
273         "    <toolitem action='ObjectToPath' />"
274         "    <toolitem action='StrokeToPath' />"
275         "    <separator />"
276         "    <toolitem action='NodeXAction' />"
277         "    <toolitem action='NodeYAction' />"
278         "    <toolitem action='NodeUnitsAction' />"
279         "    <separator />"
280         "    <toolitem action='ObjectEditClipPathAction' />"
281         "    <toolitem action='ObjectEditMaskPathAction' />"
282         "    <toolitem action='EditNextLPEParameterAction' />"
283         "    <separator />"
284         "    <toolitem action='NodesShowHandlesAction' />"
285         "    <toolitem action='NodesShowHelperpath' />"
286         "  </toolbar>"
288         "  <toolbar name='TweakToolbar'>"
289         "    <toolitem action='TweakWidthAction' />"
290         "    <separator />"
291         "    <toolitem action='TweakForceAction' />"
292         "    <toolitem action='TweakPressureAction' />"
293         "    <separator />"
294         "    <toolitem action='TweakModeAction' />"
295         "    <separator />"
296         "    <toolitem action='TweakFidelityAction' />"
297         "    <separator />"
298         "    <toolitem action='TweakChannelsLabel' />"
299         "    <toolitem action='TweakDoH' />"
300         "    <toolitem action='TweakDoS' />"
301         "    <toolitem action='TweakDoL' />"
302         "    <toolitem action='TweakDoO' />"
303         "  </toolbar>"
305         "  <toolbar name='ZoomToolbar'>"
306         "    <toolitem action='ZoomIn' />"
307         "    <toolitem action='ZoomOut' />"
308         "    <separator />"
309         "    <toolitem action='Zoom1:0' />"
310         "    <toolitem action='Zoom1:2' />"
311         "    <toolitem action='Zoom2:1' />"
312         "    <separator />"
313         "    <toolitem action='ZoomSelection' />"
314         "    <toolitem action='ZoomDrawing' />"
315         "    <toolitem action='ZoomPage' />"
316         "    <toolitem action='ZoomPageWidth' />"
317         "    <separator />"
318         "    <toolitem action='ZoomPrev' />"
319         "    <toolitem action='ZoomNext' />"
320         "  </toolbar>"
322         "  <toolbar name='StarToolbar'>"
323         "    <separator />"
324         "    <toolitem action='StarStateAction' />"
325         "    <separator />"
326         "    <toolitem action='FlatAction' />"
327         "    <separator />"
328         "    <toolitem action='MagnitudeAction' />"
329         "    <toolitem action='SpokeAction' />"
330         "    <toolitem action='RoundednessAction' />"
331         "    <toolitem action='RandomizationAction' />"
332         "    <separator />"
333         "    <toolitem action='StarResetAction' />"
334         "  </toolbar>"
336         "  <toolbar name='RectToolbar'>"
337         "    <toolitem action='RectStateAction' />"
338         "    <toolitem action='RectWidthAction' />"
339         "    <toolitem action='RectHeightAction' />"
340         "    <toolitem action='RadiusXAction' />"
341         "    <toolitem action='RadiusYAction' />"
342         "    <toolitem action='RectUnitsAction' />"
343         "    <separator />"
344         "    <toolitem action='RectResetAction' />"
345         "  </toolbar>"
347         "  <toolbar name='3DBoxToolbar'>"
348         "    <toolitem action='3DBoxAngleXAction' />"
349         "    <toolitem action='3DBoxVPXStateAction' />"
350         "    <separator />"
351         "    <toolitem action='3DBoxAngleYAction' />"
352         "    <toolitem action='3DBoxVPYStateAction' />"
353         "    <separator />"
354         "    <toolitem action='3DBoxAngleZAction' />"
355         "    <toolitem action='3DBoxVPZStateAction' />"
356         "  </toolbar>"
358         "  <toolbar name='SpiralToolbar'>"
359         "    <toolitem action='SpiralStateAction' />"
360         "    <toolitem action='SpiralRevolutionAction' />"
361         "    <toolitem action='SpiralExpansionAction' />"
362         "    <toolitem action='SpiralT0Action' />"
363         "    <separator />"
364         "    <toolitem action='SpiralResetAction' />"
365         "  </toolbar>"
367         "  <toolbar name='PenToolbar'>"
368         "    <toolitem action='FreehandModeActionPenTemp' />"
369         "    <toolitem action='FreehandModeActionPen' />"
370         "    <separator />"
371         "    <toolitem action='SetPenShapeAction'/>"
372         "  </toolbar>"
374         "  <toolbar name='PencilToolbar'>"
375         "    <toolitem action='FreehandModeActionPencilTemp' />"
376         "    <toolitem action='FreehandModeActionPencil' />"
377         "    <separator />"
378         "    <toolitem action='PencilToleranceAction' />"
379         "    <separator />"
380         "    <toolitem action='PencilResetAction' />"
381         "    <separator />"
382         "    <toolitem action='SetPencilShapeAction'/>"
383         "  </toolbar>"
385         "  <toolbar name='CalligraphyToolbar'>"
386         "    <separator />"
387         "    <toolitem action='SetProfileAction'/>"
388         "    <toolitem action='SaveDeleteProfileAction'/>"
389         "    <separator />"
390         "    <toolitem action='CalligraphyWidthAction' />"
391         "    <toolitem action='PressureAction' />"
392         "    <toolitem action='TraceAction' />"
393         "    <toolitem action='ThinningAction' />"
394         "    <separator />"
395         "    <toolitem action='AngleAction' />"
396         "    <toolitem action='TiltAction' />"
397         "    <toolitem action='FixationAction' />"
398         "    <separator />"
399         "    <toolitem action='CapRoundingAction' />"
400         "    <separator />"
401         "    <toolitem action='TremorAction' />"
402         "    <toolitem action='WiggleAction' />"
403         "    <toolitem action='MassAction' />"
404         "    <separator />"
405         "  </toolbar>"
407         "  <toolbar name='ArcToolbar'>"
408         "    <toolitem action='ArcStateAction' />"
409         "    <separator />"
410         "    <toolitem action='ArcStartAction' />"
411         "    <toolitem action='ArcEndAction' />"
412         "    <separator />"
413         "    <toolitem action='ArcOpenAction' />"
414         "    <separator />"
415         "    <toolitem action='ArcResetAction' />"
416         "    <separator />"
417         "  </toolbar>"
419         "  <toolbar name='PaintbucketToolbar'>"
420         "    <toolitem action='ChannelsAction' />"
421         "    <separator />"
422         "    <toolitem action='ThresholdAction' />"
423         "    <separator />"
424         "    <toolitem action='OffsetAction' />"
425         "    <toolitem action='PaintbucketUnitsAction' />"
426         "    <separator />"
427         "    <toolitem action='AutoGapAction' />"
428         "    <separator />"
429         "    <toolitem action='PaintbucketResetAction' />"
430         "  </toolbar>"
432         "  <toolbar name='EraserToolbar'>"
433         "    <toolitem action='EraserWidthAction' />"
434         "    <separator />"
435         "    <toolitem action='EraserModeAction' />"
436         "  </toolbar>"
438         "  <toolbar name='DropperToolbar'>"
439         "    <toolitem action='DropperOpacityAction' />"
440         "    <toolitem action='DropperPickAlphaAction' />"
441         "    <toolitem action='DropperSetAlphaAction' />"
442         "  </toolbar>"
444         "  <toolbar name='ConnectorToolbar'>"
445         "    <toolitem action='ConnectorAvoidAction' />"
446         "    <toolitem action='ConnectorIgnoreAction' />"
447         "    <toolitem action='ConnectorSpacingAction' />"
448         "    <toolitem action='ConnectorGraphAction' />"
449         "    <toolitem action='ConnectorLengthAction' />"
450         "    <toolitem action='ConnectorDirectedAction' />"
451         "    <toolitem action='ConnectorOverlapAction' />"
452         "  </toolbar>"
454         "</ui>"
457 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
459 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
461 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
462 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
464 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
465 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
467 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
468 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
471 GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick( GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
472                                                               Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
473                                                               Inkscape::UI::View::View *view, GtkTooltips *tt);
475 class VerbAction : public Gtk::Action {
476 public:
477     static Glib::RefPtr<VerbAction> create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
479     virtual ~VerbAction();
480     virtual void set_active(bool active = true);
482 protected:
483     virtual Gtk::Widget* create_menu_item_vfunc();
484     virtual Gtk::Widget* create_tool_item_vfunc();
486     virtual void connect_proxy_vfunc(Gtk::Widget* proxy);
487     virtual void disconnect_proxy_vfunc(Gtk::Widget* proxy);
489     virtual void on_activate();
491 private:
492     Inkscape::Verb* verb;
493     Inkscape::Verb* verb2;
494     Inkscape::UI::View::View *view;
495     GtkTooltips *tooltips;
496     bool active;
498     VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
499 };
502 Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips)
504     Glib::RefPtr<VerbAction> result;
505     SPAction *action = verb->get_action(view);
506     if ( action ) {
507         //SPAction* action2 = verb2 ? verb2->get_action(view) : 0;
508         result = Glib::RefPtr<VerbAction>(new VerbAction(verb, verb2, view, tooltips));
509     }
511     return result;
514 VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips) :
515     Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(GTK_STOCK_ABOUT), Glib::ustring(_(verb->get_name())), Glib::ustring(_(verb->get_tip()))),
516     verb(verb),
517     verb2(verb2),
518     view(view),
519     tooltips(tooltips),
520     active(false)
524 VerbAction::~VerbAction()
528 Gtk::Widget* VerbAction::create_menu_item_vfunc()
530     Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
531 //     g_message("create_menu_item_vfunc() = %p  for '%s'", widg, verb->get_id());
532     return widg;
535 Gtk::Widget* VerbAction::create_tool_item_vfunc()
537 //     Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
538     Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
539     GtkWidget* toolbox = 0;
540     GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
541                                                                           SP_BUTTON_TYPE_TOGGLE,
542                                                                           verb,
543                                                                           verb2,
544                                                                           view,
545                                                                           tooltips );
546     if ( active ) {
547         sp_button_toggle_set_down( SP_BUTTON(button), active);
548     }
549     gtk_widget_show_all( button );
550     Gtk::Widget* wrapped = Glib::wrap(button);
551     Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
552     holder->add(*wrapped);
554 //     g_message("create_tool_item_vfunc() = %p  for '%s'", holder, verb->get_id());
555     return holder;
558 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
560 //     g_message("connect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
561     Gtk::Action::connect_proxy_vfunc(proxy);
564 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
566 //     g_message("disconnect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
567     Gtk::Action::disconnect_proxy_vfunc(proxy);
570 void VerbAction::set_active(bool active)
572     this->active = active;
573     Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
574     for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
575         Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
576         if (ti) {
577             // *should* have one child that is the SPButton
578             Gtk::Widget* child = ti->get_child();
579             if ( child && SP_IS_BUTTON(child->gobj()) ) {
580                 SPButton* button = SP_BUTTON(child->gobj());
581                 sp_button_toggle_set_down( button, active );
582             }
583         }
584     }
587 void VerbAction::on_activate()
589     if ( verb ) {
590         SPAction *action = verb->get_action(view);
591         if ( action ) {
592             sp_action_perform(action, 0);
593         }
594     }
597 /* Global text entry widgets necessary for update */
598 /* GtkWidget *dropper_rgb_entry,
599           *dropper_opacity_entry ; */
600 // should be made a private member once this is converted to class
602 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
603     connection->disconnect();
604     delete connection;
607 static void purge_repr_listener( GObject* obj, GObject* tbl )
609     (void)obj;
610     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
611     if (oldrepr) { // remove old listener
612         sp_repr_remove_listener_by_data(oldrepr, tbl);
613         Inkscape::GC::release(oldrepr);
614         oldrepr = 0;
615         g_object_set_data( tbl, "repr", NULL );
616     }
619 GtkWidget *
620 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
621                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
622                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
624     SPAction *action = verb->get_action(view);
625     if (!action) return NULL;
627     SPAction *doubleclick_action;
628     if (doubleclick_verb)
629         doubleclick_action = doubleclick_verb->get_action(view);
630     else
631         doubleclick_action = NULL;
633     /* fixme: Handle sensitive/unsensitive */
634     /* fixme: Implement sp_button_new_from_action */
635     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
636     gtk_widget_show(b);
639     unsigned int shortcut = sp_shortcut_get_primary(verb);
640     if (shortcut) {
641         gchar key[256];
642         sp_ui_shortcut_string(shortcut, key);
643         gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
644         if ( t ) {
645             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
646         }
647         g_free(tip);
648     } else {
649         if ( t ) {
650             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
651         }
652     }
654     return b;
658 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
660     SPAction* targetAction = SP_ACTION(user_data);
661     if ( targetAction ) {
662         sp_action_perform( targetAction, NULL );
663     }
666 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
668     if ( data ) {
669         GtkAction* act = GTK_ACTION(data);
670         gtk_action_set_sensitive( act, sensitive );
671     }
674 static SPActionEventVector action_event_vector = {
675     {NULL},
676     NULL,
677     NULL,
678     sp_action_action_set_sensitive,
679     NULL,
680     NULL
681 };
683 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
685     GtkAction* act = 0;
687     SPAction* targetAction = verb->get_action(view);
688     InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size  );
689     act = GTK_ACTION(inky);
690     gtk_action_set_sensitive( act, targetAction->sensitive );
692     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
694     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
695     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
697     return act;
700 Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
702     Inkscape::UI::View::View *view = desktop;
703     gint verbsToUse[] = {
704         // disabled until we have icons for them:
705         //find
706         //SP_VERB_EDIT_TILE,
707         //SP_VERB_EDIT_UNTILE,
708         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
709         SP_VERB_DIALOG_DISPLAY,
710         SP_VERB_DIALOG_FILL_STROKE,
711         SP_VERB_DIALOG_NAMEDVIEW,
712         SP_VERB_DIALOG_TEXT,
713         SP_VERB_DIALOG_XML_EDITOR,
714         SP_VERB_EDIT_CLONE,
715         SP_VERB_EDIT_COPY,
716         SP_VERB_EDIT_CUT,
717         SP_VERB_EDIT_DUPLICATE,
718         SP_VERB_EDIT_PASTE,
719         SP_VERB_EDIT_REDO,
720         SP_VERB_EDIT_UNDO,
721         SP_VERB_EDIT_UNLINK_CLONE,
722         SP_VERB_FILE_EXPORT,
723         SP_VERB_FILE_IMPORT,
724         SP_VERB_FILE_NEW,
725         SP_VERB_FILE_OPEN,
726         SP_VERB_FILE_PRINT,
727         SP_VERB_FILE_SAVE,
728         SP_VERB_OBJECT_TO_CURVE,
729         SP_VERB_SELECTION_GROUP,
730         SP_VERB_SELECTION_OUTLINE,
731         SP_VERB_SELECTION_UNGROUP,
732         SP_VERB_ZOOM_1_1,
733         SP_VERB_ZOOM_1_2,
734         SP_VERB_ZOOM_2_1,
735         SP_VERB_ZOOM_DRAWING,
736         SP_VERB_ZOOM_IN,
737         SP_VERB_ZOOM_NEXT,
738         SP_VERB_ZOOM_OUT,
739         SP_VERB_ZOOM_PAGE,
740         SP_VERB_ZOOM_PAGE_WIDTH,
741         SP_VERB_ZOOM_PREV,
742         SP_VERB_ZOOM_SELECTION,
743     };
745     Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
747     static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
748     Glib::RefPtr<Gtk::ActionGroup> mainActions;
749     if ( groups.find(desktop) != groups.end() ) {
750         mainActions = groups[desktop];
751     }
753     if ( !mainActions ) {
754         mainActions = Gtk::ActionGroup::create("main");
755         groups[desktop] = mainActions;
756     }
758     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
759         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
760         if ( verb ) {
761             if (!mainActions->get_action(verb->get_id())) {
762                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
763                 mainActions->add(Glib::wrap(act));
764             }
765         }
766     }
768     if ( !mainActions->get_action("ToolZoom") ) {
769         GtkTooltips *tt = gtk_tooltips_new();
770         for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
771             Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
772             if ( va ) {
773                 mainActions->add(va);
774                 if ( i == 0 ) {
775                     va->set_active(true);
776                 }
777             }
778         }
779     }
782     return mainActions;
786 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
788     gtk_widget_set_size_request( widget,
789                                  widget->allocation.width,
790                                  widget->allocation.height );
793 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
795     gtk_widget_set_size_request( widget, -1, -1 );
800 GtkWidget *
801 sp_tool_toolbox_new()
803     GtkTooltips *tt = gtk_tooltips_new();
804     GtkWidget* tb = gtk_toolbar_new();
805     gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
806     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
808     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
809     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
811     gtk_widget_set_sensitive(tb, FALSE);
813     GtkWidget *hb = gtk_handle_box_new();
814     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
815     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
816     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
818     gtk_container_add(GTK_CONTAINER(hb), tb);
819     gtk_widget_show(GTK_WIDGET(tb));
821     sigc::connection* conn = new sigc::connection;
822     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
824     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
825     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
827     return hb;
830 GtkWidget *
831 sp_aux_toolbox_new()
833     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
835     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
837     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
839     gtk_widget_set_sensitive(tb, FALSE);
841     GtkWidget *hb = gtk_handle_box_new();
842     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
843     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
844     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
846     gtk_container_add(GTK_CONTAINER(hb), tb);
847     gtk_widget_show(GTK_WIDGET(tb));
849     sigc::connection* conn = new sigc::connection;
850     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
852     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
853     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
855     return hb;
858 //####################################
859 //# Commands Bar
860 //####################################
862 GtkWidget *
863 sp_commands_toolbox_new()
865     GtkWidget *tb = gtk_toolbar_new();
867     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
868     gtk_widget_set_sensitive(tb, FALSE);
870     GtkWidget *hb = gtk_handle_box_new();
871     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
872     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
873     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
875     gtk_container_add(GTK_CONTAINER(hb), tb);
876     gtk_widget_show(GTK_WIDGET(tb));
878     sigc::connection* conn = new sigc::connection;
879     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
881     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
882     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
884     return hb;
888 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
889                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
890                                                        gchar const *path, gchar const *data, gdouble def,
891                                                        GtkWidget *focusTarget,
892                                                        GtkWidget *us,
893                                                        GObject *dataKludge,
894                                                        gboolean altx, gchar const *altx_mark,
895                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
896                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
897                                                        void (*callback)(GtkAdjustment *, GObject *),
898                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
900     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
901                                                              lower, upper, step, page, page ) );
902     if (us) {
903         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
904     }
906     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
908     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
909     if ( shortLabel ) {
910         g_object_set( act, "short_label", shortLabel, NULL );
911     }
913     if ( (descrCount > 0) && descrLabels && descrValues ) {
914         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
915     }
917     if ( focusTarget ) {
918         ege_adjustment_action_set_focuswidget( act, focusTarget );
919     }
921     if ( altx && altx_mark ) {
922         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
923     }
925     if ( dataKludge ) {
926         g_object_set_data( dataKludge, data, adj );
927     }
929     // Using a cast just to make sure we pass in the right kind of function pointer
930     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
932     return act;
936 //####################################
937 //# node editing callbacks
938 //####################################
940 /**
941  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
942  */
943 static ShapeEditor *get_current_shape_editor()
945     if (!SP_ACTIVE_DESKTOP) {
946         return NULL;
947     }
949     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
951     if (!SP_IS_NODE_CONTEXT(event_context)) {
952         return NULL;
953     }
955     return SP_NODE_CONTEXT(event_context)->shape_editor;
959 void
960 sp_node_path_edit_add(void)
962     ShapeEditor *shape_editor = get_current_shape_editor();
963     if (shape_editor) shape_editor->add_node();
966 void
967 sp_node_path_edit_delete(void)
969     ShapeEditor *shape_editor = get_current_shape_editor();
970     if (shape_editor) shape_editor->delete_nodes_preserving_shape();
973 void
974 sp_node_path_edit_delete_segment(void)
976     ShapeEditor *shape_editor = get_current_shape_editor();
977     if (shape_editor) shape_editor->delete_segment();
980 void
981 sp_node_path_edit_break(void)
983     ShapeEditor *shape_editor = get_current_shape_editor();
984     if (shape_editor) shape_editor->break_at_nodes();
987 void
988 sp_node_path_edit_join(void)
990     ShapeEditor *shape_editor = get_current_shape_editor();
991     if (shape_editor) shape_editor->join_nodes();
994 void
995 sp_node_path_edit_join_segment(void)
997     ShapeEditor *shape_editor = get_current_shape_editor();
998     if (shape_editor) shape_editor->join_segments();
1001 void
1002 sp_node_path_edit_toline(void)
1004     ShapeEditor *shape_editor = get_current_shape_editor();
1005     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1008 void
1009 sp_node_path_edit_tocurve(void)
1011     ShapeEditor *shape_editor = get_current_shape_editor();
1012     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1015 void
1016 sp_node_path_edit_cusp(void)
1018     ShapeEditor *shape_editor = get_current_shape_editor();
1019     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1022 void
1023 sp_node_path_edit_smooth(void)
1025     ShapeEditor *shape_editor = get_current_shape_editor();
1026     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1029 void
1030 sp_node_path_edit_symmetrical(void)
1032     ShapeEditor *shape_editor = get_current_shape_editor();
1033     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1036 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1037     bool show = gtk_toggle_action_get_active( act );
1038     prefs_set_int_attribute ("tools.nodes", "show_handles",  show ? 1 : 0);
1039     ShapeEditor *shape_editor = get_current_shape_editor();
1040     if (shape_editor) shape_editor->show_handles(show);
1043 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1044     bool show = gtk_toggle_action_get_active( act );
1045     prefs_set_int_attribute ("tools.nodes", "show_helperpath",  show ? 1 : 0);
1046     ShapeEditor *shape_editor = get_current_shape_editor();
1047     if (shape_editor) shape_editor->show_helperpath(show);
1050 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1051     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1054 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1055     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1058 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1059     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1062 /* is called when the node selection is modified */
1063 static void
1064 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1066     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1067     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1068     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1069     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1071     // quit if run by the attr_changed listener
1072     if (g_object_get_data( tbl, "freeze" )) {
1073         return;
1074     }
1076     // in turn, prevent listener from responding
1077     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1079     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1080     SPUnit const *unit = tracker->getActiveUnit();
1082     ShapeEditor *shape_editor = get_current_shape_editor();
1083     if (shape_editor && shape_editor->has_nodepath()) {
1084         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1085         int n_selected = 0;
1086         if (nodepath) {
1087             n_selected = nodepath->numSelected();
1088         }
1090         if (n_selected == 0) {
1091             gtk_action_set_sensitive(xact, FALSE);
1092             gtk_action_set_sensitive(yact, FALSE);
1093         } else {
1094             gtk_action_set_sensitive(xact, TRUE);
1095             gtk_action_set_sensitive(yact, TRUE);
1096             NR::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1097             NR::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1099             if (n_selected == 1) {
1100                 NR::Point sel_node = nodepath->singleSelectedCoords();
1101                 if (oldx != sel_node[NR::X] || oldy != sel_node[NR::Y]) {
1102                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[NR::X], *unit));
1103                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[NR::Y], *unit));
1104                 }
1105             } else {
1106                 NR::Maybe<NR::Coord> x = sp_node_selected_common_coord(nodepath, NR::X);
1107                 NR::Maybe<NR::Coord> y = sp_node_selected_common_coord(nodepath, NR::Y);
1108                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1109                     /* Note: Currently x and y will always have a value, even if the coordinates of the
1110                        selected nodes don't coincide (in this case we use the coordinates of the center
1111                        of the bounding box). So the entries are never set to zero. */
1112                     // FIXME: Maybe we should clear the entry if several nodes are selected
1113                     //        instead of providing a kind of average value
1114                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1115                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1116                 }
1117             }
1118         }
1119     } else {
1120         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1121         gtk_action_set_sensitive(xact, FALSE);
1122         gtk_action_set_sensitive(yact, FALSE);
1123     }
1125     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1128 static void
1129 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1131     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1133     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1134     SPUnit const *unit = tracker->getActiveUnit();
1136     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1137         prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
1138     }
1140     // quit if run by the attr_changed listener
1141     if (g_object_get_data( tbl, "freeze" )) {
1142         return;
1143     }
1145     // in turn, prevent listener from responding
1146     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1148     ShapeEditor *shape_editor = get_current_shape_editor();
1149     if (shape_editor && shape_editor->has_nodepath()) {
1150         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1151         if (!strcmp(value_name, "x")) {
1152             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::X);
1153         }
1154         if (!strcmp(value_name, "y")) {
1155             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::Y);
1156         }
1157     }
1159     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1162 static void
1163 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1165     sp_node_path_value_changed(adj, tbl, "x");
1168 static void
1169 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1171     sp_node_path_value_changed(adj, tbl, "y");
1174 void
1175 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1177     {
1178     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1179     SPItem *item = selection->singleItem();
1180     if (item && SP_IS_LPE_ITEM(item)) {
1181        if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1182            gtk_action_set_sensitive(w, TRUE);
1183        } else {
1184            gtk_action_set_sensitive(w, FALSE);
1185        }
1186     } else {
1187        gtk_action_set_sensitive(w, FALSE);
1188     }
1189     }
1191     {
1192     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1193     SPItem *item = selection->singleItem();
1194     if (item && item->clip_ref && item->clip_ref->getObject()) {
1195        gtk_action_set_sensitive(w, TRUE);
1196     } else {
1197        gtk_action_set_sensitive(w, FALSE);
1198     }
1199     }
1201     {
1202     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1203     SPItem *item = selection->singleItem();
1204     if (item && item->mask_ref && item->mask_ref->getObject()) {
1205        gtk_action_set_sensitive(w, TRUE);
1206     } else {
1207        gtk_action_set_sensitive(w, FALSE);
1208     }
1209     }
1212 void
1213 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1215     sp_node_toolbox_sel_changed (selection, tbl);
1220 //################################
1221 //##    Node Editing Toolbox    ##
1222 //################################
1224 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1226     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1227     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1228     g_object_set_data( holder, "tracker", tracker );
1230     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
1232     {
1233         InkAction* inky = ink_action_new( "NodeInsertAction",
1234                                           _("Insert node"),
1235                                           _("Insert new nodes into selected segments"),
1236                                           "node_insert",
1237                                           secondarySize );
1238         g_object_set( inky, "short_label", _("Insert"), NULL );
1239         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1240         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1241     }
1243     {
1244         InkAction* inky = ink_action_new( "NodeDeleteAction",
1245                                           _("Delete node"),
1246                                           _("Delete selected nodes"),
1247                                           "node_delete",
1248                                           secondarySize );
1249         g_object_set( inky, "short_label", _("Delete"), NULL );
1250         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1251         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1252     }
1254     {
1255         InkAction* inky = ink_action_new( "NodeJoinAction",
1256                                           _("Join endnodes"),
1257                                           _("Join selected endnodes"),
1258                                           "node_join",
1259                                           secondarySize );
1260         g_object_set( inky, "short_label", _("Join"), NULL );
1261         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1262         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1263     }
1265     {
1266         InkAction* inky = ink_action_new( "NodeBreakAction",
1267                                           _("Break nodes"),
1268                                           _("Break path at selected nodes"),
1269                                           "node_break",
1270                                           secondarySize );
1271         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1272         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1273     }
1276     {
1277         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1278                                           _("Join with segment"),
1279                                           _("Join selected endnodes with a new segment"),
1280                                           "node_join_segment",
1281                                           secondarySize );
1282         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1283         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1284     }
1286     {
1287         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1288                                           _("Delete segment"),
1289                                           _("Delete segment between two non-endpoint nodes"),
1290                                           "node_delete_segment",
1291                                           secondarySize );
1292         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1293         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1294     }
1296     {
1297         InkAction* inky = ink_action_new( "NodeCuspAction",
1298                                           _("Node Cusp"),
1299                                           _("Make selected nodes corner"),
1300                                           "node_cusp",
1301                                           secondarySize );
1302         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1303         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1304     }
1306     {
1307         InkAction* inky = ink_action_new( "NodeSmoothAction",
1308                                           _("Node Smooth"),
1309                                           _("Make selected nodes smooth"),
1310                                           "node_smooth",
1311                                           secondarySize );
1312         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1313         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1314     }
1316     {
1317         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1318                                           _("Node Symmetric"),
1319                                           _("Make selected nodes symmetric"),
1320                                           "node_symmetric",
1321                                           secondarySize );
1322         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1323         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1324     }
1326     {
1327         InkAction* inky = ink_action_new( "NodeLineAction",
1328                                           _("Node Line"),
1329                                           _("Make selected segments lines"),
1330                                           "node_line",
1331                                           secondarySize );
1332         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1333         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1334     }
1336     {
1337         InkAction* inky = ink_action_new( "NodeCurveAction",
1338                                           _("Node Curve"),
1339                                           _("Make selected segments curves"),
1340                                           "node_curve",
1341                                           secondarySize );
1342         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1343         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1344     }
1346     {
1347         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1348                                                       _("Show Handles"),
1349                                                       _("Show the Bezier handles of selected nodes"),
1350                                                       "nodes_show_handles",
1351                                                       Inkscape::ICON_SIZE_DECORATION );
1352         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1353         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1354         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1355     }
1357     {
1358         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1359                                                       _("Show Outline"),
1360                                                       _("Show the outline of the path"),
1361                                                       "nodes_show_helperpath",
1362                                                       Inkscape::ICON_SIZE_DECORATION );
1363         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1364         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1365         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_helperpath", 0 ) );
1366     }
1368     {
1369         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1370                                           _("Next path effect parameter"),
1371                                           _("Show next path effect parameter for editing"),
1372                                           "edit_next_parameter",
1373                                           Inkscape::ICON_SIZE_DECORATION );
1374         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1375         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1376         g_object_set_data( holder, "nodes_lpeedit", inky);
1377     }
1379     {
1380         InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1381                                           _("Edit clipping path"),
1382                                           _("Edit the clipping path of the object"),
1383                                           "nodeedit-clippath",
1384                                           Inkscape::ICON_SIZE_DECORATION );
1385         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1386         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1387         g_object_set_data( holder, "nodes_clippathedit", inky);
1388     }
1390     {
1391         InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1392                                           _("Edit mask path"),
1393                                           _("Edit the mask of the object"),
1394                                           "nodeedit-mask",
1395                                           Inkscape::ICON_SIZE_DECORATION );
1396         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1397         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1398         g_object_set_data( holder, "nodes_maskedit", inky);
1399     }
1401     /* X coord of selected node(s) */
1402     {
1403         EgeAdjustmentAction* eact = 0;
1404         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1405         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1406         eact = create_adjustment_action( "NodeXAction",
1407                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1408                                          "tools.nodes", "Xcoord", 0,
1409                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1410                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1411                                          labels, values, G_N_ELEMENTS(labels),
1412                                          sp_node_path_x_value_changed );
1413         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1414         g_object_set_data( holder, "nodes_x_action", eact );
1415         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1416         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1417     }
1419     /* Y coord of selected node(s) */
1420     {
1421         EgeAdjustmentAction* eact = 0;
1422         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1423         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1424         eact = create_adjustment_action( "NodeYAction",
1425                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1426                                          "tools.nodes", "Ycoord", 0,
1427                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1428                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1429                                          labels, values, G_N_ELEMENTS(labels),
1430                                          sp_node_path_y_value_changed );
1431         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1432         g_object_set_data( holder, "nodes_y_action", eact );
1433         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1434         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1435     }
1437     // add the units menu
1438     {
1439         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1440         gtk_action_group_add_action( mainActions, act );
1441     }
1444     sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1446     //watch selection
1447     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1449     sigc::connection *c_selection_changed =
1450         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1451                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1452     pool->add_connection ("selection-changed", c_selection_changed);
1454     sigc::connection *c_selection_modified =
1455         new sigc::connection (sp_desktop_selection (desktop)->connectModified
1456                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1457     pool->add_connection ("selection-modified", c_selection_modified);
1459     sigc::connection *c_subselection_changed =
1460         new sigc::connection (desktop->connectToolSubselectionChanged
1461                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1462     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1464     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1466     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1467 } // end of sp_node_toolbox_prep()
1470 //########################
1471 //##    Zoom Toolbox    ##
1472 //########################
1474 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1476     // no custom GtkAction setup needed
1477 } // end of sp_zoom_toolbox_prep()
1479 void
1480 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1482     toolbox_set_desktop(toolbox,
1483                         desktop,
1484                         setup_tool_toolbox,
1485                         update_tool_toolbox,
1486                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1487                                                                          "event_context_connection")));
1491 void
1492 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1494     toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1495                         desktop,
1496                         setup_aux_toolbox,
1497                         update_aux_toolbox,
1498                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1499                                                                          "event_context_connection")));
1502 void
1503 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1505     toolbox_set_desktop(toolbox,
1506                         desktop,
1507                         setup_commands_toolbox,
1508                         update_commands_toolbox,
1509                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1510                                                                          "event_context_connection")));
1513 static void
1514 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1516     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1517     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1519     if (old_desktop) {
1520         GList *children, *iter;
1522         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1523         for ( iter = children ; iter ; iter = iter->next ) {
1524             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1525         }
1526         g_list_free(children);
1527     }
1529     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1531     if (desktop) {
1532         gtk_widget_set_sensitive(toolbox, TRUE);
1533         setup_func(toolbox, desktop);
1534         update_func(desktop, desktop->event_context, toolbox);
1535         *conn = desktop->connectEventContextChanged
1536             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1537     } else {
1538         gtk_widget_set_sensitive(toolbox, FALSE);
1539     }
1541 } // end of toolbox_set_desktop()
1544 static void
1545 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1547     gchar const * descr =
1548         "<ui>"
1549         "  <toolbar name='ToolToolbar'>"
1550         "    <toolitem action='ToolSelector' />"
1551         "    <toolitem action='ToolNode' />"
1552         "    <toolitem action='ToolTweak' />"
1553         "    <toolitem action='ToolZoom' />"
1554         "    <toolitem action='ToolRect' />"
1555         "    <toolitem action='Tool3DBox' />"
1556         "    <toolitem action='ToolArc' />"
1557         "    <toolitem action='ToolStar' />"
1558         "    <toolitem action='ToolSpiral' />"
1559         "    <toolitem action='ToolPencil' />"
1560         "    <toolitem action='ToolPen' />"
1561         "    <toolitem action='ToolCalligraphic' />"
1562         "    <toolitem action='ToolEraser' />"
1563         "    <toolitem action='ToolPaintBucket' />"
1564         "    <toolitem action='ToolText' />"
1565         "    <toolitem action='ToolConnector' />"
1566         "    <toolitem action='ToolGradient' />"
1567         "    <toolitem action='ToolDropper' />"
1568         "  </toolbar>"
1569         "</ui>";
1570     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1571     GtkUIManager* mgr = gtk_ui_manager_new();
1572     GError* errVal = 0;
1574     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1575     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1577     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" );
1578     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1579         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1580     }
1581     Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1582     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1584     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
1585     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1587     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1589     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1590     if ( child ) {
1591         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1592     }
1594     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1595 //     Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1599 static void
1600 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox )
1602     gchar const *const tname = ( eventcontext
1603                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1604                                  : NULL );
1605     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1607     for (int i = 0 ; tools[i].type_name ; i++ ) {
1608         Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1609         if ( act ) {
1610             bool setActive = tname && !strcmp(tname, tools[i].type_name);
1611             Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1612             if ( verbAct ) {
1613                 verbAct->set_active(setActive);
1614             }
1615         }
1616     }
1619 static void
1620 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1622     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1623     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1624     GtkUIManager* mgr = gtk_ui_manager_new();
1625     GError* errVal = 0;
1626     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1627     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1629     std::map<std::string, GtkWidget*> dataHolders;
1631     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1632         if ( aux_toolboxes[i].prep_func ) {
1633             // converted to GtkActions and UIManager
1635             GtkWidget* kludge = gtk_toolbar_new();
1636             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1637             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1638             dataHolders[aux_toolboxes[i].type_name] = kludge;
1639             aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1640         } else {
1642             GtkWidget *sub_toolbox = 0;
1643             if (aux_toolboxes[i].create_func == NULL)
1644                 sub_toolbox = sp_empty_toolbox_new(desktop);
1645             else {
1646                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1647             }
1649             gtk_size_group_add_widget( grouper, sub_toolbox );
1651             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1652             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1654         }
1655     }
1657     // Second pass to create toolbars *after* all GtkActions are created
1658     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1659         if ( aux_toolboxes[i].prep_func ) {
1660             // converted to GtkActions and UIManager
1662             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1664             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1665             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1667             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1668             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1669             g_free( tmp );
1670             tmp = 0;
1672             Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1673             if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1674                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1675             }
1676             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1679             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1681             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1682                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1683                 swatch->setDesktop( desktop );
1684                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1685                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1686                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1687                 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 );
1688             }
1690             gtk_widget_show_all( holder );
1691             sp_set_font_size_smaller( holder );
1693             gtk_size_group_add_widget( grouper, holder );
1695             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1696             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1697         }
1698     }
1700     g_object_unref( G_OBJECT(grouper) );
1703 static void
1704 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1706     gchar const *tname = ( eventcontext
1707                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1708                            : NULL );
1709     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1710         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1711         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1712             gtk_widget_show_all(sub_toolbox);
1713             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1714         } else {
1715             gtk_widget_hide(sub_toolbox);
1716         }
1717     }
1720 static void
1721 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1723     gchar const * descr =
1724         "<ui>"
1725         "  <toolbar name='CommandsToolbar'>"
1726         "    <toolitem action='FileNew' />"
1727         "    <toolitem action='FileOpen' />"
1728         "    <toolitem action='FileSave' />"
1729         "    <toolitem action='FilePrint' />"
1730         "    <separator />"
1731         "    <toolitem action='FileImport' />"
1732         "    <toolitem action='FileExport' />"
1733         "    <separator />"
1734         "    <toolitem action='EditUndo' />"
1735         "    <toolitem action='EditRedo' />"
1736         "    <separator />"
1737         "    <toolitem action='EditCopy' />"
1738         "    <toolitem action='EditCut' />"
1739         "    <toolitem action='EditPaste' />"
1740         "    <separator />"
1741         "    <toolitem action='ZoomSelection' />"
1742         "    <toolitem action='ZoomDrawing' />"
1743         "    <toolitem action='ZoomPage' />"
1744         "    <separator />"
1745         "    <toolitem action='EditDuplicate' />"
1746         "    <toolitem action='EditClone' />"
1747         "    <toolitem action='EditUnlinkClone' />"
1748         "    <separator />"
1749         "    <toolitem action='SelectionGroup' />"
1750         "    <toolitem action='SelectionUnGroup' />"
1751         "    <separator />"
1752         "    <toolitem action='DialogFillStroke' />"
1753         "    <toolitem action='DialogText' />"
1754         "    <toolitem action='DialogXMLEditor' />"
1755         "    <toolitem action='DialogAlignDistribute' />"
1756         "    <separator />"
1757         "    <toolitem action='DialogPreferences' />"
1758         "    <toolitem action='DialogDocumentProperties' />"
1759         "  </toolbar>"
1760         "</ui>";
1761     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1764     GtkUIManager* mgr = gtk_ui_manager_new();
1765     GError* errVal = 0;
1767     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1768     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1770     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1771     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1772         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1773     }
1775     Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1776     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1778     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1779     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1782     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1784     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1785     if ( child ) {
1786         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1787     }
1789     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1792 static void
1793 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1797 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1799     gtk_widget_show(toolbox_toplevel);
1800     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1802     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1803     if (!shown_toolbox) {
1804         return;
1805     }
1806     gtk_widget_show(toolbox);
1808     gtk_widget_show_all(shown_toolbox);
1811 static GtkWidget *
1812 sp_empty_toolbox_new(SPDesktop *desktop)
1814     GtkWidget *tbl = gtk_toolbar_new();
1815     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1816     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1818     gtk_widget_show_all(tbl);
1819     sp_set_font_size_smaller (tbl);
1821     return tbl;
1824 #define MODE_LABEL_WIDTH 70
1826 //########################
1827 //##       Star         ##
1828 //########################
1830 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1832     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1834     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1835         // do not remember prefs if this call is initiated by an undo change, because undoing object
1836         // creation sets bogus values to its attributes before it is deleted
1837         prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1838     }
1840     // quit if run by the attr_changed listener
1841     if (g_object_get_data( dataKludge, "freeze" )) {
1842         return;
1843     }
1845     // in turn, prevent listener from responding
1846     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1848     bool modmade = false;
1850     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1851     GSList const *items = selection->itemList();
1852     for (; items != NULL; items = items->next) {
1853         if (SP_IS_STAR((SPItem *) items->data)) {
1854             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1855             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1856             sp_repr_set_svg_double(repr, "sodipodi:arg2",
1857                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1858                                     + M_PI / (gint)adj->value));
1859             SP_OBJECT((SPItem *) items->data)->updateRepr();
1860             modmade = true;
1861         }
1862     }
1863     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1864                                    _("Star: Change number of corners"));
1866     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1869 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1871     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1873     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1874         prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1875     }
1877     // quit if run by the attr_changed listener
1878     if (g_object_get_data( dataKludge, "freeze" )) {
1879         return;
1880     }
1882     // in turn, prevent listener from responding
1883     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1885     bool modmade = false;
1886     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1887     GSList const *items = selection->itemList();
1888     for (; items != NULL; items = items->next) {
1889         if (SP_IS_STAR((SPItem *) items->data)) {
1890             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1892             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1893             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1894             if (r2 < r1) {
1895                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1896             } else {
1897                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1898             }
1900             SP_OBJECT((SPItem *) items->data)->updateRepr();
1901             modmade = true;
1902         }
1903     }
1905     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1906                                    _("Star: Change spoke ratio"));
1908     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1911 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1913     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1914     bool flat = ege_select_one_action_get_active( act ) == 0;
1916     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1917         prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1918                                     flat ? "true" : "false" );
1919     }
1921     // quit if run by the attr_changed listener
1922     if (g_object_get_data( dataKludge, "freeze" )) {
1923         return;
1924     }
1926     // in turn, prevent listener from responding
1927     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1929     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1930     GSList const *items = selection->itemList();
1931     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1932     bool modmade = false;
1934     if ( prop_action ) {
1935         gtk_action_set_sensitive( prop_action, !flat );
1936     }
1938     for (; items != NULL; items = items->next) {
1939         if (SP_IS_STAR((SPItem *) items->data)) {
1940             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1941             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1942             SP_OBJECT((SPItem *) items->data)->updateRepr();
1943             modmade = true;
1944         }
1945     }
1947     if (modmade) {
1948         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1949                          flat ? _("Make polygon") : _("Make star"));
1950     }
1952     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1955 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1957     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1959     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1960         prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1961     }
1963     // quit if run by the attr_changed listener
1964     if (g_object_get_data( dataKludge, "freeze" )) {
1965         return;
1966     }
1968     // in turn, prevent listener from responding
1969     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1971     bool modmade = false;
1973     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1974     GSList const *items = selection->itemList();
1975     for (; items != NULL; items = items->next) {
1976         if (SP_IS_STAR((SPItem *) items->data)) {
1977             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1978             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1979             SP_OBJECT(items->data)->updateRepr();
1980             modmade = true;
1981         }
1982     }
1983     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1984                                    _("Star: Change rounding"));
1986     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1989 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1991     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1993     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1994         prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1995     }
1997     // quit if run by the attr_changed listener
1998     if (g_object_get_data( dataKludge, "freeze" )) {
1999         return;
2000     }
2002     // in turn, prevent listener from responding
2003     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2005     bool modmade = false;
2007     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2008     GSList const *items = selection->itemList();
2009     for (; items != NULL; items = items->next) {
2010         if (SP_IS_STAR((SPItem *) items->data)) {
2011             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2012             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2013             SP_OBJECT(items->data)->updateRepr();
2014             modmade = true;
2015         }
2016     }
2017     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2018                                    _("Star: Change randomization"));
2020     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2024 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2025                                        gchar const */*old_value*/, gchar const */*new_value*/,
2026                                        bool /*is_interactive*/, gpointer data)
2028     GtkWidget *tbl = GTK_WIDGET(data);
2030     // quit if run by the _changed callbacks
2031     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2032         return;
2033     }
2035     // in turn, prevent callbacks from responding
2036     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2038     GtkAdjustment *adj = 0;
2040     gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2041     bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2043     if (!strcmp(name, "inkscape:randomized")) {
2044         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2045         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2046     } else if (!strcmp(name, "inkscape:rounded")) {
2047         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2048         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2049     } else if (!strcmp(name, "inkscape:flatsided")) {
2050         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2051         char const *flatsides = repr->attribute("inkscape:flatsided");
2052         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2053         if ( flatsides && !strcmp(flatsides,"false") ) {
2054             ege_select_one_action_set_active( flat_action, 1 );
2055             gtk_action_set_sensitive( prop_action, TRUE );
2056         } else {
2057             ege_select_one_action_set_active( flat_action, 0 );
2058             gtk_action_set_sensitive( prop_action, FALSE );
2059         }
2060     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2061         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2062         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2063         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2064         if (r2 < r1) {
2065             gtk_adjustment_set_value(adj, r2/r1);
2066         } else {
2067             gtk_adjustment_set_value(adj, r1/r2);
2068         }
2069     } else if (!strcmp(name, "sodipodi:sides")) {
2070         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2071         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2072     }
2074     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2078 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2080     NULL, /* child_added */
2081     NULL, /* child_removed */
2082     star_tb_event_attr_changed,
2083     NULL, /* content_changed */
2084     NULL  /* order_changed */
2085 };
2088 /**
2089  *  \param selection Should not be NULL.
2090  */
2091 static void
2092 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2094     int n_selected = 0;
2095     Inkscape::XML::Node *repr = NULL;
2097     purge_repr_listener( tbl, tbl );
2099     for (GSList const *items = selection->itemList();
2100          items != NULL;
2101          items = items->next)
2102     {
2103         if (SP_IS_STAR((SPItem *) items->data)) {
2104             n_selected++;
2105             repr = SP_OBJECT_REPR((SPItem *) items->data);
2106         }
2107     }
2109     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2111     if (n_selected == 0) {
2112         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2113     } else if (n_selected == 1) {
2114         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2116         if (repr) {
2117             g_object_set_data( tbl, "repr", repr );
2118             Inkscape::GC::anchor(repr);
2119             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2120             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2121         }
2122     } else {
2123         // FIXME: implement averaging of all parameters for multiple selected stars
2124         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2125         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2126     }
2130 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2132     // FIXME: in this and all other _default functions, set some flag telling the value_changed
2133     // callbacks to lump all the changes for all selected objects in one undo step
2135     GtkAdjustment *adj = 0;
2137     // fixme: make settable in prefs!
2138     gint mag = 5;
2139     gdouble prop = 0.5;
2140     gboolean flat = FALSE;
2141     gdouble randomized = 0;
2142     gdouble rounded = 0;
2144     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2145     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2147     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2148     gtk_action_set_sensitive( sb2, !flat );
2150     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2151     gtk_adjustment_set_value(adj, mag);
2152     gtk_adjustment_value_changed(adj);
2154     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2155     gtk_adjustment_set_value(adj, prop);
2156     gtk_adjustment_value_changed(adj);
2158     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2159     gtk_adjustment_set_value(adj, rounded);
2160     gtk_adjustment_value_changed(adj);
2162     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2163     gtk_adjustment_set_value(adj, randomized);
2164     gtk_adjustment_value_changed(adj);
2168 void
2169 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2171     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2172     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2173     GtkWidget *l = gtk_label_new(NULL);
2174     gtk_label_set_markup(GTK_LABEL(l), title);
2175     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2176     if ( GTK_IS_TOOLBAR(tbl) ) {
2177         gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2178     } else {
2179         gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2180     }
2181     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2185 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2187     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2189     {
2190         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2191         ege_output_action_set_use_markup( act, TRUE );
2192         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2193         g_object_set_data( holder, "mode_action", act );
2194     }
2196     {
2197         EgeAdjustmentAction* eact = 0;
2198         gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2199         bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2201         /* Flatsided checkbox */
2202         {
2203             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2205             GtkTreeIter iter;
2206             gtk_list_store_append( model, &iter );
2207             gtk_list_store_set( model, &iter,
2208                                 0, _("Polygon"),
2209                                 1, _("Regular polygon (with one handle) instead of a star"),
2210                                 2, "star_flat",
2211                                 -1 );
2213             gtk_list_store_append( model, &iter );
2214             gtk_list_store_set( model, &iter,
2215                                 0, _("Star"),
2216                                 1, _("Star instead of a regular polygon (with one handle)"),
2217                                 2, "star_angled",
2218                                 -1 );
2220             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2221             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2222             g_object_set_data( holder, "flat_action", act );
2224             ege_select_one_action_set_appearance( act, "full" );
2225             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2226             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2227             ege_select_one_action_set_icon_column( act, 2 );
2228             ege_select_one_action_set_icon_size( act, secondarySize );
2229             ege_select_one_action_set_tooltip_column( act, 1  );
2231             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2232             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2233         }
2235         /* Magnitude */
2236         {
2237         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2238         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2239         eact = create_adjustment_action( "MagnitudeAction",
2240                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2241                                          "tools.shapes.star", "magnitude", 3,
2242                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2243                                          3, 1024, 1, 5,
2244                                          labels, values, G_N_ELEMENTS(labels),
2245                                          sp_stb_magnitude_value_changed,
2246                                          1.0, 0 );
2247         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2248         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2249         }
2251         /* Spoke ratio */
2252         {
2253         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2254         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2255         eact = create_adjustment_action( "SpokeAction",
2256                                          _("Spoke ratio"), _("Spoke ratio:"),
2257                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2258                                          // Base radius is the same for the closest handle.
2259                                          _("Base radius to tip radius ratio"),
2260                                          "tools.shapes.star", "proportion", 0.5,
2261                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2262                                          0.01, 1.0, 0.01, 0.1,
2263                                          labels, values, G_N_ELEMENTS(labels),
2264                                          sp_stb_proportion_value_changed );
2265         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2266         g_object_set_data( holder, "prop_action", eact );
2267         }
2269         if ( !isFlatSided ) {
2270             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2271         } else {
2272             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2273         }
2275         /* Roundedness */
2276         {
2277         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2278         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2279         eact = create_adjustment_action( "RoundednessAction",
2280                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2281                                          "tools.shapes.star", "rounded", 0.0,
2282                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2283                                          -10.0, 10.0, 0.01, 0.1,
2284                                          labels, values, G_N_ELEMENTS(labels),
2285                                          sp_stb_rounded_value_changed );
2286         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2287         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2288         }
2290         /* Randomization */
2291         {
2292         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2293         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2294         eact = create_adjustment_action( "RandomizationAction",
2295                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2296                                          "tools.shapes.star", "randomized", 0.0,
2297                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2298                                          -10.0, 10.0, 0.001, 0.01,
2299                                          labels, values, G_N_ELEMENTS(labels),
2300                                          sp_stb_randomized_value_changed, 0.1, 3 );
2301         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2302         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2303         }
2304     }
2306     {
2307         /* Reset */
2308         {
2309             GtkAction* act = gtk_action_new( "StarResetAction",
2310                                              _("Defaults"),
2311                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2312                                              GTK_STOCK_CLEAR );
2313             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2314             gtk_action_group_add_action( mainActions, act );
2315             gtk_action_set_sensitive( act, TRUE );
2316         }
2317     }
2319     sigc::connection *connection = new sigc::connection(
2320         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2321         );
2322     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2323     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2327 //########################
2328 //##       Rect         ##
2329 //########################
2331 static void sp_rtb_sensitivize( GObject *tbl )
2333     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2334     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2335     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2337     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2338         gtk_action_set_sensitive( not_rounded, FALSE );
2339     } else {
2340         gtk_action_set_sensitive( not_rounded, TRUE );
2341     }
2345 static void
2346 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2347                           void (*setter)(SPRect *, gdouble))
2349     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2351     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2352     SPUnit const *unit = tracker->getActiveUnit();
2354     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2355         prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2356     }
2358     // quit if run by the attr_changed listener
2359     if (g_object_get_data( tbl, "freeze" )) {
2360         return;
2361     }
2363     // in turn, prevent listener from responding
2364     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2366     bool modmade = false;
2367     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2368     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2369         if (SP_IS_RECT(items->data)) {
2370             if (adj->value != 0) {
2371                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2372             } else {
2373                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2374             }
2375             modmade = true;
2376         }
2377     }
2379     sp_rtb_sensitivize( tbl );
2381     if (modmade) {
2382         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2383                                    _("Change rectangle"));
2384     }
2386     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2389 static void
2390 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2392     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2395 static void
2396 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2398     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2401 static void
2402 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2404     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2407 static void
2408 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2410     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2415 static void
2416 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2418     GtkAdjustment *adj = 0;
2420     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2421     gtk_adjustment_set_value(adj, 0.0);
2422     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2423     gtk_adjustment_value_changed(adj);
2425     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2426     gtk_adjustment_set_value(adj, 0.0);
2427     gtk_adjustment_value_changed(adj);
2429     sp_rtb_sensitivize( obj );
2432 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2433                                        gchar const */*old_value*/, gchar const */*new_value*/,
2434                                        bool /*is_interactive*/, gpointer data)
2436     GObject *tbl = G_OBJECT(data);
2438     // quit if run by the _changed callbacks
2439     if (g_object_get_data( tbl, "freeze" )) {
2440         return;
2441     }
2443     // in turn, prevent callbacks from responding
2444     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2446     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2447     SPUnit const *unit = tracker->getActiveUnit();
2449     gpointer item = g_object_get_data( tbl, "item" );
2450     if (item && SP_IS_RECT(item)) {
2451         {
2452             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2453             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2454             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2455         }
2457         {
2458             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2459             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2460             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2461         }
2463         {
2464             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2465             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2466             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2467         }
2469         {
2470             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2471             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2472             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2473         }
2474     }
2476     sp_rtb_sensitivize( tbl );
2478     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2482 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2483     NULL, /* child_added */
2484     NULL, /* child_removed */
2485     rect_tb_event_attr_changed,
2486     NULL, /* content_changed */
2487     NULL  /* order_changed */
2488 };
2490 /**
2491  *  \param selection should not be NULL.
2492  */
2493 static void
2494 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2496     int n_selected = 0;
2497     Inkscape::XML::Node *repr = NULL;
2498     SPItem *item = NULL;
2500     if ( g_object_get_data( tbl, "repr" ) ) {
2501         g_object_set_data( tbl, "item", NULL );
2502     }
2503     purge_repr_listener( tbl, tbl );
2505     for (GSList const *items = selection->itemList();
2506          items != NULL;
2507          items = items->next) {
2508         if (SP_IS_RECT((SPItem *) items->data)) {
2509             n_selected++;
2510             item = (SPItem *) items->data;
2511             repr = SP_OBJECT_REPR(item);
2512         }
2513     }
2515     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2517     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2519     if (n_selected == 0) {
2520         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2522         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2523         gtk_action_set_sensitive(w, FALSE);
2524         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2525         gtk_action_set_sensitive(h, FALSE);
2527     } else if (n_selected == 1) {
2528         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2529         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2531         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2532         gtk_action_set_sensitive(w, TRUE);
2533         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2534         gtk_action_set_sensitive(h, TRUE);
2536         if (repr) {
2537             g_object_set_data( tbl, "repr", repr );
2538             g_object_set_data( tbl, "item", item );
2539             Inkscape::GC::anchor(repr);
2540             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2541             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2542         }
2543     } else {
2544         // FIXME: implement averaging of all parameters for multiple selected
2545         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2546         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2547         sp_rtb_sensitivize( tbl );
2548     }
2552 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2554     EgeAdjustmentAction* eact = 0;
2555     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2557     {
2558         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2559         ege_output_action_set_use_markup( act, TRUE );
2560         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2561         g_object_set_data( holder, "mode_action", act );
2562     }
2564     // rx/ry units menu: create
2565     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2566     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2567     // fixme: add % meaning per cent of the width/height
2568     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2569     g_object_set_data( holder, "tracker", tracker );
2571     /* W */
2572     {
2573         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2574         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2575         eact = create_adjustment_action( "RectWidthAction",
2576                                          _("Width"), _("W:"), _("Width of rectangle"),
2577                                          "tools.shapes.rect", "width", 0,
2578                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2579                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2580                                          labels, values, G_N_ELEMENTS(labels),
2581                                          sp_rtb_width_value_changed );
2582         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2583         g_object_set_data( holder, "width_action", eact );
2584         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2585         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2586     }
2588     /* H */
2589     {
2590         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2591         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2592         eact = create_adjustment_action( "RectHeightAction",
2593                                          _("Height"), _("H:"), _("Height of rectangle"),
2594                                          "tools.shapes.rect", "height", 0,
2595                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2596                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2597                                          labels, values, G_N_ELEMENTS(labels),
2598                                          sp_rtb_height_value_changed );
2599         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2600         g_object_set_data( holder, "height_action", eact );
2601         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2602         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2603     }
2605     /* rx */
2606     {
2607         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2608         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2609         eact = create_adjustment_action( "RadiusXAction",
2610                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2611                                          "tools.shapes.rect", "rx", 0,
2612                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2613                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2614                                          labels, values, G_N_ELEMENTS(labels),
2615                                          sp_rtb_rx_value_changed);
2616         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2617         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2618     }
2620     /* ry */
2621     {
2622         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2623         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2624         eact = create_adjustment_action( "RadiusYAction",
2625                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2626                                          "tools.shapes.rect", "ry", 0,
2627                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2628                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2629                                          labels, values, G_N_ELEMENTS(labels),
2630                                          sp_rtb_ry_value_changed);
2631         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2632         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2633     }
2635     // add the units menu
2636     {
2637         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2638         gtk_action_group_add_action( mainActions, act );
2639     }
2641     /* Reset */
2642     {
2643         InkAction* inky = ink_action_new( "RectResetAction",
2644                                           _("Not rounded"),
2645                                           _("Make corners sharp"),
2646                                           "squared_corner",
2647                                           secondarySize );
2648         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2649         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2650         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2651         g_object_set_data( holder, "not_rounded", inky );
2652     }
2654     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2655     sp_rtb_sensitivize( holder );
2657     sigc::connection *connection = new sigc::connection(
2658         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2659         );
2660     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2661     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2664 //########################
2665 //##       3D Box       ##
2666 //########################
2668 // normalize angle so that it lies in the interval [0,360]
2669 static double box3d_normalize_angle (double a) {
2670     double angle = a + ((int) (a/360.0))*360;
2671     if (angle < 0) {
2672         angle += 360.0;
2673     }
2674     return angle;
2677 static void
2678 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2679                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2680     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2681     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2682     //       are reset).
2683     bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2685     if (is_infinite) {
2686         gtk_toggle_action_set_active(tact, TRUE);
2687         gtk_action_set_sensitive(act, TRUE);
2689         double angle = persp3d_get_infinite_angle(persp, axis);
2690         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2691             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2692         }
2693     } else {
2694         gtk_toggle_action_set_active(tact, FALSE);
2695         gtk_action_set_sensitive(act, FALSE);
2696     }
2699 static void
2700 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2701     if (!persp_repr) {
2702         g_print ("No perspective given to box3d_resync_toolbar().\n");
2703         return;
2704     }
2706     GtkWidget *tbl = GTK_WIDGET(data);
2707     GtkAdjustment *adj = 0;
2708     GtkAction *act = 0;
2709     GtkToggleAction *tact = 0;
2710     Persp3D *persp = persp3d_get_from_repr(persp_repr);
2711     {
2712         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2713         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2714         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2716         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2717     }
2718     {
2719         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2720         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2721         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2723         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2724     }
2725     {
2726         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2727         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2728         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2730         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2731     }
2734 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2735                                                   gchar const */*old_value*/, gchar const */*new_value*/,
2736                                                   bool /*is_interactive*/, gpointer data)
2738     GtkWidget *tbl = GTK_WIDGET(data);
2740     // quit if run by the attr_changed listener
2741     // note: it used to work without the differently called freeze_ attributes (here and in
2742     //       box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2743     if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2744         return;
2745     }
2747     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2748     // sp_document_maybe_done() when the document is undo insensitive)
2749     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2751     // TODO: Only update the appropriate part of the toolbar
2752 //    if (!strcmp(name, "inkscape:vp_z")) {
2753         box3d_resync_toolbar(repr, G_OBJECT(tbl));
2754 //    }
2756     Persp3D *persp = persp3d_get_from_repr(repr);
2757     persp3d_update_box_reprs(persp);
2759     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2762 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2764     NULL, /* child_added */
2765     NULL, /* child_removed */
2766     box3d_persp_tb_event_attr_changed,
2767     NULL, /* content_changed */
2768     NULL  /* order_changed */
2769 };
2771 /**
2772  *  \param selection Should not be NULL.
2773  */
2774 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2775 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2776 static void
2777 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2779     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2780     // disable the angle entry fields for this direction (otherwise entering a value in them should only
2781     // update the perspectives with infinite VPs and leave the other ones untouched).
2783     Inkscape::XML::Node *persp_repr = NULL;
2784     purge_repr_listener(tbl, tbl);
2786     SPItem *item = selection->singleItem();
2787     if (item && SP_IS_BOX3D(item)) {
2788         // FIXME: Also deal with multiple selected boxes
2789         SPBox3D *box = SP_BOX3D(item);
2790         Persp3D *persp = box3d_get_perspective(box);
2791         persp_repr = SP_OBJECT_REPR(persp);
2792         if (persp_repr) {
2793             g_object_set_data(tbl, "repr", persp_repr);
2794             Inkscape::GC::anchor(persp_repr);
2795             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2796             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2797         }
2799         inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2800         prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2802         box3d_resync_toolbar(persp_repr, tbl);
2803     }
2806 static void
2807 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2809     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2810     SPDocument *document = sp_desktop_document(desktop);
2812     // quit if run by the attr_changed listener
2813     // note: it used to work without the differently called freeze_ attributes (here and in
2814     //       box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2815     if (g_object_get_data( dataKludge, "freeze_attr" )) {
2816         return;
2817     }
2819     // in turn, prevent listener from responding
2820     g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2822     //Persp3D *persp = document->current_persp3d;
2823     std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
2824     if (sel_persps.empty()) {
2825         // this can happen when the document is created; we silently ignore it
2826         return;
2827     }
2828     Persp3D *persp = sel_persps.front();
2830     persp->tmat.set_infinite_direction (axis, adj->value);
2831     SP_OBJECT(persp)->updateRepr();
2833     // TODO: use the correct axis here, too
2834     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2836     g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2840 static void
2841 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2843     box3d_angle_value_changed(adj, dataKludge, Proj::X);
2846 static void
2847 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2849     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2852 static void
2853 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2855     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2859 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2861     // TODO: Take all selected perspectives into account
2862     std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
2863     if (sel_persps.empty()) {
2864         // this can happen when the document is created; we silently ignore it
2865         return;
2866     }
2867     Persp3D *persp = sel_persps.front();
2869     bool set_infinite = gtk_toggle_action_get_active(act);
2870     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2873 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2875     box3d_vp_state_changed(act, box3d_angle, Proj::X);
2878 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2880     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2883 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2885     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2888 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2890     EgeAdjustmentAction* eact = 0;
2891     SPDocument *document = sp_desktop_document (desktop);
2892     Persp3D *persp = document->current_persp3d;
2894     EgeAdjustmentAction* box3d_angle_x = 0;
2895     EgeAdjustmentAction* box3d_angle_y = 0;
2896     EgeAdjustmentAction* box3d_angle_z = 0;
2898     /* Angle X */
2899     {
2900         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2901         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2902         eact = create_adjustment_action( "3DBoxAngleXAction",
2903                                          _("Angle in X direction"), _("Angle X:"),
2904                                          // Translators: PL is short for 'perspective line'
2905                                          _("Angle of PLs in X direction"),
2906                                          "tools.shapes.3dbox", "box3d_angle_x", 30,
2907                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2908                                          -360.0, 360.0, 1.0, 10.0,
2909                                          labels, values, G_N_ELEMENTS(labels),
2910                                          box3d_angle_x_value_changed );
2911         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2912         g_object_set_data( holder, "box3d_angle_x_action", eact );
2913         box3d_angle_x = eact;
2914     }
2916     if (!persp3d_VP_is_finite(persp, Proj::X)) {
2917         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2918     } else {
2919         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2920     }
2923     /* VP X state */
2924     {
2925         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2926                                                       // Translators: VP is short for 'vanishing point'
2927                                                       _("State of VP in X direction"),
2928                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2929                                                       "toggle_vp_x",
2930                                                       Inkscape::ICON_SIZE_DECORATION );
2931         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2932         g_object_set_data( holder, "box3d_vp_x_state_action", act );
2933         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2934         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2935         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2936     }
2938     /* Angle Y */
2939     {
2940         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2941         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2942         eact = create_adjustment_action( "3DBoxAngleYAction",
2943                                          _("Angle in Y direction"), _("Angle Y:"),
2944                                          // Translators: PL is short for 'perspective line'
2945                                          _("Angle of PLs in Y direction"),
2946                                          "tools.shapes.3dbox", "box3d_angle_y", 30,
2947                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2948                                          -360.0, 360.0, 1.0, 10.0,
2949                                          labels, values, G_N_ELEMENTS(labels),
2950                                          box3d_angle_y_value_changed );
2951         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2952         g_object_set_data( holder, "box3d_angle_y_action", eact );
2953         box3d_angle_y = eact;
2954     }
2956     if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2957         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2958     } else {
2959         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2960     }
2962     /* VP Y state */
2963     {
2964         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2965                                                       // Translators: VP is short for 'vanishing point'
2966                                                       _("State of VP in Y direction"),
2967                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2968                                                       "toggle_vp_y",
2969                                                       Inkscape::ICON_SIZE_DECORATION );
2970         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2971         g_object_set_data( holder, "box3d_vp_y_state_action", act );
2972         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2973         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2974         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2975     }
2977     /* Angle Z */
2978     {
2979         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2980         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2981         eact = create_adjustment_action( "3DBoxAngleZAction",
2982                                          _("Angle in Z direction"), _("Angle Z:"),
2983                                          // Translators: PL is short for 'perspective line'
2984                                          _("Angle of PLs in Z direction"),
2985                                          "tools.shapes.3dbox", "box3d_angle_z", 30,
2986                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2987                                          -360.0, 360.0, 1.0, 10.0,
2988                                          labels, values, G_N_ELEMENTS(labels),
2989                                          box3d_angle_z_value_changed );
2990         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2991         g_object_set_data( holder, "box3d_angle_z_action", eact );
2992         box3d_angle_z = eact;
2993     }
2995     if (!persp3d_VP_is_finite(persp, Proj::Z)) {
2996         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2997     } else {
2998         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2999     }
3001     /* VP Z state */
3002     {
3003         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3004                                                       // Translators: VP is short for 'vanishing point'
3005                                                       _("State of VP in Z direction"),
3006                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3007                                                       "toggle_vp_z",
3008                                                       Inkscape::ICON_SIZE_DECORATION );
3009         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3010         g_object_set_data( holder, "box3d_vp_z_state_action", act );
3011         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3012         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3013         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3014     }
3016     sigc::connection *connection = new sigc::connection(
3017         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3018        );
3019     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3020     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3023 //########################
3024 //##       Spiral       ##
3025 //########################
3027 static void
3028 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
3030     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3032     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3033         prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
3034     }
3036     // quit if run by the attr_changed listener
3037     if (g_object_get_data( tbl, "freeze" )) {
3038         return;
3039     }
3041     // in turn, prevent listener from responding
3042     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3044     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3046     bool modmade = false;
3047     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3048          items != NULL;
3049          items = items->next)
3050     {
3051         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3052             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3053             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3054             SP_OBJECT((SPItem *) items->data)->updateRepr();
3055             modmade = true;
3056         }
3057     }
3059     g_free(namespaced_name);
3061     if (modmade) {
3062         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3063                                    _("Change spiral"));
3064     }
3066     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3069 static void
3070 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3072     sp_spl_tb_value_changed(adj, tbl, "revolution");
3075 static void
3076 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3078     sp_spl_tb_value_changed(adj, tbl, "expansion");
3081 static void
3082 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3084     sp_spl_tb_value_changed(adj, tbl, "t0");
3087 static void
3088 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3090     GtkWidget *tbl = GTK_WIDGET(obj);
3092     GtkAdjustment *adj;
3094     // fixme: make settable
3095     gdouble rev = 5;
3096     gdouble exp = 1.0;
3097     gdouble t0 = 0.0;
3099     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3100     gtk_adjustment_set_value(adj, rev);
3101     gtk_adjustment_value_changed(adj);
3103     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3104     gtk_adjustment_set_value(adj, exp);
3105     gtk_adjustment_value_changed(adj);
3107     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3108     gtk_adjustment_set_value(adj, t0);
3109     gtk_adjustment_value_changed(adj);
3111     spinbutton_defocus(GTK_OBJECT(tbl));
3115 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3116                                          gchar const */*old_value*/, gchar const */*new_value*/,
3117                                          bool /*is_interactive*/, gpointer data)
3119     GtkWidget *tbl = GTK_WIDGET(data);
3121     // quit if run by the _changed callbacks
3122     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3123         return;
3124     }
3126     // in turn, prevent callbacks from responding
3127     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3129     GtkAdjustment *adj;
3130     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3131     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3133     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3134     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3136     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3137     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3139     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3143 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3144     NULL, /* child_added */
3145     NULL, /* child_removed */
3146     spiral_tb_event_attr_changed,
3147     NULL, /* content_changed */
3148     NULL  /* order_changed */
3149 };
3151 static void
3152 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3154     int n_selected = 0;
3155     Inkscape::XML::Node *repr = NULL;
3157     purge_repr_listener( tbl, tbl );
3159     for (GSList const *items = selection->itemList();
3160          items != NULL;
3161          items = items->next)
3162     {
3163         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3164             n_selected++;
3165             repr = SP_OBJECT_REPR((SPItem *) items->data);
3166         }
3167     }
3169     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3171     if (n_selected == 0) {
3172         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3173     } else if (n_selected == 1) {
3174         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3176         if (repr) {
3177             g_object_set_data( tbl, "repr", repr );
3178             Inkscape::GC::anchor(repr);
3179             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3180             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3181         }
3182     } else {
3183         // FIXME: implement averaging of all parameters for multiple selected
3184         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3185         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3186     }
3190 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3192     EgeAdjustmentAction* eact = 0;
3193     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3195     {
3196         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3197         ege_output_action_set_use_markup( act, TRUE );
3198         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3199         g_object_set_data( holder, "mode_action", act );
3200     }
3202     /* Revolution */
3203     {
3204         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3205         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3206         eact = create_adjustment_action( "SpiralRevolutionAction",
3207                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3208                                          "tools.shapes.spiral", "revolution", 3.0,
3209                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3210                                          0.01, 1024.0, 0.1, 1.0,
3211                                          labels, values, G_N_ELEMENTS(labels),
3212                                          sp_spl_tb_revolution_value_changed, 1, 2);
3213         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3214     }
3216     /* Expansion */
3217     {
3218         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3219         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3220         eact = create_adjustment_action( "SpiralExpansionAction",
3221                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3222                                          "tools.shapes.spiral", "expansion", 1.0,
3223                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3224                                          0.0, 1000.0, 0.01, 1.0,
3225                                          labels, values, G_N_ELEMENTS(labels),
3226                                          sp_spl_tb_expansion_value_changed);
3227         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3228     }
3230     /* T0 */
3231     {
3232         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3233         gdouble values[] = {0, 0.5, 0.9};
3234         eact = create_adjustment_action( "SpiralT0Action",
3235                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3236                                          "tools.shapes.spiral", "t0", 0.0,
3237                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3238                                          0.0, 0.999, 0.01, 1.0,
3239                                          labels, values, G_N_ELEMENTS(labels),
3240                                          sp_spl_tb_t0_value_changed);
3241         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3242     }
3244     /* Reset */
3245     {
3246         InkAction* inky = ink_action_new( "SpiralResetAction",
3247                                           _("Defaults"),
3248                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3249                                           GTK_STOCK_CLEAR,
3250                                           secondarySize );
3251         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3252         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3253     }
3256     sigc::connection *connection = new sigc::connection(
3257         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3258         );
3259     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3260     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3263 //########################
3264 //##     Pen/Pencil    ##
3265 //########################
3267 static void sp_pc_spiro_spline_mode_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
3269     prefs_set_int_attribute("tools.freehand", "spiro-spline-mode", ege_select_one_action_get_active(act));
3272 static void sp_add_spiro_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3274     // FIXME: No action is needed, we only want a simple label. But sp_toolbox_add_label() always
3275     //        adds the label at the end of the toolbar, whence this workarund. How to avoid this?
3276     {
3277         EgeOutputAction* act = ege_output_action_new(
3278             tool_is_pencil ?
3279             "FreehandModeActionPencilTemp" :
3280             "FreehandModeActionPenTemp",
3281             _("<b>Mode:</b>"), "", 0 );
3282         ege_output_action_set_use_markup( act, TRUE );
3283         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3284         g_object_set_data( holder, "freehand_mode_action", act );
3285     }
3287     /* Freehand mode toggle buttons */
3288     {
3289         //gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
3290         //bool isSpiroMode = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
3291         guint spiroMode = prefs_get_int_attribute("tools.freehand", "spiro-spline-mode", 0);
3292         Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3294         {
3295             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3297             GtkTreeIter iter;
3298             gtk_list_store_append( model, &iter );
3299             gtk_list_store_set( model, &iter,
3300                                 0, _("Bézier"),
3301                                 1, _("Regular Bézier mode"),
3302                                 2, "bezier_mode",
3303                                 -1 );
3305             gtk_list_store_append( model, &iter );
3306             gtk_list_store_set( model, &iter,
3307                                 0, _("Spiro"),
3308                                 1, _("Spiro splines mode"),
3309                                 2, "spiro_splines_mode",
3310                                 -1 );
3312             EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3313                                                                 "FreehandModeActionPencil" :
3314                                                                 "FreehandModeActionPen",
3315                                                                 (""), (""), NULL, GTK_TREE_MODEL(model) );
3316             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3317             g_object_set_data( holder, "freehande_mode_action", act );
3319             ege_select_one_action_set_appearance( act, "full" );
3320             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3321             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3322             ege_select_one_action_set_icon_column( act, 2 );
3323             ege_select_one_action_set_icon_size( act, secondarySize );
3324             ege_select_one_action_set_tooltip_column( act, 1  );
3326             ege_select_one_action_set_active( act, spiroMode);
3327             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_pc_spiro_spline_mode_changed), holder);
3328         }
3329     }
3332 static void sp_freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3333     gint shape = ege_select_one_action_get_active( act );
3334     prefs_set_int_attribute("tools.freehand", "shape", shape);
3337 /**
3338  * \brief Generate the list of freehand advanced shape option entries.
3339  */
3340 GList * freehand_shape_dropdown_items_list() {
3341     GList *glist = NULL;
3343     glist = g_list_append (glist, _("None"));
3344     glist = g_list_append (glist, _("Clipboard"));
3345     glist = g_list_append (glist, _("Decrescendo"));
3347     return glist;
3350 static void
3351 sp_freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3352     /*advanced shape options */
3353     {
3354         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3356         GList* items = 0;
3357         gint count = 0;
3358         for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3359         {
3360             GtkTreeIter iter;
3361             gtk_list_store_append( model, &iter );
3362             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3363             count++;
3364         }
3365         g_list_free( items );
3366         items = 0;
3367         EgeSelectOneAction* act1 = ege_select_one_action_new(
3368             tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3369             _("Shape:"), (""), NULL, GTK_TREE_MODEL(model));
3370         g_object_set( act1, "short_label", _("Shape:"), NULL );
3371         ege_select_one_action_set_appearance( act1, "compact" );
3372         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.freehand", "shape", 0) );
3373         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(sp_freehand_change_shape), holder );
3374         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3375         g_object_set_data( holder, "shape_action", act1 );
3376     }
3379 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3381     sp_add_spiro_toggle(mainActions, holder, false);
3382     sp_freehand_add_advanced_shape_options(mainActions, holder, false);
3386 static void
3387 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3389     GtkWidget *tbl = GTK_WIDGET(obj);
3391     GtkAdjustment *adj;
3393     // fixme: make settable
3394     gdouble tolerance = 4;
3396     adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3397     gtk_adjustment_set_value(adj, tolerance);
3398     gtk_adjustment_value_changed(adj);
3400     spinbutton_defocus(GTK_OBJECT(tbl));
3403 static void
3404 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3407     // quit if run by the attr_changed listener
3408     if (g_object_get_data( tbl, "freeze" )) {
3409         return;
3410     }
3411     // in turn, prevent listener from responding
3412     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3413     prefs_set_double_attribute("tools.freehand.pencil",
3414                                "tolerance", adj->value);
3415     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3421 static void
3422 sp_pencil_tb_tolerance_value_changed_external(Inkscape::XML::Node *repr,
3423                                               const gchar *key,
3424                                               const gchar *oldval,
3425                                               const gchar *newval,
3426                                               bool is_interactive,
3427                                               void * data)
3429     GObject* tbl = G_OBJECT(data);
3430     if (g_object_get_data( tbl, "freeze" )) {
3431         return;
3432     }
3434     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3436     GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl,
3437                                                             "tolerance");
3439     double v = prefs_get_double_attribute("tools.freehand.pencil",
3440                                             "tolerance", adj->value);
3441     gtk_adjustment_set_value(adj, v);
3442     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3446 static Inkscape::XML::NodeEventVector pencil_node_events =
3448     NULL,
3449     NULL,
3450     sp_pencil_tb_tolerance_value_changed_external,
3451     NULL,
3452     NULL,
3453 };
3456 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3458     sp_add_spiro_toggle(mainActions, holder, true);
3460     EgeAdjustmentAction* eact = 0;
3462     /* Tolerance */
3463     {
3465         eact = create_adjustment_action( "PencilToleranceAction",
3466                  _("Number of pixels allowed in interpolating"),
3467                                          _("Tolerance:"), _("Tolerance"),
3468                                          "tools.freehand.pencil", "tolerance",
3469                                          3.0,
3470                                          GTK_WIDGET(desktop->canvas), NULL,
3471                                          holder, TRUE, "altx-pencil",
3472                                          0.5, 100.0, 0.5, 0,
3473                                          NULL, NULL, 0,
3474                                          sp_pencil_tb_tolerance_value_changed,
3475                                          1, 2);
3476         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3477         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3479         Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE,
3480                                                       "tools.freehand.pencil");
3481         repr->addListener(&pencil_node_events, G_OBJECT(holder));
3482         g_object_set_data(G_OBJECT(holder), "repr", repr);
3484     }
3486     /* advanced shape options */
3487     sp_freehand_add_advanced_shape_options(mainActions, holder, true);
3489     /* Reset */
3490     {
3491         InkAction* inky = ink_action_new( "PencilResetAction",
3492                                           _("Defaults"),
3493                                           _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3494                                           GTK_STOCK_CLEAR,
3495                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3496         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
3497         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3498     }
3500     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3505 //########################
3506 //##       Tweak        ##
3507 //########################
3509 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3511     prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3514 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3516     prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3519 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3521     prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3524 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3526     int mode = ege_select_one_action_get_active( act );
3527     prefs_set_int_attribute("tools.tweak", "mode", mode);
3529     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3530     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3531     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3532     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3533     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3534     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3535     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3536         if (doh) gtk_action_set_sensitive (doh, TRUE);
3537         if (dos) gtk_action_set_sensitive (dos, TRUE);
3538         if (dol) gtk_action_set_sensitive (dol, TRUE);
3539         if (doo) gtk_action_set_sensitive (doo, TRUE);
3540         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3541         if (fid) gtk_action_set_sensitive (fid, FALSE);
3542     } else {
3543         if (doh) gtk_action_set_sensitive (doh, FALSE);
3544         if (dos) gtk_action_set_sensitive (dos, FALSE);
3545         if (dol) gtk_action_set_sensitive (dol, FALSE);
3546         if (doo) gtk_action_set_sensitive (doo, FALSE);
3547         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3548         if (fid) gtk_action_set_sensitive (fid, TRUE);
3549     }
3552 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3554     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3557 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3558     bool show = gtk_toggle_action_get_active( act );
3559     prefs_set_int_attribute ("tools.tweak", "doh",  show ? 1 : 0);
3561 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3562     bool show = gtk_toggle_action_get_active( act );
3563     prefs_set_int_attribute ("tools.tweak", "dos",  show ? 1 : 0);
3565 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3566     bool show = gtk_toggle_action_get_active( act );
3567     prefs_set_int_attribute ("tools.tweak", "dol",  show ? 1 : 0);
3569 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3570     bool show = gtk_toggle_action_get_active( act );
3571     prefs_set_int_attribute ("tools.tweak", "doo",  show ? 1 : 0);
3574 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3576     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3578     {
3579         /* Width */
3580         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3581         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3582         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3583                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3584                                                               "tools.tweak", "width", 15,
3585                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3586                                                               1, 100, 1.0, 0.0,
3587                                                               labels, values, G_N_ELEMENTS(labels),
3588                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
3589         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3590         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3591         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3592     }
3595     {
3596         /* Force */
3597         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3598         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3599         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3600                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
3601                                                               "tools.tweak", "force", 20,
3602                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3603                                                               1, 100, 1.0, 0.0,
3604                                                               labels, values, G_N_ELEMENTS(labels),
3605                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
3606         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3607         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3608         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3609     }
3611     /* Mode */
3612     {
3613         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3615         GtkTreeIter iter;
3616         gtk_list_store_append( model, &iter );
3617         gtk_list_store_set( model, &iter,
3618                             0, _("Push mode"),
3619                             1, _("Push parts of paths in any direction"),
3620                             2, "tweak_push_mode",
3621                             -1 );
3623         gtk_list_store_append( model, &iter );
3624         gtk_list_store_set( model, &iter,
3625                             0, _("Shrink mode"),
3626                             1, _("Shrink (inset) parts of paths"),
3627                             2, "tweak_shrink_mode",
3628                             -1 );
3630         gtk_list_store_append( model, &iter );
3631         gtk_list_store_set( model, &iter,
3632                             0, _("Grow mode"),
3633                             1, _("Grow (outset) parts of paths"),
3634                             2, "tweak_grow_mode",
3635                             -1 );
3637         gtk_list_store_append( model, &iter );
3638         gtk_list_store_set( model, &iter,
3639                             0, _("Attract mode"),
3640                             1, _("Attract parts of paths towards cursor"),
3641                             2, "tweak_attract_mode",
3642                             -1 );
3644         gtk_list_store_append( model, &iter );
3645         gtk_list_store_set( model, &iter,
3646                             0, _("Repel mode"),
3647                             1, _("Repel parts of paths from cursor"),
3648                             2, "tweak_repel_mode",
3649                             -1 );
3651         gtk_list_store_append( model, &iter );
3652         gtk_list_store_set( model, &iter,
3653                             0, _("Roughen mode"),
3654                             1, _("Roughen parts of paths"),
3655                             2, "tweak_roughen_mode",
3656                             -1 );
3658         gtk_list_store_append( model, &iter );
3659         gtk_list_store_set( model, &iter,
3660                             0, _("Color paint mode"),
3661                             1, _("Paint the tool's color upon selected objects"),
3662                             2, "tweak_colorpaint_mode",
3663                             -1 );
3665         gtk_list_store_append( model, &iter );
3666         gtk_list_store_set( model, &iter,
3667                             0, _("Color jitter mode"),
3668                             1, _("Jitter the colors of selected objects"),
3669                             2, "tweak_colorjitter_mode",
3670                             -1 );
3672         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3673         g_object_set( act, "short_label", _("Mode:"), NULL );
3674         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3675         g_object_set_data( holder, "mode_action", act );
3677         ege_select_one_action_set_appearance( act, "full" );
3678         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3679         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3680         ege_select_one_action_set_icon_column( act, 2 );
3681         ege_select_one_action_set_icon_size( act, secondarySize );
3682         ege_select_one_action_set_tooltip_column( act, 1  );
3684         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3685         ege_select_one_action_set_active( act, mode );
3686         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3688         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3689     }
3691     guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3693     {
3694         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3695         ege_output_action_set_use_markup( act, TRUE );
3696         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3697         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3698             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3699         g_object_set_data( holder, "tweak_channels_label", act);
3700     }
3702     {
3703         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3704                                                       _("Hue"),
3705                                                       _("In color mode, act on objects' hue"),
3706                                                       NULL,
3707                                                       Inkscape::ICON_SIZE_DECORATION );
3708         //TRANSLATORS:  "H" here stands for hue
3709         g_object_set( act, "short_label", _("H"), NULL );
3710         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3711         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3712         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3713         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3714             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3715         g_object_set_data( holder, "tweak_doh", act);
3716     }
3717     {
3718         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3719                                                       _("Saturation"),
3720                                                       _("In color mode, act on objects' saturation"),
3721                                                       NULL,
3722                                                       Inkscape::ICON_SIZE_DECORATION );
3723         //TRANSLATORS: "S" here stands for Saturation
3724         g_object_set( act, "short_label", _("S"), NULL );
3725         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3726         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3727         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3728         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3729             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3730         g_object_set_data( holder, "tweak_dos", act );
3731     }
3732     {
3733         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3734                                                       _("Lightness"),
3735                                                       _("In color mode, act on objects' lightness"),
3736                                                       NULL,
3737                                                       Inkscape::ICON_SIZE_DECORATION );
3738         //TRANSLATORS: "L" here stands for Lightness
3739         g_object_set( act, "short_label", _("L"), NULL );
3740         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3741         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3742         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3743         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3744             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3745         g_object_set_data( holder, "tweak_dol", act );
3746     }
3747     {
3748         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3749                                                       _("Opacity"),
3750                                                       _("In color mode, act on objects' opacity"),
3751                                                       NULL,
3752                                                       Inkscape::ICON_SIZE_DECORATION );
3753         //TRANSLATORS: "O" here stands for Opacity
3754         g_object_set( act, "short_label", _("O"), NULL );
3755         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3756         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3757         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3758         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3759             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3760         g_object_set_data( holder, "tweak_doo", act );
3761     }
3763     {   /* Fidelity */
3764         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3765         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3766         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3767                                                               _("Fidelity"), _("Fidelity:"),
3768                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3769                                                               "tools.tweak", "fidelity", 50,
3770                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3771                                                               1, 100, 1.0, 10.0,
3772                                                               labels, values, G_N_ELEMENTS(labels),
3773                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
3774         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3775         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3776         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3777             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3778         g_object_set_data( holder, "tweak_fidelity", eact );
3779     }
3782     /* Use Pressure button */
3783     {
3784         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3785                                                       _("Pressure"),
3786                                                       _("Use the pressure of the input device to alter the force of tweak action"),
3787                                                       "use_pressure",
3788                                                       Inkscape::ICON_SIZE_DECORATION );
3789         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3790         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3791         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3792     }
3797 //########################
3798 //##     Calligraphy    ##
3799 //########################
3800 static void update_presets_list(GObject *dataKludge ){
3801     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3802     if (sel) {
3803         ege_select_one_action_set_active(sel, 0 );
3804     }
3807 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3809     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
3810     update_presets_list(tbl);
3813 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3815     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
3816     update_presets_list(tbl);
3819 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3821     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3822     update_presets_list(tbl);
3825 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3827     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3828     update_presets_list(tbl);
3831 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3833     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
3834     update_presets_list(tbl);
3837 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3839     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3840     update_presets_list(tbl);
3843 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3845     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3846     update_presets_list(tbl);
3849 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3851     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3852     update_presets_list(tbl);
3855 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
3857     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3858     update_presets_list(tbl);
3861 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
3863     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3864     update_presets_list(tbl);
3867 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
3869     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle"));
3870     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3871     update_presets_list(tbl);
3872     if (calligraphy_angle )
3873         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3877 #define PROFILE_FLOAT_SIZE 7
3878 #define PROFILE_INT_SIZE 4
3879 struct ProfileFloatElement {
3880     char const *name;
3881     double def;
3882     double min;
3883     double max;
3884 };
3885 struct ProfileIntElement {
3886     char const *name;
3887     int def;
3888     int min;
3889     int max;
3890 };
3894 static ProfileFloatElement f_profile[PROFILE_FLOAT_SIZE] = {
3895     {"mass",0.02, 0.0, 1.0},
3896     {"wiggle",0.0, 0.0, 1.0},
3897     {"angle",30.0, -90.0, 90.0},
3898     {"thinning",0.1, -1.0, 1.0},
3899     {"tremor",0.0, 0.0, 1.0},
3900     {"flatness",0.9, 0.0, 1.0},
3901     {"cap_rounding",0.0, 0.0, 5.0}
3902 };
3903 static ProfileIntElement i_profile[PROFILE_INT_SIZE] = {
3904     {"width",15, 1, 100},
3905     {"usepressure",1,0,1},
3906     {"tracebackground",0,0,1},
3907     {"usetilt",1,0,1},
3908 };
3912 static void sp_dcc_save_profile( GtkWidget */*widget*/, GObject *dataKludge ){
3913     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3914     if (! desktop) return;
3916     Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
3917     if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) return;
3918     Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
3920     unsigned int new_index = pref_path_number_of_children("tools.calligraphic.preset") +1;
3921     gchar *profile_id = g_strdup_printf("dcc%d", new_index);
3922     gchar *pref_path = create_pref("tools.calligraphic.preset",profile_id);
3924     for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3925         ProfileFloatElement const &pe = f_profile[i];
3926         double v = prefs_get_double_attribute_limited("tools.calligraphic",pe.name, pe.def, pe.min, pe.max);
3927         prefs_set_double_attribute(pref_path,pe.name,v);
3928     }
3929     for (unsigned i = 0; i < PROFILE_INT_SIZE; ++i) {
3930         ProfileIntElement const &pe = i_profile[i];
3931         int v = prefs_get_int_attribute_limited("tools.calligraphic",pe.name, pe.def,pe.min, pe.max);
3932         prefs_set_int_attribute(pref_path,pe.name,v);
3933     }
3934     prefs_set_string_attribute(pref_path,"name",profile_name.c_str());
3936     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3937     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
3938     GtkTreeIter iter;
3939     gtk_list_store_append( model, &iter );
3940     gtk_list_store_set( model, &iter, 0, profile_name.c_str(), 1, new_index, -1 );
3942     free(profile_id);
3943     free(pref_path);
3945     ege_select_one_action_set_active(selector, new_index);
3949 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject *dataKludge) {
3951     gint preset_index = ege_select_one_action_get_active( act );
3952     gchar *profile_name = get_pref_nth_child("tools.calligraphic.preset", preset_index);
3954     if ( profile_name) {
3955         g_object_set_data(dataKludge, "profile_selector",NULL); //temporary hides the selector so no one will updadte it
3956         for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3957             ProfileFloatElement const &pe = f_profile[i];
3958             double v = prefs_get_double_attribute_limited(profile_name, pe.name, pe.def, pe.min, pe.max);
3959             GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, pe.name));
3960             if ( adj ) {
3961                 gtk_adjustment_set_value(adj, v);
3962             }
3963         }
3964         for (unsigned i = 0; i < PROFILE_INT_SIZE; ++i) {
3965             ProfileIntElement const &pe = i_profile[i];
3966             int v = prefs_get_int_attribute_limited(profile_name, pe.name, pe.def, pe.min, pe.max);
3967             GtkToggleAction* toggle = static_cast<GtkToggleAction *>(g_object_get_data(dataKludge, pe.name));
3968             if ( toggle ) {
3969                 gtk_toggle_action_set_active(toggle, v);
3970             } else printf("No toggle");
3971         }
3972         free(profile_name);
3973         g_object_set_data(dataKludge, "profile_selector",act); //restore selector visibility
3974     }
3979 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3981     {
3982         EgeAdjustmentAction* calligraphy_angle = 0;
3984         {
3985         /* Width */
3986         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3987         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3988         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3989                                                               _("Pen Width"), _("Width:"),
3990                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
3991                                                               "tools.calligraphic", "width", 15,
3992                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3993                                                               1, 100, 1.0, 0.0,
3994                                                               labels, values, G_N_ELEMENTS(labels),
3995                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
3996         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3997         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3998         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3999         }
4001         {
4002         /* Thinning */
4003             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4004             gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
4005         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4006                                                               _("Stroke Thinning"), _("Thinning:"),
4007                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4008                                                               "tools.calligraphic", "thinning", 0.1,
4009                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4010                                                               -1.0, 1.0, 0.01, 0.1,
4011                                                               labels, values, G_N_ELEMENTS(labels),
4012                                                               sp_ddc_velthin_value_changed, 0.01, 2);
4013         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4014         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4015         }
4017         {
4018         /* Angle */
4019         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4020         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4021         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4022                                                               _("Pen Angle"), _("Angle:"),
4023                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4024                                                               "tools.calligraphic", "angle", 30,
4025                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4026                                                               -90.0, 90.0, 1.0, 10.0,
4027                                                               labels, values, G_N_ELEMENTS(labels),
4028                                                               sp_ddc_angle_value_changed, 1, 0 );
4029         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4030         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4031         g_object_set_data( holder, "angle", eact );
4032         calligraphy_angle = eact;
4033         }
4035         {
4036         /* Fixation */
4037             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4038         gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
4039         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4040                                                               _("Fixation"), _("Fixation:"),
4041                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
4042                                                               "tools.calligraphic", "flatness", 0.9,
4043                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4044                                                               0.0, 1.0, 0.01, 0.1,
4045                                                               labels, values, G_N_ELEMENTS(labels),
4046                                                               sp_ddc_flatness_value_changed, 0.01, 2 );
4047         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4048         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4049         }
4051         {
4052         /* Cap Rounding */
4053             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4054         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4055         // TRANSLATORS: "cap" means "end" (both start and finish) here
4056         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4057                                                               _("Cap rounding"), _("Caps:"),
4058                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4059                                                               "tools.calligraphic", "cap_rounding", 0.0,
4060                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4061                                                               0.0, 5.0, 0.01, 0.1,
4062                                                               labels, values, G_N_ELEMENTS(labels),
4063                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4064         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4065         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4066         }
4068         {
4069         /* Tremor */
4070             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4071         gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
4072         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4073                                                               _("Stroke Tremor"), _("Tremor:"),
4074                                                               _("Increase to make strokes rugged and trembling"),
4075                                                               "tools.calligraphic", "tremor", 0.0,
4076                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4077                                                               0.0, 1.0, 0.01, 0.0,
4078                                                               labels, values, G_N_ELEMENTS(labels),
4079                                                               sp_ddc_tremor_value_changed, 0.01, 2 );
4081         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4082         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4083         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4084         }
4086         {
4087         /* Wiggle */
4088         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4089         gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
4090         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4091                                                               _("Pen Wiggle"), _("Wiggle:"),
4092                                                               _("Increase to make the pen waver and wiggle"),
4093                                                               "tools.calligraphic", "wiggle", 0.0,
4094                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4095                                                               0.0, 1.0, 0.01, 0.0,
4096                                                               labels, values, G_N_ELEMENTS(labels),
4097                                                               sp_ddc_wiggle_value_changed, 0.01, 2 );
4098         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4099         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4100         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4101         }
4103         {
4104         /* Mass */
4105             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4106         gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
4107         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4108                                                               _("Pen Mass"), _("Mass:"),
4109                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
4110                                                               "tools.calligraphic", "mass", 0.02,
4111                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4112                                                               0.0, 1.0, 0.01, 0.0,
4113                                                               labels, values, G_N_ELEMENTS(labels),
4114                                                               sp_ddc_mass_value_changed, 0.01, 2 );
4115         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4116         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4117         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4118         }
4121         /* Trace Background button */
4122         {
4123             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4124                                                           _("Trace Background"),
4125                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4126                                                           "trace_background",
4127                                                           Inkscape::ICON_SIZE_DECORATION );
4128             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4129             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4130             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
4131             g_object_set_data( holder, "tracebackground", act );
4132         }
4134         /* Use Pressure button */
4135         {
4136             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4137                                                           _("Pressure"),
4138                                                           _("Use the pressure of the input device to alter the width of the pen"),
4139                                                           "use_pressure",
4140                                                           Inkscape::ICON_SIZE_DECORATION );
4141             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4142             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4143             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
4144             g_object_set_data( holder, "usepressure", act );
4145         }
4147         /* Use Tilt button */
4148         {
4149             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4150                                                           _("Tilt"),
4151                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
4152                                                           "use_tilt",
4153                                                           Inkscape::ICON_SIZE_DECORATION );
4154             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4155             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4156             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4157             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4158             g_object_set_data( holder, "usetilt", act );
4159         }
4161         /*calligraphic profile */
4162         {
4163             GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
4164             gchar *pref_path;
4167             GtkTreeIter iter;
4168             gtk_list_store_append( model, &iter );
4169             gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4171             //TODO: switch back to prefs API
4172             Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
4173             Inkscape::XML::Node *child_repr = sp_repr_children(repr);
4174             int ii=1;
4175             while (child_repr) {
4176                 GtkTreeIter iter;
4177                 char *preset_name = (char *) child_repr->attribute("name");
4178                 gtk_list_store_append( model, &iter );
4179                 gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );
4180                 child_repr = sp_repr_next(child_repr);
4181             }
4183             pref_path = NULL;
4184             EgeSelectOneAction* act1 = ege_select_one_action_new( "SetProfileAction", "" , (_("Change calligraphic profile")), NULL, GTK_TREE_MODEL(model) );
4185             ege_select_one_action_set_appearance( act1, "compact" );
4186             g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder );
4187             gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
4188             g_object_set_data( holder, "profile_selector", act1 );
4190         }
4192         /*Save or delete calligraphic profile */
4193         {
4194             GtkAction* act = gtk_action_new( "SaveDeleteProfileAction",
4195                                              _("Defaults"),
4196                                              _("Save current settings as new profile"),
4197                                              GTK_STOCK_SAVE );
4198             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_dcc_save_profile), holder );
4201             gtk_action_group_add_action( mainActions, act );
4202             gtk_action_set_sensitive( act, TRUE );
4203             g_object_set_data( holder, "profile_save_delete", act );
4204         }
4205     }
4209 //########################
4210 //##    Circle / Arc    ##
4211 //########################
4213 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4215     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4216     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4218     if (v1 == 0 && v2 == 0) {
4219         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4220             gtk_action_set_sensitive( ocb, FALSE );
4221             gtk_action_set_sensitive( make_whole, FALSE );
4222         }
4223     } else {
4224         gtk_action_set_sensitive( ocb, TRUE );
4225         gtk_action_set_sensitive( make_whole, TRUE );
4226     }
4229 static void
4230 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4232     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4234     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4235         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
4236     }
4238     // quit if run by the attr_changed listener
4239     if (g_object_get_data( tbl, "freeze" )) {
4240         return;
4241     }
4243     // in turn, prevent listener from responding
4244     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4246     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4248     bool modmade = false;
4249     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4250          items != NULL;
4251          items = items->next)
4252     {
4253         SPItem *item = SP_ITEM(items->data);
4255         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4257             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4258             SPArc *arc = SP_ARC(item);
4260             if (!strcmp(value_name, "start"))
4261                 ge->start = (adj->value * M_PI)/ 180;
4262             else
4263                 ge->end = (adj->value * M_PI)/ 180;
4265             sp_genericellipse_normalize(ge);
4266             ((SPObject *)arc)->updateRepr();
4267             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4269             modmade = true;
4270         }
4271     }
4273     g_free(namespaced_name);
4275     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4277     sp_arctb_sensitivize( tbl, adj->value, other->value );
4279     if (modmade) {
4280         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4281                                    _("Arc: Change start/end"));
4282     }
4284     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4288 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
4290     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
4293 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4295     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
4299 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4301     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4302     gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
4303     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4304         prefs_set_int_attribute( "tools.eraser", "mode", eraserMode );
4305     }
4307     // only take action if run by the attr_changed listener
4308     if (!g_object_get_data( tbl, "freeze" )) {
4309         // in turn, prevent listener from responding
4310         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4312         if ( eraserMode != 0 ) {
4313         } else {
4314         }
4315         // TODO finish implementation
4317         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4318     }
4321 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4323     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4324     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4325         if ( ege_select_one_action_get_active( act ) != 0 ) {
4326             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
4327         } else {
4328             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
4329         }
4330     }
4332     // quit if run by the attr_changed listener
4333     if (g_object_get_data( tbl, "freeze" )) {
4334         return;
4335     }
4337     // in turn, prevent listener from responding
4338     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4340     bool modmade = false;
4342     if ( ege_select_one_action_get_active(act) != 0 ) {
4343         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4344              items != NULL;
4345              items = items->next)
4346         {
4347             if (SP_IS_ARC((SPItem *) items->data)) {
4348                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4349                 repr->setAttribute("sodipodi:open", "true");
4350                 SP_OBJECT((SPItem *) items->data)->updateRepr();
4351                 modmade = true;
4352             }
4353         }
4354     } else {
4355         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4356              items != NULL;
4357              items = items->next)
4358         {
4359             if (SP_IS_ARC((SPItem *) items->data))    {
4360                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4361                 repr->setAttribute("sodipodi:open", NULL);
4362                 SP_OBJECT((SPItem *) items->data)->updateRepr();
4363                 modmade = true;
4364             }
4365         }
4366     }
4368     if (modmade) {
4369         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4370                                    _("Arc: Change open/closed"));
4371     }
4373     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4376 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4378     GtkAdjustment *adj;
4379     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
4380     gtk_adjustment_set_value(adj, 0.0);
4381     gtk_adjustment_value_changed(adj);
4383     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
4384     gtk_adjustment_set_value(adj, 0.0);
4385     gtk_adjustment_value_changed(adj);
4387     spinbutton_defocus( GTK_OBJECT(obj) );
4390 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
4391                                       gchar const */*old_value*/, gchar const */*new_value*/,
4392                                       bool /*is_interactive*/, gpointer data)
4394     GObject *tbl = G_OBJECT(data);
4396     // quit if run by the _changed callbacks
4397     if (g_object_get_data( tbl, "freeze" )) {
4398         return;
4399     }
4401     // in turn, prevent callbacks from responding
4402     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4404     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4405     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4407     GtkAdjustment *adj1,*adj2;
4408     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4409     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4410     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4411     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4413     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4415     char const *openstr = NULL;
4416     openstr = repr->attribute("sodipodi:open");
4417     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4419     if (openstr) {
4420         ege_select_one_action_set_active( ocb, 1 );
4421     } else {
4422         ege_select_one_action_set_active( ocb, 0 );
4423     }
4425     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4428 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4429     NULL, /* child_added */
4430     NULL, /* child_removed */
4431     arc_tb_event_attr_changed,
4432     NULL, /* content_changed */
4433     NULL  /* order_changed */
4434 };
4437 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4439     int n_selected = 0;
4440     Inkscape::XML::Node *repr = NULL;
4442     purge_repr_listener( tbl, tbl );
4444     for (GSList const *items = selection->itemList();
4445          items != NULL;
4446          items = items->next)
4447     {
4448         if (SP_IS_ARC((SPItem *) items->data)) {
4449             n_selected++;
4450             repr = SP_OBJECT_REPR((SPItem *) items->data);
4451         }
4452     }
4454     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4456     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4457     if (n_selected == 0) {
4458         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4459     } else if (n_selected == 1) {
4460         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4461         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4463         if (repr) {
4464             g_object_set_data( tbl, "repr", repr );
4465             Inkscape::GC::anchor(repr);
4466             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4467             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4468         }
4469     } else {
4470         // FIXME: implement averaging of all parameters for multiple selected
4471         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4472         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4473         sp_arctb_sensitivize( tbl, 1, 0 );
4474     }
4478 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4480     EgeAdjustmentAction* eact = 0;
4481     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
4484     {
4485         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4486         ege_output_action_set_use_markup( act, TRUE );
4487         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4488         g_object_set_data( holder, "mode_action", act );
4489     }
4491     /* Start */
4492     {
4493         eact = create_adjustment_action( "ArcStartAction",
4494                                          _("Start"), _("Start:"),
4495                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
4496                                          "tools.shapes.arc", "start", 0.0,
4497                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4498                                          -360.0, 360.0, 1.0, 10.0,
4499                                          0, 0, 0,
4500                                          sp_arctb_start_value_changed);
4501         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4502     }
4504     /* End */
4505     {
4506         eact = create_adjustment_action( "ArcEndAction",
4507                                          _("End"), _("End:"),
4508                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
4509                                          "tools.shapes.arc", "end", 0.0,
4510                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4511                                          -360.0, 360.0, 1.0, 10.0,
4512                                          0, 0, 0,
4513                                          sp_arctb_end_value_changed);
4514         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4515     }
4517     /* Segments / Pie checkbox */
4518     {
4519         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4521         GtkTreeIter iter;
4522         gtk_list_store_append( model, &iter );
4523         gtk_list_store_set( model, &iter,
4524                             0, _("Closed arc"),
4525                             1, _("Switch to segment (closed shape with two radii)"),
4526                             2, "circle_closed_arc",
4527                             -1 );
4529         gtk_list_store_append( model, &iter );
4530         gtk_list_store_set( model, &iter,
4531                             0, _("Open Arc"),
4532                             1, _("Switch to arc (unclosed shape)"),
4533                             2, "circle_open_arc",
4534                             -1 );
4536         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4537         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4538         g_object_set_data( holder, "open_action", act );
4540         ege_select_one_action_set_appearance( act, "full" );
4541         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4542         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4543         ege_select_one_action_set_icon_column( act, 2 );
4544         ege_select_one_action_set_icon_size( act, secondarySize );
4545         ege_select_one_action_set_tooltip_column( act, 1  );
4547         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4548         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4549         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4550         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4551     }
4553     /* Make Whole */
4554     {
4555         InkAction* inky = ink_action_new( "ArcResetAction",
4556                                           _("Make whole"),
4557                                           _("Make the shape a whole ellipse, not arc or segment"),
4558                                           "reset_circle",
4559                                           secondarySize );
4560         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4561         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4562         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4563         g_object_set_data( holder, "make_whole", inky );
4564     }
4566     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4567     // sensitivize make whole and open checkbox
4568     {
4569         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4570         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4571         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4572     }
4575     sigc::connection *connection = new sigc::connection(
4576         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4577         );
4578     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4579     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4585 // toggle button callbacks and updaters
4587 //########################
4588 //##      Dropper       ##
4589 //########################
4591 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4592     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4593     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4594     if ( set_action ) {
4595         if ( gtk_toggle_action_get_active( act ) ) {
4596             gtk_action_set_sensitive( set_action, TRUE );
4597         } else {
4598             gtk_action_set_sensitive( set_action, FALSE );
4599         }
4600     }
4602     spinbutton_defocus(GTK_OBJECT(tbl));
4605 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4606     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4607     spinbutton_defocus(GTK_OBJECT(tbl));
4611 /**
4612  * Dropper auxiliary toolbar construction and setup.
4613  *
4614  * TODO: Would like to add swatch of current color.
4615  * TODO: Add queue of last 5 or so colors selected with new swatches so that
4616  *       can drag and drop places. Will provide a nice mixing palette.
4617  */
4618 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4620     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4622     {
4623         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4624         ege_output_action_set_use_markup( act, TRUE );
4625         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4626     }
4628     {
4629         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4630                                                       _("Pick opacity"),
4631                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4632                                                       NULL,
4633                                                       Inkscape::ICON_SIZE_DECORATION );
4634         g_object_set( act, "short_label", _("Pick"), NULL );
4635         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4636         g_object_set_data( holder, "pick_action", act );
4637         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4638         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4639     }
4641     {
4642         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4643                                                       _("Assign opacity"),
4644                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4645                                                       NULL,
4646                                                       Inkscape::ICON_SIZE_DECORATION );
4647         g_object_set( act, "short_label", _("Assign"), NULL );
4648         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4649         g_object_set_data( holder, "set_action", act );
4650         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4651         // make sure it's disabled if we're not picking alpha
4652         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4653         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4654     }
4659 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4661     {
4662         /* Width */
4663         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4664         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4665         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
4666                                                               _("Pen Width"), _("Width:"),
4667                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
4668                                                               "tools.eraser", "width", 15,
4669                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
4670                                                               1, 100, 1.0, 0.0,
4671                                                               labels, values, G_N_ELEMENTS(labels),
4672                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
4673         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4674         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4675         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4676     }
4678     {
4679         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4681         GtkTreeIter iter;
4682         gtk_list_store_append( model, &iter );
4683         gtk_list_store_set( model, &iter,
4684                             0, _("Delete"),
4685                             1, _("Delete objects touched by the eraser"),
4686                             2, "delete_object",
4687                             -1 );
4689         gtk_list_store_append( model, &iter );
4690         gtk_list_store_set( model, &iter,
4691                             0, _("Cut"),
4692                             1, _("Cut out from objects"),
4693                             2, "difference",
4694                             -1 );
4696         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4697         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4698         g_object_set_data( holder, "eraser_mode_action", act );
4700         ege_select_one_action_set_appearance( act, "full" );
4701         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4702         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4703         ege_select_one_action_set_icon_column( act, 2 );
4704         ege_select_one_action_set_tooltip_column( act, 1  );
4706         gint eraserMode = (prefs_get_int_attribute("tools.eraser", "mode", 0) != 0) ? 1 : 0;
4707         ege_select_one_action_set_active( act, eraserMode );
4708         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
4709     }
4713 //########################
4714 //##    Text Toolbox    ##
4715 //########################
4716 /*
4717 static void
4718 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
4720     //Call back for letter sizing spinbutton
4723 static void
4724 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
4726     //Call back for line height spinbutton
4729 static void
4730 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4732     //Call back for horizontal kerning spinbutton
4735 static void
4736 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4738     //Call back for vertical kerning spinbutton
4741 static void
4742 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
4744     //Call back for letter rotation spinbutton
4745 }*/
4747 namespace {
4749 bool popdown_visible = false;
4750 bool popdown_hasfocus = false;
4752 void
4753 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
4755     SPStyle *query =
4756         sp_style_new (SP_ACTIVE_DOCUMENT);
4758 //    int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4760     int result_family =
4761         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4763     int result_style =
4764         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4766     int result_numbers =
4767         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4769     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4771     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4772     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
4773     {
4774         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
4776         if (repr)
4777         {
4778             sp_style_read_from_repr (query, repr);
4779         }
4780         else
4781         {
4782             return;
4783         }
4784     }
4786     if (query->text)
4787     {
4788         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4789             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4790             gtk_entry_set_text (GTK_ENTRY (entry), "");
4792         } else if (query->text->font_specification.value || query->text->font_family.value) {
4794             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4796             // Get the font that corresponds
4797             Glib::ustring familyName;
4799             font_instance * font = font_factory::Default()->FaceFromStyle(query);
4800             if (font) {
4801                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4802                 font->Unref();
4803                 font = NULL;
4804             }
4806             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4808             Gtk::TreePath path;
4809             try {
4810                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4811             } catch (...) {
4812                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4813                 return;
4814             }
4816             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4817             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4819             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4821             gtk_tree_selection_select_path (tselection, path.gobj());
4822             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4824             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4825         }
4827         //Size
4828         GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4829         char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4830         g_object_set_data (tbl, "size-block", gpointer(1));
4831         gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4832         g_object_set_data (tbl, "size-block", gpointer(0));
4833         free (str);
4835         //Anchor
4836         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4837         {
4838             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4839             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4840             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4841             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4842         }
4843         else
4844         {
4845             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4846             {
4847                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4848                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4849                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4850                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4851             }
4852             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4853             {
4854                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4855                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4856                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4857                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4858             }
4859             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4860             {
4861                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4862                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4863                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4864                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4865             }
4866         }
4868         //Style
4869         {
4870             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4872             gboolean active = gtk_toggle_button_get_active (button);
4873             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4875             if (active != check)
4876             {
4877                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4878                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4879                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4880             }
4881         }
4883         {
4884             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4886             gboolean active = gtk_toggle_button_get_active (button);
4887             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4889             if (active != check)
4890             {
4891                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4892                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4893                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4894             }
4895         }
4897         //Orientation
4898         //locking both buttons, changing one affect all group (both)
4899         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4900         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4902         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4903         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4905         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4906         {
4907             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4908         }
4909         else
4910         {
4911             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4912         }
4913         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4914         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4915     }
4917     sp_style_unref(query);
4920 void
4921 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4923     sp_text_toolbox_selection_changed (selection, tbl);
4926 void
4927 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4929     sp_text_toolbox_selection_changed (NULL, tbl);
4932 void
4933 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
4934                                 GObject             *tbl)
4936     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
4937     GtkTreeModel *model = 0;
4938     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4939     GtkTreeIter   iter;
4940     char         *family = 0;
4942     gdk_pointer_ungrab (GDK_CURRENT_TIME);
4943     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4945     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4946         return;
4947     }
4949     gtk_tree_model_get (model, &iter, 0, &family, -1);
4951     if (g_object_get_data (G_OBJECT (selection), "block"))
4952     {
4953         gtk_entry_set_text (GTK_ENTRY (entry), family);
4954         return;
4955     }
4957     gtk_entry_set_text (GTK_ENTRY (entry), family);
4959     SPStyle *query =
4960         sp_style_new (SP_ACTIVE_DOCUMENT);
4962     int result_fontspec =
4963         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4965     //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4967     SPCSSAttr *css = sp_repr_css_attr_new ();
4970     // First try to get the font spec from the stored value
4971     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
4973     if (fontSpec.empty()) {
4974         // Construct a new font specification if it does not yet exist
4975         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4976         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4977         fontFromStyle->Unref();
4978     }
4980     if (!fontSpec.empty()) {
4981         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
4982         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
4983             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
4984             if (font) {
4985                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4987                 // Set all the these just in case they were altered when finding the best
4988                 // match for the new family and old style...
4990                 gchar c[256];
4992                 font->Family(c, 256);
4993                 sp_repr_css_set_property (css, "font-family", c);
4995                 font->Attribute( "weight", c, 256);
4996                 sp_repr_css_set_property (css, "font-weight", c);
4998                 font->Attribute("style", c, 256);
4999                 sp_repr_css_set_property (css, "font-style", c);
5001                 font->Attribute("stretch", c, 256);
5002                 sp_repr_css_set_property (css, "font-stretch", c);
5004                 font->Attribute("variant", c, 256);
5005                 sp_repr_css_set_property (css, "font-variant", c);
5007                 font->Unref();
5008             }
5009         }
5010     }
5012     // If querying returned nothing, set the default style of the tool (for new texts)
5013     if (result_fontspec == QUERY_STYLE_NOTHING)
5014     {
5015         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5016         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
5017     }
5018     else
5019     {
5020         sp_desktop_set_style (desktop, css, true, true);
5021     }
5023     sp_style_unref(query);
5025     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5026                                    _("Text: Change font family"));
5027     sp_repr_css_attr_unref (css);
5028     free (family);
5029     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5031     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5034 void
5035 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
5036                                        GObject      *tbl)
5038     const char *family = gtk_entry_get_text (entry);
5040     try {
5041         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
5042         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5043         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5044         gtk_tree_selection_select_path (selection, path.gobj());
5045         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5046         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5047     } catch (...) {
5048         if (family && strlen (family))
5049         {
5050             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5051         }
5052     }
5055 void
5056 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
5057                                    gpointer          data)
5059     if (g_object_get_data (G_OBJECT (button), "block")) return;
5060     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
5061     int prop = GPOINTER_TO_INT(data);
5063     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5064     SPCSSAttr *css = sp_repr_css_attr_new ();
5066     switch (prop)
5067     {
5068         case 0:
5069         {
5070             sp_repr_css_set_property (css, "text-anchor", "start");
5071             sp_repr_css_set_property (css, "text-align", "start");
5072             break;
5073         }
5074         case 1:
5075         {
5076             sp_repr_css_set_property (css, "text-anchor", "middle");
5077             sp_repr_css_set_property (css, "text-align", "center");
5078             break;
5079         }
5081         case 2:
5082         {
5083             sp_repr_css_set_property (css, "text-anchor", "end");
5084             sp_repr_css_set_property (css, "text-align", "end");
5085             break;
5086         }
5088         case 3:
5089         {
5090             sp_repr_css_set_property (css, "text-anchor", "start");
5091             sp_repr_css_set_property (css, "text-align", "justify");
5092             break;
5093         }
5094     }
5096     SPStyle *query =
5097         sp_style_new (SP_ACTIVE_DOCUMENT);
5098     int result_numbers =
5099         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5101     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5102     if (result_numbers == QUERY_STYLE_NOTHING)
5103     {
5104         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5105     }
5107     sp_style_unref(query);
5109     sp_desktop_set_style (desktop, css, true, true);
5110     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5111                                    _("Text: Change alignment"));
5112     sp_repr_css_attr_unref (css);
5114     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5117 void
5118 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
5119                                gpointer          data)
5121     if (g_object_get_data (G_OBJECT (button), "block")) return;
5123     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5124     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5125     int          prop       = GPOINTER_TO_INT(data);
5126     bool         active     = gtk_toggle_button_get_active (button);
5128     SPStyle *query =
5129         sp_style_new (SP_ACTIVE_DOCUMENT);
5131     int result_fontspec =
5132         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5134     //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5135     //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5136     //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5138     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
5139     Glib::ustring newFontSpec = "";
5141     if (fontSpec.empty()) {
5142         // Construct a new font specification if it does not yet exist
5143         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5144         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5145         fontFromStyle->Unref();
5146     }
5148     switch (prop)
5149     {
5150         case 0:
5151         {
5152             if (!fontSpec.empty()) {
5153                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
5154             }
5155             if (fontSpec != newFontSpec) {
5156                 // Don't even set the bold if the font didn't exist on the system
5157                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
5158             }
5159             break;
5160         }
5162         case 1:
5163         {
5164             if (!fontSpec.empty()) {
5165                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
5166             }
5167             if (fontSpec != newFontSpec) {
5168                 // Don't even set the italic if the font didn't exist on the system
5169                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
5170             }
5171             break;
5172         }
5173     }
5175     if (!newFontSpec.empty()) {
5176         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5177     }
5179     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5180     if (result_fontspec == QUERY_STYLE_NOTHING)
5181     {
5182         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5183     }
5185     sp_style_unref(query);
5187     sp_desktop_set_style (desktop, css, true, true);
5188     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5189                                    _("Text: Change font style"));
5190     sp_repr_css_attr_unref (css);
5192     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5195 void
5196 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
5197                                      gpointer         data)
5199     if (g_object_get_data (G_OBJECT (button), "block")) {
5200         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5201         return;
5202     }
5204     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5205     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5206     int          prop       = GPOINTER_TO_INT(data);
5208     switch (prop)
5209     {
5210         case 0:
5211         {
5212             sp_repr_css_set_property (css, "writing-mode", "lr");
5213             break;
5214         }
5216         case 1:
5217         {
5218             sp_repr_css_set_property (css, "writing-mode", "tb");
5219             break;
5220         }
5221     }
5223     SPStyle *query =
5224         sp_style_new (SP_ACTIVE_DOCUMENT);
5225     int result_numbers =
5226         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5228     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5229     if (result_numbers == QUERY_STYLE_NOTHING)
5230     {
5231         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5232     }
5234     sp_desktop_set_style (desktop, css, true, true);
5235     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5236                                    _("Text: Change orientation"));
5237     sp_repr_css_attr_unref (css);
5239     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5242 gboolean
5243 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5245     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5246     if (!desktop) return FALSE;
5248     switch (get_group0_keyval (event)) {
5249         case GDK_Escape: // defocus
5250             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5251             sp_text_toolbox_selection_changed (NULL, tbl); // update
5252             return TRUE; // I consumed the event
5253             break;
5254     }
5255     return FALSE;
5258 gboolean
5259 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5261     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5262     if (!desktop) return FALSE;
5264     switch (get_group0_keyval (event)) {
5265         case GDK_KP_Enter:
5266         case GDK_Return:
5267         case GDK_Escape: // defocus
5268             gtk_widget_hide (w);
5269             popdown_visible = false;
5270             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5271             return TRUE; // I consumed the event
5272             break;
5273         case GDK_w:
5274         case GDK_W:
5275             if (event->state & GDK_CONTROL_MASK) {
5276                 gtk_widget_hide (w);
5277                 popdown_visible = false;
5278                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5279                 return TRUE; // I consumed the event
5280             }
5281             break;
5282     }
5283     return FALSE;
5287 void
5288 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
5289                                GObject     *tbl)
5291     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5293     if (g_object_get_data (tbl, "size-block")) return;
5295     // If this is not from selecting a size in the list (in which case get_active will give the
5296     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5297     // process this event. This fixes GTK's stupid insistence on sending an activate change every
5298     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5299     if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5300         return;
5302     gchar *endptr;
5303     gdouble value = -1;
5304     char *text = gtk_combo_box_get_active_text (cbox);
5305     if (text) {
5306         value = g_strtod (text, &endptr);
5307         if (endptr == text) // conversion failed, non-numeric input
5308             value = -1;
5309         free (text);
5310     }
5311     if (value <= 0) {
5312         return; // could not parse value
5313     }
5315     SPCSSAttr *css = sp_repr_css_attr_new ();
5316     Inkscape::CSSOStringStream osfs;
5317     osfs << value;
5318     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5320     SPStyle *query =
5321         sp_style_new (SP_ACTIVE_DOCUMENT);
5322     int result_numbers =
5323         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5325     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5326     if (result_numbers == QUERY_STYLE_NOTHING)
5327     {
5328         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5329     }
5331     sp_style_unref(query);
5333     sp_desktop_set_style (desktop, css, true, true);
5334     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5335                                    _("Text: Change font size"));
5336     sp_repr_css_attr_unref (css);
5338     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5341 gboolean
5342 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5344     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5345     if (!desktop) return FALSE;
5347     if (!g_object_get_data (tbl, "esc-pressed")) {
5348         g_object_set_data (tbl, "enter-pressed", gpointer(1));
5349         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5350         sp_text_toolbox_size_changed (cbox, tbl);
5351         g_object_set_data (tbl, "enter-pressed", gpointer(0));
5352     }
5353     return FALSE; // I consumed the event
5357 gboolean
5358 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5360     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5361     if (!desktop) return FALSE;
5363     switch (get_group0_keyval (event)) {
5364         case GDK_Escape: // defocus
5365             g_object_set_data (tbl, "esc-pressed", gpointer(1));
5366             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5367             g_object_set_data (tbl, "esc-pressed", gpointer(0));
5368             return TRUE; // I consumed the event
5369             break;
5370         case GDK_Return: // defocus
5371         case GDK_KP_Enter:
5372             g_object_set_data (tbl, "enter-pressed", gpointer(1));
5373             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5374             sp_text_toolbox_size_changed (cbox, tbl);
5375             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5376             g_object_set_data (tbl, "enter-pressed", gpointer(0));
5377             return TRUE; // I consumed the event
5378             break;
5379     }
5380     return FALSE;
5383 void
5384 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
5385                                          GObject            *tbl)
5387     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
5388     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5389     int x, y;
5391     if (!popdown_visible)
5392     {
5393         gdk_window_get_origin (widget->window, &x, &y);
5394         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
5395         gtk_widget_show_all (popdown);
5396         //sp_transientize (popdown);
5398         gdk_pointer_grab (widget->window, TRUE,
5399                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
5400                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
5401                                         GDK_POINTER_MOTION_MASK),
5402                           NULL, NULL, GDK_CURRENT_TIME);
5404         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
5406         popdown_visible = true;
5407     }
5408     else
5409     {
5410         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5411         gdk_pointer_ungrab (GDK_CURRENT_TIME);
5412         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5413         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5414         gtk_widget_hide (popdown);
5415         popdown_visible = false;
5416     }
5419 gboolean
5420 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
5421                                  GdkEventFocus    */*event*/,
5422                                  GObject          */*tbl*/)
5424     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
5425     return FALSE;
5428 gboolean
5429 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
5430                                    GdkEventFocus    */*event*/,
5431                                    GObject          */*tbl*/)
5433     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5435     if (popdown_hasfocus) {
5436         gtk_widget_hide (popdown);
5437         popdown_hasfocus = false;
5438         popdown_visible = false;
5439         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5440         return TRUE;
5441     }
5442     return FALSE;
5445 gboolean
5446 sp_text_toolbox_popdown_focus_in (GtkWidget        */*popdown*/,
5447                                    GdkEventFocus    */*event*/,
5448                                    GObject          */*tbl*/)
5450     popdown_hasfocus = true;
5451     return TRUE;
5455 void
5456 cell_data_func  (GtkTreeViewColumn */*column*/,
5457                  GtkCellRenderer   *cell,
5458                  GtkTreeModel      *tree_model,
5459                  GtkTreeIter       *iter,
5460                  gpointer           /*data*/)
5462     char        *family,
5463         *family_escaped,
5464         *sample_escaped;
5466     static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
5468     gtk_tree_model_get (tree_model, iter, 0, &family, -1);
5470     family_escaped = g_markup_escape_text (family, -1);
5471     sample_escaped = g_markup_escape_text (sample, -1);
5473     std::stringstream markup;
5474     markup << family_escaped << "  <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
5475     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
5477     free (family);
5478     free (family_escaped);
5479     free (sample_escaped);
5482 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
5483     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
5484     if (completion) {
5485         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
5486         g_object_unref (completion);
5487     }
5490 GtkWidget*
5491 sp_text_toolbox_new (SPDesktop *desktop)
5493     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
5494     GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
5496     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
5497     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
5499     GtkTooltips *tt = gtk_tooltips_new();
5500     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
5502     ////////////Family
5503     //Window
5504     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5505     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
5507     //Entry
5508     GtkWidget           *entry = gtk_entry_new ();
5509     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
5510     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
5511     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
5512     gtk_entry_completion_set_text_column (completion, 0);
5513     gtk_entry_completion_set_minimum_key_length (completion, 1);
5514     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
5515     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
5516     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
5517     gtk_toolbar_append_widget( tbl, entry, "", "" );
5518     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
5520     //Button
5521     GtkWidget   *button = gtk_button_new ();
5522     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
5523     gtk_toolbar_append_widget( tbl, button, "", "");
5525     //Popdown
5526     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
5527     GtkWidget           *treeview = gtk_tree_view_new ();
5529     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
5530     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
5531     gtk_tree_view_column_pack_start (column, cell, FALSE);
5532     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
5533     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5534     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5536     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5537     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5538     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5540     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5542     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5543     gtk_container_add (GTK_CONTAINER (sw), treeview);
5545     gtk_container_add (GTK_CONTAINER (window), sw);
5546     gtk_widget_set_size_request (window, 300, 450);
5548     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5549     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5550     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5552     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5554     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5555     g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5556     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5558     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5559     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5561     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5562     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5563     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5564     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5565     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5567     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
5568     GtkWidget *box = gtk_event_box_new ();
5569     gtk_container_add (GTK_CONTAINER (box), image);
5570     gtk_toolbar_append_widget( tbl, box, "", "");
5571     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5572     GtkTooltips *tooltips = gtk_tooltips_new ();
5573     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5574     gtk_widget_hide (GTK_WIDGET (box));
5575     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
5577     ////////////Size
5578     const char *sizes[] = {
5579         "4", "6", "8", "9", "10", "11", "12", "13", "14",
5580         "16", "18", "20", "22", "24", "28",
5581         "32", "36", "40", "48", "56", "64", "72", "144"
5582     };
5584     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
5585     for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
5586     gtk_widget_set_size_request (cbox, 80, -1);
5587     gtk_toolbar_append_widget( tbl, cbox, "", "");
5588     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
5589     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
5590     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
5591     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
5593     ////////////Text anchor
5594     GtkWidget *group   = gtk_radio_button_new (NULL);
5595     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
5596     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
5598     // left
5599     GtkWidget *rbutton = group;
5600     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5601     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
5602     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5604     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5605     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
5606     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
5607     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
5609     // center
5610     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5611     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5612     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
5613     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5615     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5616     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
5617     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5618     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5620     // right
5621     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5622     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5623     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
5624     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5626     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5627     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
5628     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5629     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5631     // fill
5632     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5633     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5634     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
5635     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5637     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5638     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
5639     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
5640     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
5642     gtk_toolbar_append_widget( tbl, row, "", "");
5644     //spacer
5645     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5647     ////////////Text style
5648     row = gtk_hbox_new (FALSE, 4);
5650     // bold
5651     rbutton = gtk_toggle_button_new ();
5652     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5653     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
5654     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5655     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
5657     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5658     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
5659     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
5661     // italic
5662     rbutton = gtk_toggle_button_new ();
5663     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5664     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
5665     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5666     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
5668     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5669     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
5670     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
5672     gtk_toolbar_append_widget( tbl, row, "", "");
5674     //spacer
5675     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5677     ////////////Text orientation
5678     group   = gtk_radio_button_new (NULL);
5679     row     = gtk_hbox_new (FALSE, 4);
5680     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
5682     // horizontal
5683     rbutton = group;
5684     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5685     gtk_container_add           (GTK_CONTAINER (rbutton),
5686                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
5687     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5688     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
5690     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5691     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
5692     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
5694     // vertical
5695     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5696     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5697     gtk_container_add           (GTK_CONTAINER (rbutton),
5698                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
5699     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5700     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
5702     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5703     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
5704     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
5705     gtk_toolbar_append_widget( tbl, row, "", "" );
5708     //watch selection
5709     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
5711     sigc::connection *c_selection_changed =
5712         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5713                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
5714     pool->add_connection ("selection-changed", c_selection_changed);
5716     sigc::connection *c_selection_modified =
5717         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5718                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
5719     pool->add_connection ("selection-modified", c_selection_modified);
5721     sigc::connection *c_subselection_changed =
5722         new sigc::connection (desktop->connectToolSubselectionChanged
5723                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
5724     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
5726     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
5729     gtk_widget_show_all( GTK_WIDGET(tbl) );
5731     return GTK_WIDGET(tbl);
5732 } // end of sp_text_toolbox_new()
5734 }//<unnamed> namespace
5737 //#########################
5738 //##      Connector      ##
5739 //#########################
5741 static void sp_connector_path_set_avoid(void)
5743     cc_selection_set_avoid(true);
5747 static void sp_connector_path_set_ignore(void)
5749     cc_selection_set_avoid(false);
5754 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
5756     // quit if run by the _changed callbacks
5757     if (g_object_get_data( tbl, "freeze" )) {
5758         return;
5759     }
5761     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5762     SPDocument *doc = sp_desktop_document(desktop);
5764     if (!sp_document_get_undo_sensitive(doc))
5765     {
5766         return;
5767     }
5769     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5771     if ( repr->attribute("inkscape:connector-spacing") ) {
5772         gdouble priorValue = gtk_adjustment_get_value(adj);
5773         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
5774         if ( priorValue == gtk_adjustment_get_value(adj) ) {
5775             return;
5776         }
5777     } else if ( adj->value == defaultConnSpacing ) {
5778         return;
5779     }
5781     // in turn, prevent callbacks from responding
5782     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5784     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
5785     SP_OBJECT(desktop->namedview)->updateRepr();
5787     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
5788     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
5789         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
5790         NR::Matrix m = NR::identity();
5791         avoid_item_move(&m, item);
5792     }
5794     if (items) {
5795         g_slist_free(items);
5796     }
5798     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
5799             _("Change connector spacing"));
5801     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5803     spinbutton_defocus(GTK_OBJECT(tbl));
5806 static void sp_connector_graph_layout(void)
5808     if (!SP_ACTIVE_DESKTOP) return;
5810     // hack for clones, see comment in align-and-distribute.cpp
5811     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5812     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5814     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5816     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5818     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5821 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5823     if ( gtk_toggle_action_get_active( act ) ) {
5824         prefs_set_string_attribute("tools.connector", "directedlayout",
5825                 "true");
5826     } else {
5827         prefs_set_string_attribute("tools.connector", "directedlayout",
5828                 "false");
5829     }
5832 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5834     if ( gtk_toggle_action_get_active( act ) ) {
5835         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5836                 "true");
5837     } else {
5838         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5839                 "false");
5840     }
5844 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5846     prefs_set_double_attribute("tools.connector", "length", adj->value);
5847     spinbutton_defocus(GTK_OBJECT(tbl));
5850 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5851                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5852                                             bool /*is_interactive*/, gpointer data)
5854     GtkWidget *tbl = GTK_WIDGET(data);
5856     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5857         return;
5858     }
5859     if (strcmp(name, "inkscape:connector-spacing") != 0) {
5860         return;
5861     }
5863     GtkAdjustment *adj = (GtkAdjustment*)
5864             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5865     gdouble spacing = defaultConnSpacing;
5866     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5868     gtk_adjustment_set_value(adj, spacing);
5872 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5873     NULL, /* child_added */
5874     NULL, /* child_removed */
5875     connector_tb_event_attr_changed,
5876     NULL, /* content_changed */
5877     NULL  /* order_changed */
5878 };
5881 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5883     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
5885     {
5886         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5887                                           _("Avoid"),
5888                                           _("Make connectors avoid selected objects"),
5889                                           "connector_avoid",
5890                                           secondarySize );
5891         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5892         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5893     }
5895     {
5896         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5897                                           _("Ignore"),
5898                                           _("Make connectors ignore selected objects"),
5899                                           "connector_ignore",
5900                                           secondarySize );
5901         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5902         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5903     }
5905     EgeAdjustmentAction* eact = 0;
5907     // Spacing spinbox
5908     eact = create_adjustment_action( "ConnectorSpacingAction",
5909                                      _("Connector Spacing"), _("Spacing:"),
5910                                      _("The amount of space left around objects by auto-routing connectors"),
5911                                      "tools.connector", "spacing", defaultConnSpacing,
5912                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5913                                      0, 100, 1.0, 10.0,
5914                                      0, 0, 0,
5915                                      connector_spacing_changed, 1, 0 );
5916     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5918     // Graph (connector network) layout
5919     {
5920         InkAction* inky = ink_action_new( "ConnectorGraphAction",
5921                                           _("Graph"),
5922                                           _("Nicely arrange selected connector network"),
5923                                           "graph_layout",
5924                                           secondarySize );
5925         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5926         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5927     }
5929     // Default connector length spinbox
5930     eact = create_adjustment_action( "ConnectorLengthAction",
5931                                      _("Connector Length"), _("Length:"),
5932                                      _("Ideal length for connectors when layout is applied"),
5933                                      "tools.connector", "length", 100,
5934                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5935                                      10, 1000, 10.0, 100.0,
5936                                      0, 0, 0,
5937                                      connector_length_changed, 1, 0 );
5938     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5941     // Directed edges toggle button
5942     {
5943         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5944                                                       _("Downwards"),
5945                                                       _("Make connectors with end-markers (arrows) point downwards"),
5946                                                       "directed_graph",
5947                                                       Inkscape::ICON_SIZE_DECORATION );
5948         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5950         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5951         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5952                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5954         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5955     }
5957     // Avoid overlaps toggle button
5958     {
5959         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5960                                                       _("Remove overlaps"),
5961                                                       _("Do not allow overlapping shapes"),
5962                                                       "remove_overlaps",
5963                                                       Inkscape::ICON_SIZE_DECORATION );
5964         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5966         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5967         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5968                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5970         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5971     }
5973     // Code to watch for changes to the connector-spacing attribute in
5974     // the XML.
5975     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5976     g_assert(repr != NULL);
5978     purge_repr_listener( holder, holder );
5980     if (repr) {
5981         g_object_set_data( holder, "repr", repr );
5982         Inkscape::GC::anchor(repr);
5983         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5984         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5985     }
5986 } // end of sp_connector_toolbox_prep()
5989 //#########################
5990 //##     Paintbucket     ##
5991 //#########################
5993 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5995     gint channels = ege_select_one_action_get_active( act );
5996     flood_channels_set_channels( channels );
5999 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
6001     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
6004 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
6006     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
6009 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
6011     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
6012     SPUnit const *unit = tracker->getActiveUnit();
6014     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
6016     prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
6019 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
6021     // FIXME: make defaults settable via Inkscape Options
6022     struct KeyValue {
6023         char const *key;
6024         double value;
6025     } const key_values[] = {
6026         {"threshold", 15},
6027         {"offset", 0.0}
6028     };
6030     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
6031         KeyValue const &kv = key_values[i];
6032         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
6033         if ( adj ) {
6034             gtk_adjustment_set_value(adj, kv.value);
6035         }
6036     }
6038     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
6039     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
6040     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
6041     ege_select_one_action_set_active( autogap_action, 0 );
6044 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6046     EgeAdjustmentAction* eact = 0;
6048     {
6049         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6051         GList* items = 0;
6052         gint count = 0;
6053         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
6054         {
6055             GtkTreeIter iter;
6056             gtk_list_store_append( model, &iter );
6057             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6058             count++;
6059         }
6060         g_list_free( items );
6061         items = 0;
6062         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
6063         g_object_set( act1, "short_label", _("Fill by:"), NULL );
6064         ege_select_one_action_set_appearance( act1, "compact" );
6065         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
6066         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
6067         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
6068         g_object_set_data( holder, "channels_action", act1 );
6069     }
6071     // Spacing spinbox
6072     {
6073         eact = create_adjustment_action(
6074             "ThresholdAction",
6075             _("Fill Threshold"), _("Threshold:"),
6076             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
6077             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
6078             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
6079             0, 0, 0,
6080             paintbucket_threshold_changed, 1, 0 );
6082         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6083         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6084     }
6086     // Create the units menu.
6087     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
6088     const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
6089     if (stored_unit)
6090         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
6091     g_object_set_data( holder, "tracker", tracker );
6092     {
6093         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
6094         gtk_action_group_add_action( mainActions, act );
6095     }
6097     // Offset spinbox
6098     {
6099         eact = create_adjustment_action(
6100             "OffsetAction",
6101             _("Grow/shrink by"), _("Grow/shrink by:"),
6102             _("The amount to grow (positive) or shrink (negative) the created fill path"),
6103             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
6104             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
6105             0, 0, 0,
6106             paintbucket_offset_changed, 1, 2);
6107         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
6109         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6110     }
6112     /* Auto Gap */
6113     {
6114         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6116         GList* items = 0;
6117         gint count = 0;
6118         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
6119         {
6120             GtkTreeIter iter;
6121             gtk_list_store_append( model, &iter );
6122             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6123             count++;
6124         }
6125         g_list_free( items );
6126         items = 0;
6127         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
6128         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
6129         ege_select_one_action_set_appearance( act2, "compact" );
6130         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
6131         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
6132         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
6133         g_object_set_data( holder, "autogap_action", act2 );
6134     }
6136     /* Reset */
6137     {
6138         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
6139                                           _("Defaults"),
6140                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
6141                                           GTK_STOCK_CLEAR );
6142         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
6143         gtk_action_group_add_action( mainActions, act );
6144         gtk_action_set_sensitive( act, TRUE );
6145     }
6149 /*
6150   Local Variables:
6151   mode:c++
6152   c-file-style:"stroustrup"
6153   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
6154   indent-tabs-mode:nil
6155   fill-column:99
6156   End:
6157 */
6158 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :