Code

make use of sliders
[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, 0,
3420                                          NULL, NULL, 0,
3421                                          sp_pencil_tb_tolerance_value_changed,
3422                                          1, 2);
3423         ege_adjustment_action_set_appearance( eact, "full" );
3424         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3426         Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE,
3427                                                       "tools.freehand.pencil");
3428         repr->addListener(&pencil_node_events, G_OBJECT(holder));
3429         g_object_set_data(G_OBJECT(holder), "repr", repr);
3431     }
3432     /* Reset */
3433     {
3434         InkAction* inky = ink_action_new( "PencilResetAction",
3435                                           _("Defaults"),
3436                                           _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3437                                           GTK_STOCK_CLEAR,
3438                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3439         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
3440         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3441     }
3443     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3448 //########################
3449 //##       Tweak        ##
3450 //########################
3452 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3454     prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3457 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3459     prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3462 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3464     prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3467 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3469     int mode = ege_select_one_action_get_active( act );
3470     prefs_set_int_attribute("tools.tweak", "mode", mode);
3472     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3473     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3474     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3475     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3476     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3477     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3478     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3479         if (doh) gtk_action_set_sensitive (doh, TRUE);
3480         if (dos) gtk_action_set_sensitive (dos, TRUE);
3481         if (dol) gtk_action_set_sensitive (dol, TRUE);
3482         if (doo) gtk_action_set_sensitive (doo, TRUE);
3483         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3484         if (fid) gtk_action_set_sensitive (fid, FALSE);
3485     } else {
3486         if (doh) gtk_action_set_sensitive (doh, FALSE);
3487         if (dos) gtk_action_set_sensitive (dos, FALSE);
3488         if (dol) gtk_action_set_sensitive (dol, FALSE);
3489         if (doo) gtk_action_set_sensitive (doo, FALSE);
3490         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3491         if (fid) gtk_action_set_sensitive (fid, TRUE);
3492     }
3495 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3497     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3500 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3501     bool show = gtk_toggle_action_get_active( act );
3502     prefs_set_int_attribute ("tools.tweak", "doh",  show ? 1 : 0);
3504 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3505     bool show = gtk_toggle_action_get_active( act );
3506     prefs_set_int_attribute ("tools.tweak", "dos",  show ? 1 : 0);
3508 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3509     bool show = gtk_toggle_action_get_active( act );
3510     prefs_set_int_attribute ("tools.tweak", "dol",  show ? 1 : 0);
3512 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3513     bool show = gtk_toggle_action_get_active( act );
3514     prefs_set_int_attribute ("tools.tweak", "doo",  show ? 1 : 0);
3517 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3519     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3521     {
3522         /* Width */
3523         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3524         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3525         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3526                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3527                                                               "tools.tweak", "width", 15,
3528                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3529                                                               1, 100, 1.0, 0.0,
3530                                                               labels, values, G_N_ELEMENTS(labels),
3531                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
3532         ege_adjustment_action_set_appearance( eact, "full" );
3533         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3534         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3535     }
3538     {
3539         /* Force */
3540         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3541         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3542         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3543                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
3544                                                               "tools.tweak", "force", 20,
3545                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3546                                                               1, 100, 1.0, 0.0,
3547                                                               labels, values, G_N_ELEMENTS(labels),
3548                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
3549         ege_adjustment_action_set_appearance( eact, "full" );
3550         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3551         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3552     }
3554     /* Mode */
3555     {
3556         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3558         GtkTreeIter iter;
3559         gtk_list_store_append( model, &iter );
3560         gtk_list_store_set( model, &iter,
3561                             0, _("Push mode"),
3562                             1, _("Push parts of paths in any direction"),
3563                             2, "tweak_push_mode",
3564                             -1 );
3566         gtk_list_store_append( model, &iter );
3567         gtk_list_store_set( model, &iter,
3568                             0, _("Shrink mode"),
3569                             1, _("Shrink (inset) parts of paths"),
3570                             2, "tweak_shrink_mode",
3571                             -1 );
3573         gtk_list_store_append( model, &iter );
3574         gtk_list_store_set( model, &iter,
3575                             0, _("Grow mode"),
3576                             1, _("Grow (outset) parts of paths"),
3577                             2, "tweak_grow_mode",
3578                             -1 );
3580         gtk_list_store_append( model, &iter );
3581         gtk_list_store_set( model, &iter,
3582                             0, _("Attract mode"),
3583                             1, _("Attract parts of paths towards cursor"),
3584                             2, "tweak_attract_mode",
3585                             -1 );
3587         gtk_list_store_append( model, &iter );
3588         gtk_list_store_set( model, &iter,
3589                             0, _("Repel mode"),
3590                             1, _("Repel parts of paths from cursor"),
3591                             2, "tweak_repel_mode",
3592                             -1 );
3594         gtk_list_store_append( model, &iter );
3595         gtk_list_store_set( model, &iter,
3596                             0, _("Roughen mode"),
3597                             1, _("Roughen parts of paths"),
3598                             2, "tweak_roughen_mode",
3599                             -1 );
3601         gtk_list_store_append( model, &iter );
3602         gtk_list_store_set( model, &iter,
3603                             0, _("Color paint mode"),
3604                             1, _("Paint the tool's color upon selected objects"),
3605                             2, "tweak_colorpaint_mode",
3606                             -1 );
3608         gtk_list_store_append( model, &iter );
3609         gtk_list_store_set( model, &iter,
3610                             0, _("Color jitter mode"),
3611                             1, _("Jitter the colors of selected objects"),
3612                             2, "tweak_colorjitter_mode",
3613                             -1 );
3615         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3616         g_object_set( act, "short_label", _("Mode:"), NULL );
3617         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3618         g_object_set_data( holder, "mode_action", act );
3620         ege_select_one_action_set_appearance( act, "full" );
3621         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3622         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3623         ege_select_one_action_set_icon_column( act, 2 );
3624         ege_select_one_action_set_icon_size( act, secondarySize );
3625         ege_select_one_action_set_tooltip_column( act, 1  );
3627         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3628         ege_select_one_action_set_active( act, mode );
3629         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3631         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3632     }
3634     guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3636     {
3637         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3638         ege_output_action_set_use_markup( act, TRUE );
3639         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3640         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3641             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3642         g_object_set_data( holder, "tweak_channels_label", act);
3643     }
3645     {
3646         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3647                                                       _("Hue"),
3648                                                       _("In color mode, act on objects' hue"),
3649                                                       NULL,
3650                                                       Inkscape::ICON_SIZE_DECORATION );
3651         //TRANSLATORS:  "H" here stands for hue
3652         g_object_set( act, "short_label", _("H"), NULL );
3653         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3654         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3655         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3656         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3657             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3658         g_object_set_data( holder, "tweak_doh", act);
3659     }
3660     {
3661         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3662                                                       _("Saturation"),
3663                                                       _("In color mode, act on objects' saturation"),
3664                                                       NULL,
3665                                                       Inkscape::ICON_SIZE_DECORATION );
3666         //TRANSLATORS: "S" here stands for Saturation
3667         g_object_set( act, "short_label", _("S"), NULL );
3668         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3669         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3670         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3671         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3672             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3673         g_object_set_data( holder, "tweak_dos", act );
3674     }
3675     {
3676         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3677                                                       _("Lightness"),
3678                                                       _("In color mode, act on objects' lightness"),
3679                                                       NULL,
3680                                                       Inkscape::ICON_SIZE_DECORATION );
3681         //TRANSLATORS: "L" here stands for Lightness
3682         g_object_set( act, "short_label", _("L"), NULL );
3683         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3684         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3685         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3686         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3687             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3688         g_object_set_data( holder, "tweak_dol", act );
3689     }
3690     {
3691         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3692                                                       _("Opacity"),
3693                                                       _("In color mode, act on objects' opacity"),
3694                                                       NULL,
3695                                                       Inkscape::ICON_SIZE_DECORATION );
3696         //TRANSLATORS: "O" here stands for Opacity
3697         g_object_set( act, "short_label", _("O"), NULL );
3698         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3699         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3700         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3701         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3702             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3703         g_object_set_data( holder, "tweak_doo", act );
3704     }
3706     {   /* Fidelity */
3707         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3708         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3709         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3710                                                               _("Fidelity"), _("Fidelity:"),
3711                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3712                                                               "tools.tweak", "fidelity", 50,
3713                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3714                                                               1, 100, 1.0, 10.0,
3715                                                               labels, values, G_N_ELEMENTS(labels),
3716                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
3717         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3718         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3719         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3720             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3721         g_object_set_data( holder, "tweak_fidelity", eact );
3722     }
3725     /* Use Pressure button */
3726     {
3727         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3728                                                       _("Pressure"),
3729                                                       _("Use the pressure of the input device to alter the force of tweak action"),
3730                                                       "use_pressure",
3731                                                       Inkscape::ICON_SIZE_DECORATION );
3732         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3733         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3734         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3735     }
3740 //########################
3741 //##     Calligraphy    ##
3742 //########################
3743 static void update_presets_list(GObject *dataKludge ){
3744     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3745     if (sel) {
3746         ege_select_one_action_set_active(sel, 0 );
3747     }
3750 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3752     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
3753     update_presets_list(tbl);
3756 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3758     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
3759     update_presets_list(tbl);
3762 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3764     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3765     update_presets_list(tbl);
3768 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3770     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3771     update_presets_list(tbl);
3774 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3776     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
3777     update_presets_list(tbl);
3780 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3782     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3783     update_presets_list(tbl);
3786 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3788     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3789     update_presets_list(tbl);
3792 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3794     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3795     update_presets_list(tbl);
3798 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
3800     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3801     update_presets_list(tbl);
3804 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
3806     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3807     update_presets_list(tbl);
3810 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
3812     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle"));
3813     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3814     update_presets_list(tbl);
3815     if (calligraphy_angle )
3816         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3820 #define PROFILE_FLOAT_SIZE 7
3821 #define PROFILE_INT_SIZE 4
3822 struct ProfileFloatElement {
3823     char const *name;
3824     double def;
3825     double min;
3826     double max;
3827 };
3828 struct ProfileIntElement {
3829     char const *name;
3830     int def;
3831     int min;
3832     int max;
3833 };
3837 static ProfileFloatElement f_profile[PROFILE_FLOAT_SIZE] = {
3838     {"mass",0.02, 0.0, 1.0},
3839     {"wiggle",0.0, 0.0, 1.0},
3840     {"angle",30.0, -90.0, 90.0},
3841     {"thinning",0.1, -1.0, 1.0},
3842     {"tremor",0.0, 0.0, 1.0},
3843     {"flatness",0.9, 0.0, 1.0},
3844     {"cap_rounding",0.0, 0.0, 5.0}
3845 };
3846 static ProfileIntElement i_profile[PROFILE_INT_SIZE] = {
3847     {"width",15, 1, 100},
3848     {"usepressure",1,0,1},
3849     {"tracebackground",0,0,1},
3850     {"usetilt",1,0,1},
3851 };
3855 static void sp_dcc_save_profile( GtkWidget */*widget*/, GObject *dataKludge ){
3856     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3857     if (! desktop) return;
3859     Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
3860     if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) return;
3861     Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
3863     unsigned int new_index = pref_path_number_of_children("tools.calligraphic.preset") +1;
3864     gchar *profile_id = g_strdup_printf("dcc%d", new_index);
3865     gchar *pref_path = create_pref("tools.calligraphic.preset",profile_id);
3867     for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3868         ProfileFloatElement const &pe = f_profile[i];
3869         double v = prefs_get_double_attribute_limited("tools.calligraphic",pe.name, pe.def, pe.min, pe.max);
3870         prefs_set_double_attribute(pref_path,pe.name,v);
3871     }
3872     for (unsigned i = 0; i < PROFILE_INT_SIZE; ++i) {
3873         ProfileIntElement const &pe = i_profile[i];
3874         int v = prefs_get_int_attribute_limited("tools.calligraphic",pe.name, pe.def,pe.min, pe.max);
3875         prefs_set_int_attribute(pref_path,pe.name,v);
3876     }
3877     prefs_set_string_attribute(pref_path,"name",profile_name.c_str());
3879     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3880     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
3881     GtkTreeIter iter;
3882     gtk_list_store_append( model, &iter );
3883     gtk_list_store_set( model, &iter, 0, profile_name.c_str(), 1, new_index, -1 );
3885     free(profile_id);
3886     free(pref_path);
3888     ege_select_one_action_set_active(selector, new_index);
3892 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject *dataKludge) {
3894     gint preset_index = ege_select_one_action_get_active( act );
3895     gchar *profile_name = get_pref_nth_child("tools.calligraphic.preset", preset_index);
3897     if ( profile_name) {
3898         g_object_set_data(dataKludge, "profile_selector",NULL); //temporary hides the selector so no one will updadte it
3899         for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3900             ProfileFloatElement const &pe = f_profile[i];
3901             double v = prefs_get_double_attribute_limited(profile_name, pe.name, pe.def, pe.min, pe.max);
3902             GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, pe.name));
3903             if ( adj ) {
3904                 gtk_adjustment_set_value(adj, v);
3905             }
3906         }
3907         for (unsigned i = 0; i < PROFILE_INT_SIZE; ++i) {
3908             ProfileIntElement const &pe = i_profile[i];
3909             int v = prefs_get_int_attribute_limited(profile_name, pe.name, pe.def, pe.min, pe.max);
3910             GtkToggleAction* toggle = static_cast<GtkToggleAction *>(g_object_get_data(dataKludge, pe.name));
3911             if ( toggle ) {
3912                 gtk_toggle_action_set_active(toggle, v);
3913             } else printf("No toggle");
3914         }
3915         free(profile_name);
3916         g_object_set_data(dataKludge, "profile_selector",act); //restore selector visibility
3917     }
3922 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3924     {
3925         EgeAdjustmentAction* calligraphy_angle = 0;
3927         {
3928         /* Width */
3929         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3930         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3931         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3932                                                               _("Pen Width"), _("Width:"),
3933                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
3934                                                               "tools.calligraphic", "width", 15,
3935                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3936                                                               1, 100, 1.0, 0.0,
3937                                                               labels, values, G_N_ELEMENTS(labels),
3938                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
3939         ege_adjustment_action_set_appearance( eact, "full" );
3940         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3941         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3942         }
3944         {
3945         /* Thinning */
3946             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3947             gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3948         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3949                                                               _("Stroke Thinning"), _("Thinning:"),
3950                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3951                                                               "tools.calligraphic", "thinning", 0.1,
3952                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3953                                                               -1.0, 1.0, 0.01, 0.1,
3954                                                               labels, values, G_N_ELEMENTS(labels),
3955                                                               sp_ddc_velthin_value_changed, 0.01, 2);
3956         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3957         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3958         }
3960         {
3961         /* Angle */
3962         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3963         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3964         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3965                                                               _("Pen Angle"), _("Angle:"),
3966                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3967                                                               "tools.calligraphic", "angle", 30,
3968                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3969                                                               -90.0, 90.0, 1.0, 10.0,
3970                                                               labels, values, G_N_ELEMENTS(labels),
3971                                                               sp_ddc_angle_value_changed, 1, 0 );
3972         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3973         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3974         g_object_set_data( holder, "angle", eact );
3975         calligraphy_angle = eact;
3976         }
3978         {
3979         /* Fixation */
3980             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3981         gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3982         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3983                                                               _("Fixation"), _("Fixation:"),
3984                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3985                                                               "tools.calligraphic", "flatness", 0.9,
3986                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3987                                                               0.0, 1.0, 0.01, 0.1,
3988                                                               labels, values, G_N_ELEMENTS(labels),
3989                                                               sp_ddc_flatness_value_changed, 0.01, 2 );
3990         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3991         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3992         }
3994         {
3995         /* Cap Rounding */
3996             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3997         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3998         // TRANSLATORS: "cap" means "end" (both start and finish) here
3999         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4000                                                               _("Cap rounding"), _("Caps:"),
4001                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4002                                                               "tools.calligraphic", "cap_rounding", 0.0,
4003                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4004                                                               0.0, 5.0, 0.01, 0.1,
4005                                                               labels, values, G_N_ELEMENTS(labels),
4006                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4007         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4008         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4009         }
4011         {
4012         /* Tremor */
4013             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4014         gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
4015         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4016                                                               _("Stroke Tremor"), _("Tremor:"),
4017                                                               _("Increase to make strokes rugged and trembling"),
4018                                                               "tools.calligraphic", "tremor", 0.0,
4019                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4020                                                               0.0, 1.0, 0.01, 0.0,
4021                                                               labels, values, G_N_ELEMENTS(labels),
4022                                                               sp_ddc_tremor_value_changed, 0.01, 2 );
4024         ege_adjustment_action_set_appearance( eact, "full" );
4025         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4026         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4027         }
4029         {
4030         /* Wiggle */
4031         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4032         gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
4033         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4034                                                               _("Pen Wiggle"), _("Wiggle:"),
4035                                                               _("Increase to make the pen waver and wiggle"),
4036                                                               "tools.calligraphic", "wiggle", 0.0,
4037                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4038                                                               0.0, 1.0, 0.01, 0.0,
4039                                                               labels, values, G_N_ELEMENTS(labels),
4040                                                               sp_ddc_wiggle_value_changed, 0.01, 2 );
4041         ege_adjustment_action_set_appearance( eact, "full" );
4042         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4043         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4044         }
4046         {
4047         /* Mass */
4048             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4049         gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
4050         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4051                                                               _("Pen Mass"), _("Mass:"),
4052                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
4053                                                               "tools.calligraphic", "mass", 0.02,
4054                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4055                                                               0.0, 1.0, 0.01, 0.0,
4056                                                               labels, values, G_N_ELEMENTS(labels),
4057                                                               sp_ddc_mass_value_changed, 0.01, 2 );
4058         ege_adjustment_action_set_appearance( eact, "full" );
4059         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4060         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4061         }
4064         /* Trace Background button */
4065         {
4066             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4067                                                           _("Trace Background"),
4068                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4069                                                           "trace_background",
4070                                                           Inkscape::ICON_SIZE_DECORATION );
4071             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4072             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4073             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
4074             g_object_set_data( holder, "tracebackground", act );
4075         }
4077         /* Use Pressure button */
4078         {
4079             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4080                                                           _("Pressure"),
4081                                                           _("Use the pressure of the input device to alter the width of the pen"),
4082                                                           "use_pressure",
4083                                                           Inkscape::ICON_SIZE_DECORATION );
4084             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4085             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4086             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
4087             g_object_set_data( holder, "usepressure", act );
4088         }
4090         /* Use Tilt button */
4091         {
4092             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4093                                                           _("Tilt"),
4094                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
4095                                                           "use_tilt",
4096                                                           Inkscape::ICON_SIZE_DECORATION );
4097             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4098             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4099             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4100             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4101             g_object_set_data( holder, "usetilt", act );
4102         }
4104         /*calligraphic profile */
4105         {
4106             GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
4107             gchar *pref_path;
4110             GtkTreeIter iter;
4111             gtk_list_store_append( model, &iter );
4112             gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4114             //TODO: switch back to prefs API
4115             Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
4116             Inkscape::XML::Node *child_repr = sp_repr_children(repr);
4117             int ii=1;
4118             while (child_repr) {
4119                 GtkTreeIter iter;
4120                 char *preset_name = (char *) child_repr->attribute("name");
4121                 gtk_list_store_append( model, &iter );
4122                 gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );
4123                 child_repr = sp_repr_next(child_repr);
4124             }
4126             pref_path = NULL;
4127             EgeSelectOneAction* act1 = ege_select_one_action_new( "SetProfileAction", "" , (_("Change calligraphic profile")), NULL, GTK_TREE_MODEL(model) );
4128             ege_select_one_action_set_appearance( act1, "compact" );
4129             g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder );
4130             gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
4131             g_object_set_data( holder, "profile_selector", act1 );
4133         }
4135         /*Save or delete calligraphic profile */
4136         {
4137             GtkAction* act = gtk_action_new( "SaveDeleteProfileAction",
4138                                              _("Defaults"),
4139                                              _("Save current settings as new profile"),
4140                                              GTK_STOCK_SAVE );
4141             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_dcc_save_profile), holder );
4144             gtk_action_group_add_action( mainActions, act );
4145             gtk_action_set_sensitive( act, TRUE );
4146             g_object_set_data( holder, "profile_save_delete", act );
4147         }
4148     }
4152 //########################
4153 //##    Circle / Arc    ##
4154 //########################
4156 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4158     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4159     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4161     if (v1 == 0 && v2 == 0) {
4162         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4163             gtk_action_set_sensitive( ocb, FALSE );
4164             gtk_action_set_sensitive( make_whole, FALSE );
4165         }
4166     } else {
4167         gtk_action_set_sensitive( ocb, TRUE );
4168         gtk_action_set_sensitive( make_whole, TRUE );
4169     }
4172 static void
4173 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4175     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4177     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4178         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
4179     }
4181     // quit if run by the attr_changed listener
4182     if (g_object_get_data( tbl, "freeze" )) {
4183         return;
4184     }
4186     // in turn, prevent listener from responding
4187     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4189     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4191     bool modmade = false;
4192     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4193          items != NULL;
4194          items = items->next)
4195     {
4196         SPItem *item = SP_ITEM(items->data);
4198         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4200             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4201             SPArc *arc = SP_ARC(item);
4203             if (!strcmp(value_name, "start"))
4204                 ge->start = (adj->value * M_PI)/ 180;
4205             else
4206                 ge->end = (adj->value * M_PI)/ 180;
4208             sp_genericellipse_normalize(ge);
4209             ((SPObject *)arc)->updateRepr();
4210             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4212             modmade = true;
4213         }
4214     }
4216     g_free(namespaced_name);
4218     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4220     sp_arctb_sensitivize( tbl, adj->value, other->value );
4222     if (modmade) {
4223         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4224                                    _("Arc: Change start/end"));
4225     }
4227     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4231 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
4233     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
4236 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4238     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
4242 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4244     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4245     gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
4246     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4247         prefs_set_int_attribute( "tools.eraser", "mode", eraserMode );
4248     }
4250     // only take action if run by the attr_changed listener
4251     if (!g_object_get_data( tbl, "freeze" )) {
4252         // in turn, prevent listener from responding
4253         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4255         if ( eraserMode != 0 ) {
4256         } else {
4257         }
4258         // TODO finish implementation
4260         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4261     }
4264 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4266     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4267     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4268         if ( ege_select_one_action_get_active( act ) != 0 ) {
4269             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
4270         } else {
4271             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
4272         }
4273     }
4275     // quit if run by the attr_changed listener
4276     if (g_object_get_data( tbl, "freeze" )) {
4277         return;
4278     }
4280     // in turn, prevent listener from responding
4281     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4283     bool modmade = false;
4285     if ( ege_select_one_action_get_active(act) != 0 ) {
4286         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4287              items != NULL;
4288              items = items->next)
4289         {
4290             if (SP_IS_ARC((SPItem *) items->data)) {
4291                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4292                 repr->setAttribute("sodipodi:open", "true");
4293                 SP_OBJECT((SPItem *) items->data)->updateRepr();
4294                 modmade = true;
4295             }
4296         }
4297     } else {
4298         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4299              items != NULL;
4300              items = items->next)
4301         {
4302             if (SP_IS_ARC((SPItem *) items->data))    {
4303                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4304                 repr->setAttribute("sodipodi:open", NULL);
4305                 SP_OBJECT((SPItem *) items->data)->updateRepr();
4306                 modmade = true;
4307             }
4308         }
4309     }
4311     if (modmade) {
4312         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4313                                    _("Arc: Change open/closed"));
4314     }
4316     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4319 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4321     GtkAdjustment *adj;
4322     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
4323     gtk_adjustment_set_value(adj, 0.0);
4324     gtk_adjustment_value_changed(adj);
4326     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
4327     gtk_adjustment_set_value(adj, 0.0);
4328     gtk_adjustment_value_changed(adj);
4330     spinbutton_defocus( GTK_OBJECT(obj) );
4333 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
4334                                       gchar const */*old_value*/, gchar const */*new_value*/,
4335                                       bool /*is_interactive*/, gpointer data)
4337     GObject *tbl = G_OBJECT(data);
4339     // quit if run by the _changed callbacks
4340     if (g_object_get_data( tbl, "freeze" )) {
4341         return;
4342     }
4344     // in turn, prevent callbacks from responding
4345     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4347     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4348     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4350     GtkAdjustment *adj1,*adj2;
4351     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4352     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4353     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4354     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4356     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4358     char const *openstr = NULL;
4359     openstr = repr->attribute("sodipodi:open");
4360     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4362     if (openstr) {
4363         ege_select_one_action_set_active( ocb, 1 );
4364     } else {
4365         ege_select_one_action_set_active( ocb, 0 );
4366     }
4368     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4371 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4372     NULL, /* child_added */
4373     NULL, /* child_removed */
4374     arc_tb_event_attr_changed,
4375     NULL, /* content_changed */
4376     NULL  /* order_changed */
4377 };
4380 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4382     int n_selected = 0;
4383     Inkscape::XML::Node *repr = NULL;
4385     purge_repr_listener( tbl, tbl );
4387     for (GSList const *items = selection->itemList();
4388          items != NULL;
4389          items = items->next)
4390     {
4391         if (SP_IS_ARC((SPItem *) items->data)) {
4392             n_selected++;
4393             repr = SP_OBJECT_REPR((SPItem *) items->data);
4394         }
4395     }
4397     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4399     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4400     if (n_selected == 0) {
4401         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4402     } else if (n_selected == 1) {
4403         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4404         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4406         if (repr) {
4407             g_object_set_data( tbl, "repr", repr );
4408             Inkscape::GC::anchor(repr);
4409             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4410             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4411         }
4412     } else {
4413         // FIXME: implement averaging of all parameters for multiple selected
4414         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4415         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4416         sp_arctb_sensitivize( tbl, 1, 0 );
4417     }
4421 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4423     EgeAdjustmentAction* eact = 0;
4424     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
4427     {
4428         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4429         ege_output_action_set_use_markup( act, TRUE );
4430         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4431         g_object_set_data( holder, "mode_action", act );
4432     }
4434     /* Start */
4435     {
4436         eact = create_adjustment_action( "ArcStartAction",
4437                                          _("Start"), _("Start:"),
4438                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
4439                                          "tools.shapes.arc", "start", 0.0,
4440                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4441                                          -360.0, 360.0, 1.0, 10.0,
4442                                          0, 0, 0,
4443                                          sp_arctb_start_value_changed);
4444         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4445     }
4447     /* End */
4448     {
4449         eact = create_adjustment_action( "ArcEndAction",
4450                                          _("End"), _("End:"),
4451                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
4452                                          "tools.shapes.arc", "end", 0.0,
4453                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4454                                          -360.0, 360.0, 1.0, 10.0,
4455                                          0, 0, 0,
4456                                          sp_arctb_end_value_changed);
4457         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4458     }
4460     /* Segments / Pie checkbox */
4461     {
4462         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4464         GtkTreeIter iter;
4465         gtk_list_store_append( model, &iter );
4466         gtk_list_store_set( model, &iter,
4467                             0, _("Closed arc"),
4468                             1, _("Switch to segment (closed shape with two radii)"),
4469                             2, "circle_closed_arc",
4470                             -1 );
4472         gtk_list_store_append( model, &iter );
4473         gtk_list_store_set( model, &iter,
4474                             0, _("Open Arc"),
4475                             1, _("Switch to arc (unclosed shape)"),
4476                             2, "circle_open_arc",
4477                             -1 );
4479         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4480         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4481         g_object_set_data( holder, "open_action", act );
4483         ege_select_one_action_set_appearance( act, "full" );
4484         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4485         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4486         ege_select_one_action_set_icon_column( act, 2 );
4487         ege_select_one_action_set_icon_size( act, secondarySize );
4488         ege_select_one_action_set_tooltip_column( act, 1  );
4490         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4491         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4492         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4493         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4494     }
4496     /* Make Whole */
4497     {
4498         InkAction* inky = ink_action_new( "ArcResetAction",
4499                                           _("Make whole"),
4500                                           _("Make the shape a whole ellipse, not arc or segment"),
4501                                           "reset_circle",
4502                                           secondarySize );
4503         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4504         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4505         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4506         g_object_set_data( holder, "make_whole", inky );
4507     }
4509     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4510     // sensitivize make whole and open checkbox
4511     {
4512         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4513         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4514         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4515     }
4518     sigc::connection *connection = new sigc::connection(
4519         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4520         );
4521     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4522     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4528 // toggle button callbacks and updaters
4530 //########################
4531 //##      Dropper       ##
4532 //########################
4534 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4535     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4536     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4537     if ( set_action ) {
4538         if ( gtk_toggle_action_get_active( act ) ) {
4539             gtk_action_set_sensitive( set_action, TRUE );
4540         } else {
4541             gtk_action_set_sensitive( set_action, FALSE );
4542         }
4543     }
4545     spinbutton_defocus(GTK_OBJECT(tbl));
4548 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4549     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4550     spinbutton_defocus(GTK_OBJECT(tbl));
4554 /**
4555  * Dropper auxiliary toolbar construction and setup.
4556  *
4557  * TODO: Would like to add swatch of current color.
4558  * TODO: Add queue of last 5 or so colors selected with new swatches so that
4559  *       can drag and drop places. Will provide a nice mixing palette.
4560  */
4561 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4563     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4565     {
4566         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4567         ege_output_action_set_use_markup( act, TRUE );
4568         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4569     }
4571     {
4572         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4573                                                       _("Pick opacity"),
4574                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4575                                                       NULL,
4576                                                       Inkscape::ICON_SIZE_DECORATION );
4577         g_object_set( act, "short_label", _("Pick"), NULL );
4578         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4579         g_object_set_data( holder, "pick_action", act );
4580         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4581         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4582     }
4584     {
4585         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4586                                                       _("Assign opacity"),
4587                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4588                                                       NULL,
4589                                                       Inkscape::ICON_SIZE_DECORATION );
4590         g_object_set( act, "short_label", _("Assign"), NULL );
4591         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4592         g_object_set_data( holder, "set_action", act );
4593         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4594         // make sure it's disabled if we're not picking alpha
4595         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4596         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4597     }
4602 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4604     {
4605         /* Width */
4606         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4607         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4608         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
4609                                                               _("Pen Width"), _("Width:"),
4610                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
4611                                                               "tools.eraser", "width", 15,
4612                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
4613                                                               1, 100, 1.0, 0.0,
4614                                                               labels, values, G_N_ELEMENTS(labels),
4615                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
4616         ege_adjustment_action_set_appearance( eact, "full" );
4617         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4618         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4619     }
4621     {
4622         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4624         GtkTreeIter iter;
4625         gtk_list_store_append( model, &iter );
4626         gtk_list_store_set( model, &iter,
4627                             0, _("Delete"),
4628                             1, _("Delete objects touched by the eraser"),
4629                             2, "delete_object",
4630                             -1 );
4632         gtk_list_store_append( model, &iter );
4633         gtk_list_store_set( model, &iter,
4634                             0, _("Cut"),
4635                             1, _("Cut out from objects"),
4636                             2, "difference",
4637                             -1 );
4639         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4640         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4641         g_object_set_data( holder, "eraser_mode_action", act );
4643         ege_select_one_action_set_appearance( act, "full" );
4644         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4645         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4646         ege_select_one_action_set_icon_column( act, 2 );
4647         ege_select_one_action_set_tooltip_column( act, 1  );
4649         gint eraserMode = (prefs_get_int_attribute("tools.eraser", "mode", 0) != 0) ? 1 : 0;
4650         ege_select_one_action_set_active( act, eraserMode );
4651         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
4652     }
4656 //########################
4657 //##    Text Toolbox    ##
4658 //########################
4659 /*
4660 static void
4661 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
4663     //Call back for letter sizing spinbutton
4666 static void
4667 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
4669     //Call back for line height spinbutton
4672 static void
4673 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4675     //Call back for horizontal kerning spinbutton
4678 static void
4679 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4681     //Call back for vertical kerning spinbutton
4684 static void
4685 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
4687     //Call back for letter rotation spinbutton
4688 }*/
4690 namespace {
4692 bool popdown_visible = false;
4693 bool popdown_hasfocus = false;
4695 void
4696 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
4698     SPStyle *query =
4699         sp_style_new (SP_ACTIVE_DOCUMENT);
4701 //    int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4703     int result_family =
4704         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4706     int result_style =
4707         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4709     int result_numbers =
4710         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4712     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4714     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4715     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
4716     {
4717         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
4719         if (repr)
4720         {
4721             sp_style_read_from_repr (query, repr);
4722         }
4723         else
4724         {
4725             return;
4726         }
4727     }
4729     if (query->text)
4730     {
4731         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4732             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4733             gtk_entry_set_text (GTK_ENTRY (entry), "");
4735         } else if (query->text->font_specification.value || query->text->font_family.value) {
4737             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4739             // Get the font that corresponds
4740             Glib::ustring familyName;
4742             font_instance * font = font_factory::Default()->FaceFromStyle(query);
4743             if (font) {
4744                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4745                 font->Unref();
4746                 font = NULL;
4747             }
4749             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4751             Gtk::TreePath path;
4752             try {
4753                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4754             } catch (...) {
4755                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4756                 return;
4757             }
4759             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4760             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4762             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4764             gtk_tree_selection_select_path (tselection, path.gobj());
4765             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4767             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4768         }
4770         //Size
4771         GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4772         char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4773         g_object_set_data (tbl, "size-block", gpointer(1));
4774         gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4775         g_object_set_data (tbl, "size-block", gpointer(0));
4776         free (str);
4778         //Anchor
4779         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4780         {
4781             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4782             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4783             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4784             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4785         }
4786         else
4787         {
4788             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4789             {
4790                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4791                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4792                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4793                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4794             }
4795             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4796             {
4797                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4798                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4799                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4800                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4801             }
4802             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4803             {
4804                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4805                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4806                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4807                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4808             }
4809         }
4811         //Style
4812         {
4813             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4815             gboolean active = gtk_toggle_button_get_active (button);
4816             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4818             if (active != check)
4819             {
4820                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4821                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4822                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4823             }
4824         }
4826         {
4827             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4829             gboolean active = gtk_toggle_button_get_active (button);
4830             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4832             if (active != check)
4833             {
4834                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4835                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4836                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4837             }
4838         }
4840         //Orientation
4841         //locking both buttons, changing one affect all group (both)
4842         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4843         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4845         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4846         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4848         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4849         {
4850             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4851         }
4852         else
4853         {
4854             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4855         }
4856         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4857         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4858     }
4860     sp_style_unref(query);
4863 void
4864 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4866     sp_text_toolbox_selection_changed (selection, tbl);
4869 void
4870 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4872     sp_text_toolbox_selection_changed (NULL, tbl);
4875 void
4876 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
4877                                 GObject             *tbl)
4879     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
4880     GtkTreeModel *model = 0;
4881     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4882     GtkTreeIter   iter;
4883     char         *family = 0;
4885     gdk_pointer_ungrab (GDK_CURRENT_TIME);
4886     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4888     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4889         return;
4890     }
4892     gtk_tree_model_get (model, &iter, 0, &family, -1);
4894     if (g_object_get_data (G_OBJECT (selection), "block"))
4895     {
4896         gtk_entry_set_text (GTK_ENTRY (entry), family);
4897         return;
4898     }
4900     gtk_entry_set_text (GTK_ENTRY (entry), family);
4902     SPStyle *query =
4903         sp_style_new (SP_ACTIVE_DOCUMENT);
4905     int result_fontspec =
4906         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4908     //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4910     SPCSSAttr *css = sp_repr_css_attr_new ();
4913     // First try to get the font spec from the stored value
4914     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
4916     if (fontSpec.empty()) {
4917         // Construct a new font specification if it does not yet exist
4918         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4919         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4920         fontFromStyle->Unref();
4921     }
4923     if (!fontSpec.empty()) {
4924         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
4925         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
4926             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
4927             if (font) {
4928                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4930                 // Set all the these just in case they were altered when finding the best
4931                 // match for the new family and old style...
4933                 gchar c[256];
4935                 font->Family(c, 256);
4936                 sp_repr_css_set_property (css, "font-family", c);
4938                 font->Attribute( "weight", c, 256);
4939                 sp_repr_css_set_property (css, "font-weight", c);
4941                 font->Attribute("style", c, 256);
4942                 sp_repr_css_set_property (css, "font-style", c);
4944                 font->Attribute("stretch", c, 256);
4945                 sp_repr_css_set_property (css, "font-stretch", c);
4947                 font->Attribute("variant", c, 256);
4948                 sp_repr_css_set_property (css, "font-variant", c);
4950                 font->Unref();
4951             }
4952         }
4953     }
4955     // If querying returned nothing, set the default style of the tool (for new texts)
4956     if (result_fontspec == QUERY_STYLE_NOTHING)
4957     {
4958         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4959         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
4960     }
4961     else
4962     {
4963         sp_desktop_set_style (desktop, css, true, true);
4964     }
4966     sp_style_unref(query);
4968     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4969                                    _("Text: Change font family"));
4970     sp_repr_css_attr_unref (css);
4971     free (family);
4972     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4974     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4977 void
4978 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
4979                                        GObject      *tbl)
4981     const char *family = gtk_entry_get_text (entry);
4983     try {
4984         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
4985         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4986         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4987         gtk_tree_selection_select_path (selection, path.gobj());
4988         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4989         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4990     } catch (...) {
4991         if (family && strlen (family))
4992         {
4993             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4994         }
4995     }
4998 void
4999 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
5000                                    gpointer          data)
5002     if (g_object_get_data (G_OBJECT (button), "block")) return;
5003     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
5004     int prop = GPOINTER_TO_INT(data);
5006     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5007     SPCSSAttr *css = sp_repr_css_attr_new ();
5009     switch (prop)
5010     {
5011         case 0:
5012         {
5013             sp_repr_css_set_property (css, "text-anchor", "start");
5014             sp_repr_css_set_property (css, "text-align", "start");
5015             break;
5016         }
5017         case 1:
5018         {
5019             sp_repr_css_set_property (css, "text-anchor", "middle");
5020             sp_repr_css_set_property (css, "text-align", "center");
5021             break;
5022         }
5024         case 2:
5025         {
5026             sp_repr_css_set_property (css, "text-anchor", "end");
5027             sp_repr_css_set_property (css, "text-align", "end");
5028             break;
5029         }
5031         case 3:
5032         {
5033             sp_repr_css_set_property (css, "text-anchor", "start");
5034             sp_repr_css_set_property (css, "text-align", "justify");
5035             break;
5036         }
5037     }
5039     SPStyle *query =
5040         sp_style_new (SP_ACTIVE_DOCUMENT);
5041     int result_numbers =
5042         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5044     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5045     if (result_numbers == QUERY_STYLE_NOTHING)
5046     {
5047         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5048     }
5050     sp_style_unref(query);
5052     sp_desktop_set_style (desktop, css, true, true);
5053     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5054                                    _("Text: Change alignment"));
5055     sp_repr_css_attr_unref (css);
5057     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5060 void
5061 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
5062                                gpointer          data)
5064     if (g_object_get_data (G_OBJECT (button), "block")) return;
5066     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5067     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5068     int          prop       = GPOINTER_TO_INT(data);
5069     bool         active     = gtk_toggle_button_get_active (button);
5071     SPStyle *query =
5072         sp_style_new (SP_ACTIVE_DOCUMENT);
5074     int result_fontspec =
5075         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5077     //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5078     //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5079     //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5081     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
5082     Glib::ustring newFontSpec = "";
5084     if (fontSpec.empty()) {
5085         // Construct a new font specification if it does not yet exist
5086         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5087         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5088         fontFromStyle->Unref();
5089     }
5091     switch (prop)
5092     {
5093         case 0:
5094         {
5095             if (!fontSpec.empty()) {
5096                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
5097             }
5098             if (fontSpec != newFontSpec) {
5099                 // Don't even set the bold if the font didn't exist on the system
5100                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
5101             }
5102             break;
5103         }
5105         case 1:
5106         {
5107             if (!fontSpec.empty()) {
5108                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
5109             }
5110             if (fontSpec != newFontSpec) {
5111                 // Don't even set the italic if the font didn't exist on the system
5112                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
5113             }
5114             break;
5115         }
5116     }
5118     if (!newFontSpec.empty()) {
5119         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5120     }
5122     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5123     if (result_fontspec == QUERY_STYLE_NOTHING)
5124     {
5125         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5126     }
5128     sp_style_unref(query);
5130     sp_desktop_set_style (desktop, css, true, true);
5131     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5132                                    _("Text: Change font style"));
5133     sp_repr_css_attr_unref (css);
5135     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5138 void
5139 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
5140                                      gpointer         data)
5142     if (g_object_get_data (G_OBJECT (button), "block")) {
5143         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5144         return;
5145     }
5147     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5148     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5149     int          prop       = GPOINTER_TO_INT(data);
5151     switch (prop)
5152     {
5153         case 0:
5154         {
5155             sp_repr_css_set_property (css, "writing-mode", "lr");
5156             break;
5157         }
5159         case 1:
5160         {
5161             sp_repr_css_set_property (css, "writing-mode", "tb");
5162             break;
5163         }
5164     }
5166     SPStyle *query =
5167         sp_style_new (SP_ACTIVE_DOCUMENT);
5168     int result_numbers =
5169         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5171     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5172     if (result_numbers == QUERY_STYLE_NOTHING)
5173     {
5174         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5175     }
5177     sp_desktop_set_style (desktop, css, true, true);
5178     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5179                                    _("Text: Change orientation"));
5180     sp_repr_css_attr_unref (css);
5182     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5185 gboolean
5186 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5188     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5189     if (!desktop) return FALSE;
5191     switch (get_group0_keyval (event)) {
5192         case GDK_Escape: // defocus
5193             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5194             sp_text_toolbox_selection_changed (NULL, tbl); // update
5195             return TRUE; // I consumed the event
5196             break;
5197     }
5198     return FALSE;
5201 gboolean
5202 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5204     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5205     if (!desktop) return FALSE;
5207     switch (get_group0_keyval (event)) {
5208         case GDK_KP_Enter:
5209         case GDK_Return:
5210         case GDK_Escape: // defocus
5211             gtk_widget_hide (w);
5212             popdown_visible = false;
5213             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5214             return TRUE; // I consumed the event
5215             break;
5216         case GDK_w:
5217         case GDK_W:
5218             if (event->state & GDK_CONTROL_MASK) {
5219                 gtk_widget_hide (w);
5220                 popdown_visible = false;
5221                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5222                 return TRUE; // I consumed the event
5223             }
5224             break;
5225     }
5226     return FALSE;
5230 void
5231 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
5232                                GObject     *tbl)
5234     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5236     if (g_object_get_data (tbl, "size-block")) return;
5238     // If this is not from selecting a size in the list (in which case get_active will give the
5239     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5240     // process this event. This fixes GTK's stupid insistence on sending an activate change every
5241     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5242     if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5243         return;
5245     gchar *endptr;
5246     gdouble value = -1;
5247     char *text = gtk_combo_box_get_active_text (cbox);
5248     if (text) {
5249         value = g_strtod (text, &endptr);
5250         if (endptr == text) // conversion failed, non-numeric input
5251             value = -1;
5252         free (text);
5253     }
5254     if (value <= 0) {
5255         return; // could not parse value
5256     }
5258     SPCSSAttr *css = sp_repr_css_attr_new ();
5259     Inkscape::CSSOStringStream osfs;
5260     osfs << value;
5261     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5263     SPStyle *query =
5264         sp_style_new (SP_ACTIVE_DOCUMENT);
5265     int result_numbers =
5266         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5268     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5269     if (result_numbers == QUERY_STYLE_NOTHING)
5270     {
5271         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5272     }
5274     sp_style_unref(query);
5276     sp_desktop_set_style (desktop, css, true, true);
5277     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5278                                    _("Text: Change font size"));
5279     sp_repr_css_attr_unref (css);
5281     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5284 gboolean
5285 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5287     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5288     if (!desktop) return FALSE;
5290     if (!g_object_get_data (tbl, "esc-pressed")) {
5291         g_object_set_data (tbl, "enter-pressed", gpointer(1));
5292         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5293         sp_text_toolbox_size_changed (cbox, tbl);
5294         g_object_set_data (tbl, "enter-pressed", gpointer(0));
5295     }
5296     return FALSE; // I consumed the event
5300 gboolean
5301 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5303     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5304     if (!desktop) return FALSE;
5306     switch (get_group0_keyval (event)) {
5307         case GDK_Escape: // defocus
5308             g_object_set_data (tbl, "esc-pressed", gpointer(1));
5309             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5310             g_object_set_data (tbl, "esc-pressed", gpointer(0));
5311             return TRUE; // I consumed the event
5312             break;
5313         case GDK_Return: // defocus
5314         case GDK_KP_Enter:
5315             g_object_set_data (tbl, "enter-pressed", gpointer(1));
5316             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5317             sp_text_toolbox_size_changed (cbox, tbl);
5318             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5319             g_object_set_data (tbl, "enter-pressed", gpointer(0));
5320             return TRUE; // I consumed the event
5321             break;
5322     }
5323     return FALSE;
5326 void
5327 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
5328                                          GObject            *tbl)
5330     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
5331     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5332     int x, y;
5334     if (!popdown_visible)
5335     {
5336         gdk_window_get_origin (widget->window, &x, &y);
5337         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
5338         gtk_widget_show_all (popdown);
5339         //sp_transientize (popdown);
5341         gdk_pointer_grab (widget->window, TRUE,
5342                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
5343                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
5344                                         GDK_POINTER_MOTION_MASK),
5345                           NULL, NULL, GDK_CURRENT_TIME);
5347         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
5349         popdown_visible = true;
5350     }
5351     else
5352     {
5353         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5354         gdk_pointer_ungrab (GDK_CURRENT_TIME);
5355         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5356         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5357         gtk_widget_hide (popdown);
5358         popdown_visible = false;
5359     }
5362 gboolean
5363 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
5364                                  GdkEventFocus    */*event*/,
5365                                  GObject          */*tbl*/)
5367     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
5368     return FALSE;
5371 gboolean
5372 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
5373                                    GdkEventFocus    */*event*/,
5374                                    GObject          */*tbl*/)
5376     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5378     if (popdown_hasfocus) {
5379         gtk_widget_hide (popdown);
5380         popdown_hasfocus = false;
5381         popdown_visible = false;
5382         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5383         return TRUE;
5384     }
5385     return FALSE;
5388 gboolean
5389 sp_text_toolbox_popdown_focus_in (GtkWidget        */*popdown*/,
5390                                    GdkEventFocus    */*event*/,
5391                                    GObject          */*tbl*/)
5393     popdown_hasfocus = true;
5394     return TRUE;
5398 void
5399 cell_data_func  (GtkTreeViewColumn */*column*/,
5400                  GtkCellRenderer   *cell,
5401                  GtkTreeModel      *tree_model,
5402                  GtkTreeIter       *iter,
5403                  gpointer           /*data*/)
5405     char        *family,
5406         *family_escaped,
5407         *sample_escaped;
5409     static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
5411     gtk_tree_model_get (tree_model, iter, 0, &family, -1);
5413     family_escaped = g_markup_escape_text (family, -1);
5414     sample_escaped = g_markup_escape_text (sample, -1);
5416     std::stringstream markup;
5417     markup << family_escaped << "  <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
5418     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
5420     free (family);
5421     free (family_escaped);
5422     free (sample_escaped);
5425 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
5426     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
5427     if (completion) {
5428         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
5429         g_object_unref (completion);
5430     }
5433 GtkWidget*
5434 sp_text_toolbox_new (SPDesktop *desktop)
5436     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
5437     GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
5439     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
5440     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
5442     GtkTooltips *tt = gtk_tooltips_new();
5443     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
5445     ////////////Family
5446     //Window
5447     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5448     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
5450     //Entry
5451     GtkWidget           *entry = gtk_entry_new ();
5452     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
5453     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
5454     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
5455     gtk_entry_completion_set_text_column (completion, 0);
5456     gtk_entry_completion_set_minimum_key_length (completion, 1);
5457     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
5458     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
5459     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
5460     gtk_toolbar_append_widget( tbl, entry, "", "" );
5461     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
5463     //Button
5464     GtkWidget   *button = gtk_button_new ();
5465     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
5466     gtk_toolbar_append_widget( tbl, button, "", "");
5468     //Popdown
5469     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
5470     GtkWidget           *treeview = gtk_tree_view_new ();
5472     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
5473     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
5474     gtk_tree_view_column_pack_start (column, cell, FALSE);
5475     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
5476     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5477     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5479     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5480     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5481     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5483     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5485     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5486     gtk_container_add (GTK_CONTAINER (sw), treeview);
5488     gtk_container_add (GTK_CONTAINER (window), sw);
5489     gtk_widget_set_size_request (window, 300, 450);
5491     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5492     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5493     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5495     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5497     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5498     g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5499     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5501     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5502     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5504     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5505     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5506     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5507     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5508     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5510     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
5511     GtkWidget *box = gtk_event_box_new ();
5512     gtk_container_add (GTK_CONTAINER (box), image);
5513     gtk_toolbar_append_widget( tbl, box, "", "");
5514     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5515     GtkTooltips *tooltips = gtk_tooltips_new ();
5516     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5517     gtk_widget_hide (GTK_WIDGET (box));
5518     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
5520     ////////////Size
5521     const char *sizes[] = {
5522         "4", "6", "8", "9", "10", "11", "12", "13", "14",
5523         "16", "18", "20", "22", "24", "28",
5524         "32", "36", "40", "48", "56", "64", "72", "144"
5525     };
5527     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
5528     for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
5529     gtk_widget_set_size_request (cbox, 80, -1);
5530     gtk_toolbar_append_widget( tbl, cbox, "", "");
5531     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
5532     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
5533     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
5534     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
5536     ////////////Text anchor
5537     GtkWidget *group   = gtk_radio_button_new (NULL);
5538     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
5539     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
5541     // left
5542     GtkWidget *rbutton = group;
5543     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5544     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
5545     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5547     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5548     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
5549     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
5550     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
5552     // center
5553     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5554     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5555     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
5556     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5558     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5559     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
5560     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5561     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5563     // right
5564     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5565     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5566     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
5567     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5569     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5570     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
5571     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5572     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5574     // fill
5575     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5576     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5577     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
5578     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5580     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5581     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
5582     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
5583     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
5585     gtk_toolbar_append_widget( tbl, row, "", "");
5587     //spacer
5588     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5590     ////////////Text style
5591     row = gtk_hbox_new (FALSE, 4);
5593     // bold
5594     rbutton = gtk_toggle_button_new ();
5595     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5596     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
5597     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5598     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
5600     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5601     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
5602     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
5604     // italic
5605     rbutton = gtk_toggle_button_new ();
5606     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5607     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
5608     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5609     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
5611     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5612     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
5613     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
5615     gtk_toolbar_append_widget( tbl, row, "", "");
5617     //spacer
5618     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5620     ////////////Text orientation
5621     group   = gtk_radio_button_new (NULL);
5622     row     = gtk_hbox_new (FALSE, 4);
5623     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
5625     // horizontal
5626     rbutton = group;
5627     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5628     gtk_container_add           (GTK_CONTAINER (rbutton),
5629                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
5630     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5631     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
5633     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5634     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
5635     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
5637     // vertical
5638     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5639     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5640     gtk_container_add           (GTK_CONTAINER (rbutton),
5641                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
5642     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5643     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
5645     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5646     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
5647     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
5648     gtk_toolbar_append_widget( tbl, row, "", "" );
5651     //watch selection
5652     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
5654     sigc::connection *c_selection_changed =
5655         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5656                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
5657     pool->add_connection ("selection-changed", c_selection_changed);
5659     sigc::connection *c_selection_modified =
5660         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5661                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
5662     pool->add_connection ("selection-modified", c_selection_modified);
5664     sigc::connection *c_subselection_changed =
5665         new sigc::connection (desktop->connectToolSubselectionChanged
5666                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
5667     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
5669     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
5672     gtk_widget_show_all( GTK_WIDGET(tbl) );
5674     return GTK_WIDGET(tbl);
5675 } // end of sp_text_toolbox_new()
5677 }//<unnamed> namespace
5680 //#########################
5681 //##      Connector      ##
5682 //#########################
5684 static void sp_connector_path_set_avoid(void)
5686     cc_selection_set_avoid(true);
5690 static void sp_connector_path_set_ignore(void)
5692     cc_selection_set_avoid(false);
5697 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
5699     // quit if run by the _changed callbacks
5700     if (g_object_get_data( tbl, "freeze" )) {
5701         return;
5702     }
5704     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5705     SPDocument *doc = sp_desktop_document(desktop);
5707     if (!sp_document_get_undo_sensitive(doc))
5708     {
5709         return;
5710     }
5712     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5714     if ( repr->attribute("inkscape:connector-spacing") ) {
5715         gdouble priorValue = gtk_adjustment_get_value(adj);
5716         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
5717         if ( priorValue == gtk_adjustment_get_value(adj) ) {
5718             return;
5719         }
5720     } else if ( adj->value == defaultConnSpacing ) {
5721         return;
5722     }
5724     // in turn, prevent callbacks from responding
5725     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5727     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
5728     SP_OBJECT(desktop->namedview)->updateRepr();
5730     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
5731     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
5732         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
5733         NR::Matrix m = NR::identity();
5734         avoid_item_move(&m, item);
5735     }
5737     if (items) {
5738         g_slist_free(items);
5739     }
5741     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
5742             _("Change connector spacing"));
5744     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5746     spinbutton_defocus(GTK_OBJECT(tbl));
5749 static void sp_connector_graph_layout(void)
5751     if (!SP_ACTIVE_DESKTOP) return;
5753     // hack for clones, see comment in align-and-distribute.cpp
5754     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5755     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5757     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5759     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5761     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5764 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5766     if ( gtk_toggle_action_get_active( act ) ) {
5767         prefs_set_string_attribute("tools.connector", "directedlayout",
5768                 "true");
5769     } else {
5770         prefs_set_string_attribute("tools.connector", "directedlayout",
5771                 "false");
5772     }
5775 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5777     if ( gtk_toggle_action_get_active( act ) ) {
5778         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5779                 "true");
5780     } else {
5781         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5782                 "false");
5783     }
5787 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5789     prefs_set_double_attribute("tools.connector", "length", adj->value);
5790     spinbutton_defocus(GTK_OBJECT(tbl));
5793 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5794                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5795                                             bool /*is_interactive*/, gpointer data)
5797     GtkWidget *tbl = GTK_WIDGET(data);
5799     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5800         return;
5801     }
5802     if (strcmp(name, "inkscape:connector-spacing") != 0) {
5803         return;
5804     }
5806     GtkAdjustment *adj = (GtkAdjustment*)
5807             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5808     gdouble spacing = defaultConnSpacing;
5809     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5811     gtk_adjustment_set_value(adj, spacing);
5815 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5816     NULL, /* child_added */
5817     NULL, /* child_removed */
5818     connector_tb_event_attr_changed,
5819     NULL, /* content_changed */
5820     NULL  /* order_changed */
5821 };
5824 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5826     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
5828     {
5829         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5830                                           _("Avoid"),
5831                                           _("Make connectors avoid selected objects"),
5832                                           "connector_avoid",
5833                                           secondarySize );
5834         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5835         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5836     }
5838     {
5839         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5840                                           _("Ignore"),
5841                                           _("Make connectors ignore selected objects"),
5842                                           "connector_ignore",
5843                                           secondarySize );
5844         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5845         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5846     }
5848     EgeAdjustmentAction* eact = 0;
5850     // Spacing spinbox
5851     eact = create_adjustment_action( "ConnectorSpacingAction",
5852                                      _("Connector Spacing"), _("Spacing:"),
5853                                      _("The amount of space left around objects by auto-routing connectors"),
5854                                      "tools.connector", "spacing", defaultConnSpacing,
5855                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5856                                      0, 100, 1.0, 10.0,
5857                                      0, 0, 0,
5858                                      connector_spacing_changed, 1, 0 );
5859     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5861     // Graph (connector network) layout
5862     {
5863         InkAction* inky = ink_action_new( "ConnectorGraphAction",
5864                                           _("Graph"),
5865                                           _("Nicely arrange selected connector network"),
5866                                           "graph_layout",
5867                                           secondarySize );
5868         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5869         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5870     }
5872     // Default connector length spinbox
5873     eact = create_adjustment_action( "ConnectorLengthAction",
5874                                      _("Connector Length"), _("Length:"),
5875                                      _("Ideal length for connectors when layout is applied"),
5876                                      "tools.connector", "length", 100,
5877                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5878                                      10, 1000, 10.0, 100.0,
5879                                      0, 0, 0,
5880                                      connector_length_changed, 1, 0 );
5881     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5884     // Directed edges toggle button
5885     {
5886         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5887                                                       _("Downwards"),
5888                                                       _("Make connectors with end-markers (arrows) point downwards"),
5889                                                       "directed_graph",
5890                                                       Inkscape::ICON_SIZE_DECORATION );
5891         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5893         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5894         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5895                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5897         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5898     }
5900     // Avoid overlaps toggle button
5901     {
5902         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5903                                                       _("Remove overlaps"),
5904                                                       _("Do not allow overlapping shapes"),
5905                                                       "remove_overlaps",
5906                                                       Inkscape::ICON_SIZE_DECORATION );
5907         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5909         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5910         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5911                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5913         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5914     }
5916     // Code to watch for changes to the connector-spacing attribute in
5917     // the XML.
5918     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5919     g_assert(repr != NULL);
5921     purge_repr_listener( holder, holder );
5923     if (repr) {
5924         g_object_set_data( holder, "repr", repr );
5925         Inkscape::GC::anchor(repr);
5926         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5927         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5928     }
5929 } // end of sp_connector_toolbox_prep()
5932 //#########################
5933 //##     Paintbucket     ##
5934 //#########################
5936 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5938     gint channels = ege_select_one_action_get_active( act );
5939     flood_channels_set_channels( channels );
5942 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
5944     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
5947 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
5949     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
5952 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
5954     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
5955     SPUnit const *unit = tracker->getActiveUnit();
5957     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
5959     prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
5962 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
5964     // FIXME: make defaults settable via Inkscape Options
5965     struct KeyValue {
5966         char const *key;
5967         double value;
5968     } const key_values[] = {
5969         {"threshold", 15},
5970         {"offset", 0.0}
5971     };
5973     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
5974         KeyValue const &kv = key_values[i];
5975         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
5976         if ( adj ) {
5977             gtk_adjustment_set_value(adj, kv.value);
5978         }
5979     }
5981     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
5982     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
5983     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
5984     ege_select_one_action_set_active( autogap_action, 0 );
5987 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5989     EgeAdjustmentAction* eact = 0;
5991     {
5992         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5994         GList* items = 0;
5995         gint count = 0;
5996         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5997         {
5998             GtkTreeIter iter;
5999             gtk_list_store_append( model, &iter );
6000             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6001             count++;
6002         }
6003         g_list_free( items );
6004         items = 0;
6005         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
6006         g_object_set( act1, "short_label", _("Fill by:"), NULL );
6007         ege_select_one_action_set_appearance( act1, "compact" );
6008         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
6009         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
6010         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
6011         g_object_set_data( holder, "channels_action", act1 );
6012     }
6014     // Spacing spinbox
6015     {
6016         eact = create_adjustment_action(
6017             "ThresholdAction",
6018             _("Fill Threshold"), _("Threshold:"),
6019             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
6020             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
6021             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
6022             0, 0, 0,
6023             paintbucket_threshold_changed, 1, 0 );
6025         ege_adjustment_action_set_appearance( eact, "full" );
6026         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6027     }
6029     // Create the units menu.
6030     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
6031     const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
6032     if (stored_unit)
6033         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
6034     g_object_set_data( holder, "tracker", tracker );
6035     {
6036         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
6037         gtk_action_group_add_action( mainActions, act );
6038     }
6040     // Offset spinbox
6041     {
6042         eact = create_adjustment_action(
6043             "OffsetAction",
6044             _("Grow/shrink by"), _("Grow/shrink by:"),
6045             _("The amount to grow (positive) or shrink (negative) the created fill path"),
6046             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
6047             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
6048             0, 0, 0,
6049             paintbucket_offset_changed, 1, 2);
6050         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
6052         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6053     }
6055     /* Auto Gap */
6056     {
6057         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6059         GList* items = 0;
6060         gint count = 0;
6061         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
6062         {
6063             GtkTreeIter iter;
6064             gtk_list_store_append( model, &iter );
6065             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6066             count++;
6067         }
6068         g_list_free( items );
6069         items = 0;
6070         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
6071         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
6072         ege_select_one_action_set_appearance( act2, "compact" );
6073         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
6074         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
6075         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
6076         g_object_set_data( holder, "autogap_action", act2 );
6077     }
6079     /* Reset */
6080     {
6081         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
6082                                           _("Defaults"),
6083                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
6084                                           GTK_STOCK_CLEAR );
6085         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
6086         gtk_action_group_add_action( mainActions, act );
6087         gtk_action_set_sensitive( act, TRUE );
6088     }
6092 /*
6093   Local Variables:
6094   mode:c++
6095   c-file-style:"stroustrup"
6096   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
6097   indent-tabs-mode:nil
6098   fill-column:99
6099   End:
6100 */
6101 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :