Code

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