Code

Absorb all mouse clicks (and flash a warning on the status bar) when no subtool is...
[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 *   Maximilian Albert <maximilian.albert@gmail.com>
18 *
19 * Copyright (C) 2004 David Turner
20 * Copyright (C) 2003 MenTaLguY
21 * Copyright (C) 1999-2008 authors
22 * Copyright (C) 2001-2002 Ximian, Inc.
23 *
24 * Released under GNU GPL, read the file 'COPYING' for more information
25 */
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
31 #include <cstring>
32 #include <string>
34 #include <gtkmm.h>
35 #include <gtk/gtk.h>
36 #include <iostream>
37 #include <sstream>
39 #include "widgets/button.h"
40 #include "widgets/widget-sizes.h"
41 #include "widgets/spw-utilities.h"
42 #include "widgets/spinbutton-events.h"
43 #include "dialogs/text-edit.h"
44 #include "dialogs/dialog-events.h"
46 #include "ui/widget/style-swatch.h"
48 #include "prefs-utils.h"
49 #include "verbs.h"
50 #include "sp-namedview.h"
51 #include "desktop.h"
52 #include "desktop-handles.h"
53 #include "xml/repr.h"
54 #include "xml/node-event-vector.h"
55 #include "xml/attribute-record.h"
56 #include <glibmm/i18n.h>
57 #include "helper/unit-menu.h"
58 #include "helper/units.h"
59 #include "live_effects/effect.h"
61 #include "inkscape.h"
62 #include "conn-avoid-ref.h"
65 #include "select-toolbar.h"
66 #include "gradient-toolbar.h"
68 #include "connector-context.h"
69 #include "node-context.h"
70 #include "pen-context.h"
71 #include "lpe-tool-context.h"
72 #include "shape-editor.h"
73 #include "tweak-context.h"
74 #include "sp-rect.h"
75 #include "box3d.h"
76 #include "box3d-context.h"
77 #include "sp-star.h"
78 #include "sp-spiral.h"
79 #include "sp-ellipse.h"
80 #include "sp-text.h"
81 #include "sp-flowtext.h"
82 #include "sp-clippath.h"
83 #include "sp-mask.h"
84 #include "style.h"
85 #include "tools-switch.h"
86 #include "selection.h"
87 #include "selection-chemistry.h"
88 #include "document-private.h"
89 #include "desktop-style.h"
90 #include "../libnrtype/font-lister.h"
91 #include "../libnrtype/font-instance.h"
92 #include "../connection-pool.h"
93 #include "../prefs-utils.h"
94 #include "../inkscape-stock.h"
95 #include "icon.h"
96 #include "graphlayout/graphlayout.h"
97 #include "interface.h"
98 #include "shortcuts.h"
100 #include "mod360.h"
102 #include "toolbox.h"
104 #include "flood-context.h"
106 #include "ink-action.h"
107 #include "ege-adjustment-action.h"
108 #include "ege-output-action.h"
109 #include "ege-select-one-action.h"
110 #include "helper/unit-tracker.h"
111 #include "live_effects/lpe-angle_bisector.h"
113 #include "svg/css-ostringstream.h"
115 #include "widgets/calligraphic-profile-rename.h"
117 using Inkscape::UnitTracker;
119 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
120 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
122 static void       sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
123 static void       sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
126 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
127 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
128 static void       box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
129 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
130 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
131 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
132 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
133 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
134 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
135 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
136 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
137 static void       sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
138 static void       sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
140 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
143 Inkscape::IconSize prefToSize( gchar const *path, gchar const *attr, int base ) {
144     static Inkscape::IconSize sizeChoices[] = {
145         Inkscape::ICON_SIZE_LARGE_TOOLBAR,
146         Inkscape::ICON_SIZE_SMALL_TOOLBAR,
147         Inkscape::ICON_SIZE_MENU
148     };
149     int index = prefs_get_int_attribute_limited( path, attr, base, 0, G_N_ELEMENTS(sizeChoices) );
150     return sizeChoices[index];
153 static struct {
154     gchar const *type_name;
155     gchar const *data_name;
156     sp_verb_t verb;
157     sp_verb_t doubleclick_verb;
158 } const tools[] = {
159     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
160     { "SPNodeContext",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
161     { "SPTweakContext",    "tweak_tool",     SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
162     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
163     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
164     { "Box3DContext",      "3dbox_tool",     SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
165     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
166     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
167     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
168     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
169     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
170     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
171     { "SPLPEToolContext",  "lpetool_tool",   SP_VERB_CONTEXT_LPETOOL, SP_VERB_CONTEXT_LPETOOL_PREFS },
172     { "SPEraserContext",   "eraser_tool",    SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
173     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
174     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
175     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
176     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
177     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
178     { NULL, NULL, 0, 0 }
179 };
181 static struct {
182     gchar const *type_name;
183     gchar const *data_name;
184     GtkWidget *(*create_func)(SPDesktop *desktop);
185     void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
186     gchar const *ui_name;
187     gint swatch_verb_id;
188     gchar const *swatch_tool;
189     gchar const *swatch_tip;
190 } const aux_toolboxes[] = {
191     { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep,            "SelectToolbar",
192       SP_VERB_INVALID, 0, 0},
193     { "SPNodeContext",   "node_toolbox",   0, sp_node_toolbox_prep,              "NodeToolbar",
194       SP_VERB_INVALID, 0, 0},
195     { "SPTweakContext",   "tweak_toolbox",   0, sp_tweak_toolbox_prep,              "TweakToolbar",
196       SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", N_("Color/opacity used for color tweaking")},
197     { "SPZoomContext",   "zoom_toolbox",   0, sp_zoom_toolbox_prep,              "ZoomToolbar",
198       SP_VERB_INVALID, 0, 0},
199     { "SPStarContext",   "star_toolbox",   0, sp_star_toolbox_prep,              "StarToolbar",
200       SP_VERB_CONTEXT_STAR_PREFS,   "tools.shapes.star",     N_("Style of new stars")},
201     { "SPRectContext",   "rect_toolbox",   0, sp_rect_toolbox_prep,              "RectToolbar",
202       SP_VERB_CONTEXT_RECT_PREFS,   "tools.shapes.rect",     N_("Style of new rectangles")},
203     { "Box3DContext",  "3dbox_toolbox",  0, box3d_toolbox_prep,             "3DBoxToolbar",
204       SP_VERB_CONTEXT_3DBOX_PREFS,  "tools.shapes.3dbox",    N_("Style of new 3D boxes")},
205     { "SPArcContext",    "arc_toolbox",    0, sp_arc_toolbox_prep,               "ArcToolbar",
206       SP_VERB_CONTEXT_ARC_PREFS,    "tools.shapes.arc",      N_("Style of new ellipses")},
207     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
208       SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral",   N_("Style of new spirals")},
209     { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep,            "PencilToolbar",
210       SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", N_("Style of new paths created by Pencil")},
211     { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep,                     "PenToolbar",
212       SP_VERB_CONTEXT_PEN_PREFS,    "tools.freehand.pen",    N_("Style of new paths created by Pen")},
213     { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
214       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", N_("Style of new calligraphic strokes")},
215     { "SPEraserContext", "eraser_toolbox", 0, sp_eraser_toolbox_prep,"EraserToolbar",
216       SP_VERB_CONTEXT_ERASER_PREFS, "tools.eraser", _("TBD")},
217     { "SPLPEToolContext", "lpetool_toolbox", 0, sp_lpetool_toolbox_prep, "LPEToolToolbar",
218       SP_VERB_CONTEXT_LPETOOL_PREFS, "tools.lpetool", _("TBD")},
219     { "SPTextContext",   "text_toolbox",   sp_text_toolbox_new, 0,               0,
220       SP_VERB_INVALID, 0, 0},
221     { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep,         "DropperToolbar",
222       SP_VERB_INVALID, 0, 0},
223     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0,       0,
224       SP_VERB_INVALID, 0, 0},
225     { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep,   "ConnectorToolbar",
226       SP_VERB_INVALID, 0, 0},
227     { "SPFloodContext",  "paintbucket_toolbox",  0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
228       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", N_("Style of Paint Bucket fill objects")},
229     { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
230 };
232 #define TOOLBAR_SLIDER_HINT "full"
234 static gchar const * ui_descr =
235         "<ui>"
236         "  <toolbar name='SelectToolbar'>"
237         "    <toolitem action='EditSelectAll' />"
238         "    <toolitem action='EditSelectAllInAllLayers' />"
239         "    <toolitem action='EditDeselect' />"
240         "    <separator />"
241         "    <toolitem action='ObjectRotate90CCW' />"
242         "    <toolitem action='ObjectRotate90' />"
243         "    <toolitem action='ObjectFlipHorizontally' />"
244         "    <toolitem action='ObjectFlipVertically' />"
245         "    <separator />"
246         "    <toolitem action='SelectionToBack' />"
247         "    <toolitem action='SelectionLower' />"
248         "    <toolitem action='SelectionRaise' />"
249         "    <toolitem action='SelectionToFront' />"
250         "    <separator />"
251         "    <toolitem action='XAction' />"
252         "    <toolitem action='YAction' />"
253         "    <toolitem action='WidthAction' />"
254         "    <toolitem action='LockAction' />"
255         "    <toolitem action='HeightAction' />"
256         "    <toolitem action='UnitsAction' />"
257         "    <separator />"
258         "    <toolitem action='transform_affect_label' />"
259         "    <toolitem action='transform_stroke' />"
260         "    <toolitem action='transform_corners' />"
261         "    <toolitem action='transform_gradient' />"
262         "    <toolitem action='transform_pattern' />"
263         "  </toolbar>"
265         "  <toolbar name='NodeToolbar'>"
266         "    <toolitem action='NodeInsertAction' />"
267         "    <toolitem action='NodeDeleteAction' />"
268         "    <separator />"
269         "    <toolitem action='NodeJoinAction' />"
270         "    <toolitem action='NodeBreakAction' />"
271         "    <separator />"
272         "    <toolitem action='NodeJoinSegmentAction' />"
273         "    <toolitem action='NodeDeleteSegmentAction' />"
274         "    <separator />"
275         "    <toolitem action='NodeCuspAction' />"
276         "    <toolitem action='NodeSmoothAction' />"
277         "    <toolitem action='NodeSymmetricAction' />"
278         "    <separator />"
279         "    <toolitem action='NodeLineAction' />"
280         "    <toolitem action='NodeCurveAction' />"
281         "    <separator />"
282         "    <toolitem action='ObjectToPath' />"
283         "    <toolitem action='StrokeToPath' />"
284         "    <separator />"
285         "    <toolitem action='NodeXAction' />"
286         "    <toolitem action='NodeYAction' />"
287         "    <toolitem action='NodeUnitsAction' />"
288         "    <separator />"
289         "    <toolitem action='ObjectEditClipPathAction' />"
290         "    <toolitem action='ObjectEditMaskPathAction' />"
291         "    <toolitem action='EditNextLPEParameterAction' />"
292         "    <separator />"
293         "    <toolitem action='NodesShowHandlesAction' />"
294         "    <toolitem action='NodesShowHelperpath' />"
295         "  </toolbar>"
297         "  <toolbar name='TweakToolbar'>"
298         "    <toolitem action='TweakWidthAction' />"
299         "    <separator />"
300         "    <toolitem action='TweakForceAction' />"
301         "    <toolitem action='TweakPressureAction' />"
302         "    <separator />"
303         "    <toolitem action='TweakModeAction' />"
304         "    <separator />"
305         "    <toolitem action='TweakFidelityAction' />"
306         "    <separator />"
307         "    <toolitem action='TweakChannelsLabel' />"
308         "    <toolitem action='TweakDoH' />"
309         "    <toolitem action='TweakDoS' />"
310         "    <toolitem action='TweakDoL' />"
311         "    <toolitem action='TweakDoO' />"
312         "  </toolbar>"
314         "  <toolbar name='ZoomToolbar'>"
315         "    <toolitem action='ZoomIn' />"
316         "    <toolitem action='ZoomOut' />"
317         "    <separator />"
318         "    <toolitem action='Zoom1:0' />"
319         "    <toolitem action='Zoom1:2' />"
320         "    <toolitem action='Zoom2:1' />"
321         "    <separator />"
322         "    <toolitem action='ZoomSelection' />"
323         "    <toolitem action='ZoomDrawing' />"
324         "    <toolitem action='ZoomPage' />"
325         "    <toolitem action='ZoomPageWidth' />"
326         "    <separator />"
327         "    <toolitem action='ZoomPrev' />"
328         "    <toolitem action='ZoomNext' />"
329         "  </toolbar>"
331         "  <toolbar name='StarToolbar'>"
332         "    <separator />"
333         "    <toolitem action='StarStateAction' />"
334         "    <separator />"
335         "    <toolitem action='FlatAction' />"
336         "    <separator />"
337         "    <toolitem action='MagnitudeAction' />"
338         "    <toolitem action='SpokeAction' />"
339         "    <toolitem action='RoundednessAction' />"
340         "    <toolitem action='RandomizationAction' />"
341         "    <separator />"
342         "    <toolitem action='StarResetAction' />"
343         "  </toolbar>"
345         "  <toolbar name='RectToolbar'>"
346         "    <toolitem action='RectStateAction' />"
347         "    <toolitem action='RectWidthAction' />"
348         "    <toolitem action='RectHeightAction' />"
349         "    <toolitem action='RadiusXAction' />"
350         "    <toolitem action='RadiusYAction' />"
351         "    <toolitem action='RectUnitsAction' />"
352         "    <separator />"
353         "    <toolitem action='RectResetAction' />"
354         "  </toolbar>"
356         "  <toolbar name='3DBoxToolbar'>"
357         "    <toolitem action='3DBoxAngleXAction' />"
358         "    <toolitem action='3DBoxVPXStateAction' />"
359         "    <separator />"
360         "    <toolitem action='3DBoxAngleYAction' />"
361         "    <toolitem action='3DBoxVPYStateAction' />"
362         "    <separator />"
363         "    <toolitem action='3DBoxAngleZAction' />"
364         "    <toolitem action='3DBoxVPZStateAction' />"
365         "  </toolbar>"
367         "  <toolbar name='SpiralToolbar'>"
368         "    <toolitem action='SpiralStateAction' />"
369         "    <toolitem action='SpiralRevolutionAction' />"
370         "    <toolitem action='SpiralExpansionAction' />"
371         "    <toolitem action='SpiralT0Action' />"
372         "    <separator />"
373         "    <toolitem action='SpiralResetAction' />"
374         "  </toolbar>"
376         "  <toolbar name='PenToolbar'>"
377         "    <toolitem action='FreehandModeActionPen' />"
378         "    <separator />"
379         "    <toolitem action='SetPenShapeAction'/>"
380         "  </toolbar>"
382         "  <toolbar name='PencilToolbar'>"
383         "    <toolitem action='FreehandModeActionPencil' />"
384         "    <separator />"
385         "    <toolitem action='PencilToleranceAction' />"
386         "    <separator />"
387         "    <toolitem action='PencilResetAction' />"
388         "    <separator />"
389         "    <toolitem action='SetPencilShapeAction'/>"
390         "  </toolbar>"
392         "  <toolbar name='CalligraphyToolbar'>"
393         "    <separator />"
394         "    <toolitem action='SetProfileAction'/>"
395         "    <separator />"
396         "    <toolitem action='CalligraphyWidthAction' />"
397         "    <toolitem action='PressureAction' />"
398         "    <toolitem action='TraceAction' />"
399         "    <toolitem action='ThinningAction' />"
400         "    <separator />"
401         "    <toolitem action='AngleAction' />"
402         "    <toolitem action='TiltAction' />"
403         "    <toolitem action='FixationAction' />"
404         "    <separator />"
405         "    <toolitem action='CapRoundingAction' />"
406         "    <separator />"
407         "    <toolitem action='TremorAction' />"
408         "    <toolitem action='WiggleAction' />"
409         "    <toolitem action='MassAction' />"
410         "    <separator />"
411         "  </toolbar>"
413         "  <toolbar name='ArcToolbar'>"
414         "    <toolitem action='ArcStateAction' />"
415         "    <separator />"
416         "    <toolitem action='ArcStartAction' />"
417         "    <toolitem action='ArcEndAction' />"
418         "    <separator />"
419         "    <toolitem action='ArcOpenAction' />"
420         "    <separator />"
421         "    <toolitem action='ArcResetAction' />"
422         "    <separator />"
423         "  </toolbar>"
425         "  <toolbar name='PaintbucketToolbar'>"
426         "    <toolitem action='ChannelsAction' />"
427         "    <separator />"
428         "    <toolitem action='ThresholdAction' />"
429         "    <separator />"
430         "    <toolitem action='OffsetAction' />"
431         "    <toolitem action='PaintbucketUnitsAction' />"
432         "    <separator />"
433         "    <toolitem action='AutoGapAction' />"
434         "    <separator />"
435         "    <toolitem action='PaintbucketResetAction' />"
436         "  </toolbar>"
438         "  <toolbar name='EraserToolbar'>"
439         "    <toolitem action='EraserWidthAction' />"
440         "    <separator />"
441         "    <toolitem action='EraserModeAction' />"
442         "  </toolbar>"
444         "  <toolbar name='LPEToolToolbar'>"
445         "    <toolitem action='LPEToolModeAction' />"
446         "    <separator />"
447         "    <toolitem action='LPEShowBBoxAction' />"
448         "  </toolbar>"
450         "  <toolbar name='DropperToolbar'>"
451         "    <toolitem action='DropperOpacityAction' />"
452         "    <toolitem action='DropperPickAlphaAction' />"
453         "    <toolitem action='DropperSetAlphaAction' />"
454         "  </toolbar>"
456         "  <toolbar name='ConnectorToolbar'>"
457         "    <toolitem action='ConnectorAvoidAction' />"
458         "    <toolitem action='ConnectorIgnoreAction' />"
459         "    <toolitem action='ConnectorSpacingAction' />"
460         "    <toolitem action='ConnectorGraphAction' />"
461         "    <toolitem action='ConnectorLengthAction' />"
462         "    <toolitem action='ConnectorDirectedAction' />"
463         "    <toolitem action='ConnectorOverlapAction' />"
464         "  </toolbar>"
466         "</ui>"
469 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
471 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
473 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
474 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
476 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
477 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
479 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
480 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
483 GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick( GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
484                                                               Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
485                                                               Inkscape::UI::View::View *view, GtkTooltips *tt);
487 class VerbAction : public Gtk::Action {
488 public:
489     static Glib::RefPtr<VerbAction> create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
491     virtual ~VerbAction();
492     virtual void set_active(bool active = true);
494 protected:
495     virtual Gtk::Widget* create_menu_item_vfunc();
496     virtual Gtk::Widget* create_tool_item_vfunc();
498     virtual void connect_proxy_vfunc(Gtk::Widget* proxy);
499     virtual void disconnect_proxy_vfunc(Gtk::Widget* proxy);
501     virtual void on_activate();
503 private:
504     Inkscape::Verb* verb;
505     Inkscape::Verb* verb2;
506     Inkscape::UI::View::View *view;
507     GtkTooltips *tooltips;
508     bool active;
510     VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
511 };
514 Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips)
516     Glib::RefPtr<VerbAction> result;
517     SPAction *action = verb->get_action(view);
518     if ( action ) {
519         //SPAction* action2 = verb2 ? verb2->get_action(view) : 0;
520         result = Glib::RefPtr<VerbAction>(new VerbAction(verb, verb2, view, tooltips));
521     }
523     return result;
526 VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips) :
527     Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(verb->get_image()), Glib::ustring(_(verb->get_name())), Glib::ustring(_(verb->get_tip()))),
528     verb(verb),
529     verb2(verb2),
530     view(view),
531     tooltips(tooltips),
532     active(false)
536 VerbAction::~VerbAction()
540 Gtk::Widget* VerbAction::create_menu_item_vfunc()
542 // First call in to get the icon rendered if present in SVG
543     Gtk::Widget *widget = sp_icon_get_icon( property_stock_id().get_value().get_string(), Inkscape::ICON_SIZE_MENU );
544     delete widget;
545     widget = 0;
547     Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
548 //     g_message("create_menu_item_vfunc() = %p  for '%s'", widg, verb->get_id());
549     return widg;
552 Gtk::Widget* VerbAction::create_tool_item_vfunc()
554 //     Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
555     Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
556     GtkWidget* toolbox = 0;
557     GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
558                                                                           SP_BUTTON_TYPE_TOGGLE,
559                                                                           verb,
560                                                                           verb2,
561                                                                           view,
562                                                                           tooltips );
563     if ( active ) {
564         sp_button_toggle_set_down( SP_BUTTON(button), active);
565     }
566     gtk_widget_show_all( button );
567     Gtk::Widget* wrapped = Glib::wrap(button);
568     Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
569     holder->add(*wrapped);
571 //     g_message("create_tool_item_vfunc() = %p  for '%s'", holder, verb->get_id());
572     return holder;
575 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
577 //     g_message("connect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
578     Gtk::Action::connect_proxy_vfunc(proxy);
581 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
583 //     g_message("disconnect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
584     Gtk::Action::disconnect_proxy_vfunc(proxy);
587 void VerbAction::set_active(bool active)
589     this->active = active;
590     Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
591     for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
592         Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
593         if (ti) {
594             // *should* have one child that is the SPButton
595             Gtk::Widget* child = ti->get_child();
596             if ( child && SP_IS_BUTTON(child->gobj()) ) {
597                 SPButton* button = SP_BUTTON(child->gobj());
598                 sp_button_toggle_set_down( button, active );
599             }
600         }
601     }
604 void VerbAction::on_activate()
606     if ( verb ) {
607         SPAction *action = verb->get_action(view);
608         if ( action ) {
609             sp_action_perform(action, 0);
610         }
611     }
614 /* Global text entry widgets necessary for update */
615 /* GtkWidget *dropper_rgb_entry,
616           *dropper_opacity_entry ; */
617 // should be made a private member once this is converted to class
619 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
620     connection->disconnect();
621     delete connection;
624 static void purge_repr_listener( GObject* obj, GObject* tbl )
626     (void)obj;
627     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
628     if (oldrepr) { // remove old listener
629         sp_repr_remove_listener_by_data(oldrepr, tbl);
630         Inkscape::GC::release(oldrepr);
631         oldrepr = 0;
632         g_object_set_data( tbl, "repr", NULL );
633     }
636 GtkWidget *
637 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
638                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
639                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
641     SPAction *action = verb->get_action(view);
642     if (!action) return NULL;
644     SPAction *doubleclick_action;
645     if (doubleclick_verb)
646         doubleclick_action = doubleclick_verb->get_action(view);
647     else
648         doubleclick_action = NULL;
650     /* fixme: Handle sensitive/unsensitive */
651     /* fixme: Implement sp_button_new_from_action */
652     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
653     gtk_widget_show(b);
656     unsigned int shortcut = sp_shortcut_get_primary(verb);
657     if (shortcut) {
658         gchar key[256];
659         sp_ui_shortcut_string(shortcut, key);
660         gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
661         if ( t ) {
662             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
663         }
664         g_free(tip);
665     } else {
666         if ( t ) {
667             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
668         }
669     }
671     return b;
675 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
677     SPAction* targetAction = SP_ACTION(user_data);
678     if ( targetAction ) {
679         sp_action_perform( targetAction, NULL );
680     }
683 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
685     if ( data ) {
686         GtkAction* act = GTK_ACTION(data);
687         gtk_action_set_sensitive( act, sensitive );
688     }
691 static SPActionEventVector action_event_vector = {
692     {NULL},
693     NULL,
694     NULL,
695     sp_action_action_set_sensitive,
696     NULL,
697     NULL
698 };
700 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
702     GtkAction* act = 0;
704     SPAction* targetAction = verb->get_action(view);
705     InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size  );
706     act = GTK_ACTION(inky);
707     gtk_action_set_sensitive( act, targetAction->sensitive );
709     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
711     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
712     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
714     return act;
717 Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
719     Inkscape::UI::View::View *view = desktop;
720     gint verbsToUse[] = {
721         // disabled until we have icons for them:
722         //find
723         //SP_VERB_EDIT_TILE,
724         //SP_VERB_EDIT_UNTILE,
725         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
726         SP_VERB_DIALOG_DISPLAY,
727         SP_VERB_DIALOG_FILL_STROKE,
728         SP_VERB_DIALOG_NAMEDVIEW,
729         SP_VERB_DIALOG_TEXT,
730         SP_VERB_DIALOG_XML_EDITOR,
731         SP_VERB_EDIT_CLONE,
732         SP_VERB_EDIT_COPY,
733         SP_VERB_EDIT_CUT,
734         SP_VERB_EDIT_DUPLICATE,
735         SP_VERB_EDIT_PASTE,
736         SP_VERB_EDIT_REDO,
737         SP_VERB_EDIT_UNDO,
738         SP_VERB_EDIT_UNLINK_CLONE,
739         SP_VERB_FILE_EXPORT,
740         SP_VERB_FILE_IMPORT,
741         SP_VERB_FILE_NEW,
742         SP_VERB_FILE_OPEN,
743         SP_VERB_FILE_PRINT,
744         SP_VERB_FILE_SAVE,
745         SP_VERB_OBJECT_TO_CURVE,
746         SP_VERB_SELECTION_GROUP,
747         SP_VERB_SELECTION_OUTLINE,
748         SP_VERB_SELECTION_UNGROUP,
749         SP_VERB_ZOOM_1_1,
750         SP_VERB_ZOOM_1_2,
751         SP_VERB_ZOOM_2_1,
752         SP_VERB_ZOOM_DRAWING,
753         SP_VERB_ZOOM_IN,
754         SP_VERB_ZOOM_NEXT,
755         SP_VERB_ZOOM_OUT,
756         SP_VERB_ZOOM_PAGE,
757         SP_VERB_ZOOM_PAGE_WIDTH,
758         SP_VERB_ZOOM_PREV,
759         SP_VERB_ZOOM_SELECTION,
760     };
762     Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
764     static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
765     Glib::RefPtr<Gtk::ActionGroup> mainActions;
766     if ( groups.find(desktop) != groups.end() ) {
767         mainActions = groups[desktop];
768     }
770     if ( !mainActions ) {
771         mainActions = Gtk::ActionGroup::create("main");
772         groups[desktop] = mainActions;
773     }
775     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
776         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
777         if ( verb ) {
778             if (!mainActions->get_action(verb->get_id())) {
779                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
780                 mainActions->add(Glib::wrap(act));
781             }
782         }
783     }
785     if ( !mainActions->get_action("ToolZoom") ) {
786         GtkTooltips *tt = gtk_tooltips_new();
787         for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
788             Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
789             if ( va ) {
790                 mainActions->add(va);
791                 if ( i == 0 ) {
792                     va->set_active(true);
793                 }
794             }
795         }
796     }
799     return mainActions;
803 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
805     gtk_widget_set_size_request( widget,
806                                  widget->allocation.width,
807                                  widget->allocation.height );
810 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
812     gtk_widget_set_size_request( widget, -1, -1 );
817 GtkWidget *
818 sp_tool_toolbox_new()
820     GtkTooltips *tt = gtk_tooltips_new();
821     GtkWidget* tb = gtk_toolbar_new();
822     gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
823     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
825     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
826     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
828     gtk_widget_set_sensitive(tb, FALSE);
830     GtkWidget *hb = gtk_handle_box_new();
831     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
832     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
833     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
835     gtk_container_add(GTK_CONTAINER(hb), tb);
836     gtk_widget_show(GTK_WIDGET(tb));
838     sigc::connection* conn = new sigc::connection;
839     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
841     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
842     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
844     return hb;
847 GtkWidget *
848 sp_aux_toolbox_new()
850     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
852     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
854     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
856     gtk_widget_set_sensitive(tb, FALSE);
858     GtkWidget *hb = gtk_handle_box_new();
859     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
860     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
861     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
863     gtk_container_add(GTK_CONTAINER(hb), tb);
864     gtk_widget_show(GTK_WIDGET(tb));
866     sigc::connection* conn = new sigc::connection;
867     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
869     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
870     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
872     return hb;
875 //####################################
876 //# Commands Bar
877 //####################################
879 GtkWidget *
880 sp_commands_toolbox_new()
882     GtkWidget *tb = gtk_toolbar_new();
884     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
885     gtk_widget_set_sensitive(tb, FALSE);
887     GtkWidget *hb = gtk_handle_box_new();
888     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
889     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
890     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
892     gtk_container_add(GTK_CONTAINER(hb), tb);
893     gtk_widget_show(GTK_WIDGET(tb));
895     sigc::connection* conn = new sigc::connection;
896     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
898     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
899     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
901     return hb;
905 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
906                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
907                                                        gchar const *path, gchar const *data, gdouble def,
908                                                        GtkWidget *focusTarget,
909                                                        GtkWidget *us,
910                                                        GObject *dataKludge,
911                                                        gboolean altx, gchar const *altx_mark,
912                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
913                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
914                                                        void (*callback)(GtkAdjustment *, GObject *),
915                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
917     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
918                                                              lower, upper, step, page, page ) );
919     if (us) {
920         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
921     }
923     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
925     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
926     if ( shortLabel ) {
927         g_object_set( act, "short_label", shortLabel, NULL );
928     }
930     if ( (descrCount > 0) && descrLabels && descrValues ) {
931         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
932     }
934     if ( focusTarget ) {
935         ege_adjustment_action_set_focuswidget( act, focusTarget );
936     }
938     if ( altx && altx_mark ) {
939         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
940     }
942     if ( dataKludge ) {
943         g_object_set_data( dataKludge, data, adj );
944     }
946     // Using a cast just to make sure we pass in the right kind of function pointer
947     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
949     return act;
953 //####################################
954 //# node editing callbacks
955 //####################################
957 /**
958  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
959  */
960 static ShapeEditor *get_current_shape_editor()
962     if (!SP_ACTIVE_DESKTOP) {
963         return NULL;
964     }
966     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
968     if (!SP_IS_NODE_CONTEXT(event_context)) {
969         return NULL;
970     }
972     return SP_NODE_CONTEXT(event_context)->shape_editor;
976 void
977 sp_node_path_edit_add(void)
979     ShapeEditor *shape_editor = get_current_shape_editor();
980     if (shape_editor) shape_editor->add_node();
983 void
984 sp_node_path_edit_delete(void)
986     ShapeEditor *shape_editor = get_current_shape_editor();
987     if (shape_editor) shape_editor->delete_nodes_preserving_shape();
990 void
991 sp_node_path_edit_delete_segment(void)
993     ShapeEditor *shape_editor = get_current_shape_editor();
994     if (shape_editor) shape_editor->delete_segment();
997 void
998 sp_node_path_edit_break(void)
1000     ShapeEditor *shape_editor = get_current_shape_editor();
1001     if (shape_editor) shape_editor->break_at_nodes();
1004 void
1005 sp_node_path_edit_join(void)
1007     ShapeEditor *shape_editor = get_current_shape_editor();
1008     if (shape_editor) shape_editor->join_nodes();
1011 void
1012 sp_node_path_edit_join_segment(void)
1014     ShapeEditor *shape_editor = get_current_shape_editor();
1015     if (shape_editor) shape_editor->join_segments();
1018 void
1019 sp_node_path_edit_toline(void)
1021     ShapeEditor *shape_editor = get_current_shape_editor();
1022     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1025 void
1026 sp_node_path_edit_tocurve(void)
1028     ShapeEditor *shape_editor = get_current_shape_editor();
1029     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1032 void
1033 sp_node_path_edit_cusp(void)
1035     ShapeEditor *shape_editor = get_current_shape_editor();
1036     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1039 void
1040 sp_node_path_edit_smooth(void)
1042     ShapeEditor *shape_editor = get_current_shape_editor();
1043     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1046 void
1047 sp_node_path_edit_symmetrical(void)
1049     ShapeEditor *shape_editor = get_current_shape_editor();
1050     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1053 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1054     bool show = gtk_toggle_action_get_active( act );
1055     prefs_set_int_attribute ("tools.nodes", "show_handles",  show ? 1 : 0);
1056     ShapeEditor *shape_editor = get_current_shape_editor();
1057     if (shape_editor) shape_editor->show_handles(show);
1060 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1061     bool show = gtk_toggle_action_get_active( act );
1062     prefs_set_int_attribute ("tools.nodes", "show_helperpath",  show ? 1 : 0);
1063     ShapeEditor *shape_editor = get_current_shape_editor();
1064     if (shape_editor) shape_editor->show_helperpath(show);
1067 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1068     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1071 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1072     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1075 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1076     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1079 /* is called when the node selection is modified */
1080 static void
1081 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1083     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1084     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1085     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1086     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1088     // quit if run by the attr_changed listener
1089     if (g_object_get_data( tbl, "freeze" )) {
1090         return;
1091     }
1093     // in turn, prevent listener from responding
1094     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1096     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1097     SPUnit const *unit = tracker->getActiveUnit();
1099     ShapeEditor *shape_editor = get_current_shape_editor();
1100     if (shape_editor && shape_editor->has_nodepath()) {
1101         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1102         int n_selected = 0;
1103         if (nodepath) {
1104             n_selected = nodepath->numSelected();
1105         }
1107         if (n_selected == 0) {
1108             gtk_action_set_sensitive(xact, FALSE);
1109             gtk_action_set_sensitive(yact, FALSE);
1110         } else {
1111             gtk_action_set_sensitive(xact, TRUE);
1112             gtk_action_set_sensitive(yact, TRUE);
1113             Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1114             Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1116             if (n_selected == 1) {
1117                 Geom::Point sel_node = nodepath->singleSelectedCoords();
1118                 if (oldx != sel_node[Geom::X] || oldy != sel_node[Geom::Y]) {
1119                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[Geom::X], *unit));
1120                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[Geom::Y], *unit));
1121                 }
1122             } else {
1123                 boost::optional<Geom::Coord> x = sp_node_selected_common_coord(nodepath, Geom::X);
1124                 boost::optional<Geom::Coord> y = sp_node_selected_common_coord(nodepath, Geom::Y);
1125                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1126                     /* Note: Currently x and y will always have a value, even if the coordinates of the
1127                        selected nodes don't coincide (in this case we use the coordinates of the center
1128                        of the bounding box). So the entries are never set to zero. */
1129                     // FIXME: Maybe we should clear the entry if several nodes are selected
1130                     //        instead of providing a kind of average value
1131                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1132                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1133                 }
1134             }
1135         }
1136     } else {
1137         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1138         gtk_action_set_sensitive(xact, FALSE);
1139         gtk_action_set_sensitive(yact, FALSE);
1140     }
1142     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1145 static void
1146 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1148     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1150     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1151     SPUnit const *unit = tracker->getActiveUnit();
1153     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1154         prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
1155     }
1157     // quit if run by the attr_changed listener
1158     if (g_object_get_data( tbl, "freeze" )) {
1159         return;
1160     }
1162     // in turn, prevent listener from responding
1163     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1165     ShapeEditor *shape_editor = get_current_shape_editor();
1166     if (shape_editor && shape_editor->has_nodepath()) {
1167         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1168         if (!strcmp(value_name, "x")) {
1169             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::X);
1170         }
1171         if (!strcmp(value_name, "y")) {
1172             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::Y);
1173         }
1174     }
1176     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1179 static void
1180 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1182     sp_node_path_value_changed(adj, tbl, "x");
1185 static void
1186 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1188     sp_node_path_value_changed(adj, tbl, "y");
1191 void
1192 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1194     {
1195     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1196     SPItem *item = selection->singleItem();
1197     if (item && SP_IS_LPE_ITEM(item)) {
1198        if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1199            gtk_action_set_sensitive(w, TRUE);
1200        } else {
1201            gtk_action_set_sensitive(w, FALSE);
1202        }
1203     } else {
1204        gtk_action_set_sensitive(w, FALSE);
1205     }
1206     }
1208     {
1209     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1210     SPItem *item = selection->singleItem();
1211     if (item && item->clip_ref && item->clip_ref->getObject()) {
1212        gtk_action_set_sensitive(w, TRUE);
1213     } else {
1214        gtk_action_set_sensitive(w, FALSE);
1215     }
1216     }
1218     {
1219     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1220     SPItem *item = selection->singleItem();
1221     if (item && item->mask_ref && item->mask_ref->getObject()) {
1222        gtk_action_set_sensitive(w, TRUE);
1223     } else {
1224        gtk_action_set_sensitive(w, FALSE);
1225     }
1226     }
1229 void
1230 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1232     sp_node_toolbox_sel_changed (selection, tbl);
1237 //################################
1238 //##    Node Editing Toolbox    ##
1239 //################################
1241 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1243     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1244     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1245     g_object_set_data( holder, "tracker", tracker );
1247     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
1249     {
1250         InkAction* inky = ink_action_new( "NodeInsertAction",
1251                                           _("Insert node"),
1252                                           _("Insert new nodes into selected segments"),
1253                                           "node_insert",
1254                                           secondarySize );
1255         g_object_set( inky, "short_label", _("Insert"), NULL );
1256         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1257         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1258     }
1260     {
1261         InkAction* inky = ink_action_new( "NodeDeleteAction",
1262                                           _("Delete node"),
1263                                           _("Delete selected nodes"),
1264                                           "node_delete",
1265                                           secondarySize );
1266         g_object_set( inky, "short_label", _("Delete"), NULL );
1267         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1268         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1269     }
1271     {
1272         InkAction* inky = ink_action_new( "NodeJoinAction",
1273                                           _("Join endnodes"),
1274                                           _("Join selected endnodes"),
1275                                           "node_join",
1276                                           secondarySize );
1277         g_object_set( inky, "short_label", _("Join"), NULL );
1278         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1279         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1280     }
1282     {
1283         InkAction* inky = ink_action_new( "NodeBreakAction",
1284                                           _("Break nodes"),
1285                                           _("Break path at selected nodes"),
1286                                           "node_break",
1287                                           secondarySize );
1288         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1289         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1290     }
1293     {
1294         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1295                                           _("Join with segment"),
1296                                           _("Join selected endnodes with a new segment"),
1297                                           "node_join_segment",
1298                                           secondarySize );
1299         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1300         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1301     }
1303     {
1304         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1305                                           _("Delete segment"),
1306                                           _("Delete segment between two non-endpoint nodes"),
1307                                           "node_delete_segment",
1308                                           secondarySize );
1309         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1310         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1311     }
1313     {
1314         InkAction* inky = ink_action_new( "NodeCuspAction",
1315                                           _("Node Cusp"),
1316                                           _("Make selected nodes corner"),
1317                                           "node_cusp",
1318                                           secondarySize );
1319         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1320         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1321     }
1323     {
1324         InkAction* inky = ink_action_new( "NodeSmoothAction",
1325                                           _("Node Smooth"),
1326                                           _("Make selected nodes smooth"),
1327                                           "node_smooth",
1328                                           secondarySize );
1329         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1330         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1331     }
1333     {
1334         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1335                                           _("Node Symmetric"),
1336                                           _("Make selected nodes symmetric"),
1337                                           "node_symmetric",
1338                                           secondarySize );
1339         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1340         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1341     }
1343     {
1344         InkAction* inky = ink_action_new( "NodeLineAction",
1345                                           _("Node Line"),
1346                                           _("Make selected segments lines"),
1347                                           "node_line",
1348                                           secondarySize );
1349         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1350         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1351     }
1353     {
1354         InkAction* inky = ink_action_new( "NodeCurveAction",
1355                                           _("Node Curve"),
1356                                           _("Make selected segments curves"),
1357                                           "node_curve",
1358                                           secondarySize );
1359         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1360         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1361     }
1363     {
1364         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1365                                                       _("Show Handles"),
1366                                                       _("Show the Bezier handles of selected nodes"),
1367                                                       "nodes_show_handles",
1368                                                       Inkscape::ICON_SIZE_DECORATION );
1369         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1370         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1371         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1372     }
1374     {
1375         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1376                                                       _("Show Outline"),
1377                                                       _("Show the outline of the path"),
1378                                                       "nodes_show_helperpath",
1379                                                       Inkscape::ICON_SIZE_DECORATION );
1380         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1381         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1382         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_helperpath", 0 ) );
1383     }
1385     {
1386         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1387                                           _("Next path effect parameter"),
1388                                           _("Show next path effect parameter for editing"),
1389                                           "edit_next_parameter",
1390                                           Inkscape::ICON_SIZE_DECORATION );
1391         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1392         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1393         g_object_set_data( holder, "nodes_lpeedit", inky);
1394     }
1396     {
1397         InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1398                                           _("Edit clipping path"),
1399                                           _("Edit the clipping path of the object"),
1400                                           "nodeedit-clippath",
1401                                           Inkscape::ICON_SIZE_DECORATION );
1402         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1403         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1404         g_object_set_data( holder, "nodes_clippathedit", inky);
1405     }
1407     {
1408         InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1409                                           _("Edit mask path"),
1410                                           _("Edit the mask of the object"),
1411                                           "nodeedit-mask",
1412                                           Inkscape::ICON_SIZE_DECORATION );
1413         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1414         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1415         g_object_set_data( holder, "nodes_maskedit", inky);
1416     }
1418     /* X coord of selected node(s) */
1419     {
1420         EgeAdjustmentAction* eact = 0;
1421         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1422         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1423         eact = create_adjustment_action( "NodeXAction",
1424                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1425                                          "tools.nodes", "Xcoord", 0,
1426                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1427                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1428                                          labels, values, G_N_ELEMENTS(labels),
1429                                          sp_node_path_x_value_changed );
1430         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1431         g_object_set_data( holder, "nodes_x_action", eact );
1432         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1433         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1434     }
1436     /* Y coord of selected node(s) */
1437     {
1438         EgeAdjustmentAction* eact = 0;
1439         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1440         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1441         eact = create_adjustment_action( "NodeYAction",
1442                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1443                                          "tools.nodes", "Ycoord", 0,
1444                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1445                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1446                                          labels, values, G_N_ELEMENTS(labels),
1447                                          sp_node_path_y_value_changed );
1448         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1449         g_object_set_data( holder, "nodes_y_action", eact );
1450         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1451         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1452     }
1454     // add the units menu
1455     {
1456         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1457         gtk_action_group_add_action( mainActions, act );
1458     }
1461     sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1463     //watch selection
1464     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1466     sigc::connection *c_selection_changed =
1467         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1468                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1469     pool->add_connection ("selection-changed", c_selection_changed);
1471     sigc::connection *c_selection_modified =
1472         new sigc::connection (sp_desktop_selection (desktop)->connectModified
1473                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1474     pool->add_connection ("selection-modified", c_selection_modified);
1476     sigc::connection *c_subselection_changed =
1477         new sigc::connection (desktop->connectToolSubselectionChanged
1478                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1479     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1481     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1483     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1484 } // end of sp_node_toolbox_prep()
1487 //########################
1488 //##    Zoom Toolbox    ##
1489 //########################
1491 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1493     // no custom GtkAction setup needed
1494 } // end of sp_zoom_toolbox_prep()
1496 void
1497 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1499     toolbox_set_desktop(toolbox,
1500                         desktop,
1501                         setup_tool_toolbox,
1502                         update_tool_toolbox,
1503                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1504                                                                          "event_context_connection")));
1508 void
1509 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1511     toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1512                         desktop,
1513                         setup_aux_toolbox,
1514                         update_aux_toolbox,
1515                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1516                                                                          "event_context_connection")));
1519 void
1520 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1522     toolbox_set_desktop(toolbox,
1523                         desktop,
1524                         setup_commands_toolbox,
1525                         update_commands_toolbox,
1526                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1527                                                                          "event_context_connection")));
1530 static void
1531 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1533     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1534     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1536     if (old_desktop) {
1537         GList *children, *iter;
1539         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1540         for ( iter = children ; iter ; iter = iter->next ) {
1541             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1542         }
1543         g_list_free(children);
1544     }
1546     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1548     if (desktop) {
1549         gtk_widget_set_sensitive(toolbox, TRUE);
1550         setup_func(toolbox, desktop);
1551         update_func(desktop, desktop->event_context, toolbox);
1552         *conn = desktop->connectEventContextChanged
1553             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1554     } else {
1555         gtk_widget_set_sensitive(toolbox, FALSE);
1556     }
1558 } // end of toolbox_set_desktop()
1561 static void
1562 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1564     gchar const * descr =
1565         "<ui>"
1566         "  <toolbar name='ToolToolbar'>"
1567         "    <toolitem action='ToolSelector' />"
1568         "    <toolitem action='ToolNode' />"
1569         "    <toolitem action='ToolTweak' />"
1570         "    <toolitem action='ToolZoom' />"
1571         "    <toolitem action='ToolRect' />"
1572         "    <toolitem action='Tool3DBox' />"
1573         "    <toolitem action='ToolArc' />"
1574         "    <toolitem action='ToolStar' />"
1575         "    <toolitem action='ToolSpiral' />"
1576         "    <toolitem action='ToolPencil' />"
1577         "    <toolitem action='ToolPen' />"
1578         "    <toolitem action='ToolCalligraphic' />"
1579         "    <toolitem action='ToolEraser' />"
1580         "    <toolitem action='ToolLPETool' />"
1581         "    <toolitem action='ToolPaintBucket' />"
1582         "    <toolitem action='ToolText' />"
1583         "    <toolitem action='ToolConnector' />"
1584         "    <toolitem action='ToolGradient' />"
1585         "    <toolitem action='ToolDropper' />"
1586         "  </toolbar>"
1587         "</ui>";
1588     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1589     GtkUIManager* mgr = gtk_ui_manager_new();
1590     GError* errVal = 0;
1592     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1593     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1595     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" );
1596     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1597         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1598     }
1599     Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1600     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1602     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
1603     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1605     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1607     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1608     if ( child ) {
1609         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1610     }
1612     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1613 //     Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1617 static void
1618 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1620     gchar const *const tname = ( eventcontext
1621                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1622                                  : NULL );
1623     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1625     for (int i = 0 ; tools[i].type_name ; i++ ) {
1626         Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1627         if ( act ) {
1628             bool setActive = tname && !strcmp(tname, tools[i].type_name);
1629             Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1630             if ( verbAct ) {
1631                 verbAct->set_active(setActive);
1632             }
1633         }
1634     }
1637 static void
1638 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1640     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1641     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1642     GtkUIManager* mgr = gtk_ui_manager_new();
1643     GError* errVal = 0;
1644     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1645     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1647     std::map<std::string, GtkWidget*> dataHolders;
1649     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1650         if ( aux_toolboxes[i].prep_func ) {
1651             // converted to GtkActions and UIManager
1653             GtkWidget* kludge = gtk_toolbar_new();
1654             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1655             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1656             dataHolders[aux_toolboxes[i].type_name] = kludge;
1657             aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1658         } else {
1660             GtkWidget *sub_toolbox = 0;
1661             if (aux_toolboxes[i].create_func == NULL)
1662                 sub_toolbox = sp_empty_toolbox_new(desktop);
1663             else {
1664                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1665             }
1667             gtk_size_group_add_widget( grouper, sub_toolbox );
1669             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1670             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1672         }
1673     }
1675     // Second pass to create toolbars *after* all GtkActions are created
1676     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1677         if ( aux_toolboxes[i].prep_func ) {
1678             // converted to GtkActions and UIManager
1680             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1682             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1683             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1685             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1686             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1687             g_free( tmp );
1688             tmp = 0;
1690             Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1691             if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1692                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1693             }
1694             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1697             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1699             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1700                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1701                 swatch->setDesktop( desktop );
1702                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1703                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1704                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1705                 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 );
1706             }
1708             gtk_widget_show_all( holder );
1709             sp_set_font_size_smaller( holder );
1711             gtk_size_group_add_widget( grouper, holder );
1713             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1714             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1715         }
1716     }
1718     g_object_unref( G_OBJECT(grouper) );
1721 static void
1722 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1724     gchar const *tname = ( eventcontext
1725                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1726                            : NULL );
1727     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1728         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1729         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1730             gtk_widget_show_all(sub_toolbox);
1731             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1732         } else {
1733             gtk_widget_hide(sub_toolbox);
1734         }
1735     }
1738 static void
1739 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1741     gchar const * descr =
1742         "<ui>"
1743         "  <toolbar name='CommandsToolbar'>"
1744         "    <toolitem action='FileNew' />"
1745         "    <toolitem action='FileOpen' />"
1746         "    <toolitem action='FileSave' />"
1747         "    <toolitem action='FilePrint' />"
1748         "    <separator />"
1749         "    <toolitem action='FileImport' />"
1750         "    <toolitem action='FileExport' />"
1751         "    <separator />"
1752         "    <toolitem action='EditUndo' />"
1753         "    <toolitem action='EditRedo' />"
1754         "    <separator />"
1755         "    <toolitem action='EditCopy' />"
1756         "    <toolitem action='EditCut' />"
1757         "    <toolitem action='EditPaste' />"
1758         "    <separator />"
1759         "    <toolitem action='ZoomSelection' />"
1760         "    <toolitem action='ZoomDrawing' />"
1761         "    <toolitem action='ZoomPage' />"
1762         "    <separator />"
1763         "    <toolitem action='EditDuplicate' />"
1764         "    <toolitem action='EditClone' />"
1765         "    <toolitem action='EditUnlinkClone' />"
1766         "    <separator />"
1767         "    <toolitem action='SelectionGroup' />"
1768         "    <toolitem action='SelectionUnGroup' />"
1769         "    <separator />"
1770         "    <toolitem action='DialogFillStroke' />"
1771         "    <toolitem action='DialogText' />"
1772         "    <toolitem action='DialogXMLEditor' />"
1773         "    <toolitem action='DialogAlignDistribute' />"
1774         "    <separator />"
1775         "    <toolitem action='DialogPreferences' />"
1776         "    <toolitem action='DialogDocumentProperties' />"
1777         "  </toolbar>"
1778         "</ui>";
1779     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1782     GtkUIManager* mgr = gtk_ui_manager_new();
1783     GError* errVal = 0;
1785     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1786     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1788     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1789     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1790         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1791     }
1793     Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1794     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1796     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1797     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1800     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1802     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1803     if ( child ) {
1804         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1805     }
1807     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1810 static void
1811 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1815 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1817     gtk_widget_show(toolbox_toplevel);
1818     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1820     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1821     if (!shown_toolbox) {
1822         return;
1823     }
1824     gtk_widget_show(toolbox);
1826     gtk_widget_show_all(shown_toolbox);
1829 static GtkWidget *
1830 sp_empty_toolbox_new(SPDesktop *desktop)
1832     GtkWidget *tbl = gtk_toolbar_new();
1833     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1834     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1836     gtk_widget_show_all(tbl);
1837     sp_set_font_size_smaller (tbl);
1839     return tbl;
1842 #define MODE_LABEL_WIDTH 70
1844 //########################
1845 //##       Star         ##
1846 //########################
1848 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1850     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1852     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1853         // do not remember prefs if this call is initiated by an undo change, because undoing object
1854         // creation sets bogus values to its attributes before it is deleted
1855         prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1856     }
1858     // quit if run by the attr_changed listener
1859     if (g_object_get_data( dataKludge, "freeze" )) {
1860         return;
1861     }
1863     // in turn, prevent listener from responding
1864     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1866     bool modmade = false;
1868     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1869     GSList const *items = selection->itemList();
1870     for (; items != NULL; items = items->next) {
1871         if (SP_IS_STAR((SPItem *) items->data)) {
1872             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1873             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1874             sp_repr_set_svg_double(repr, "sodipodi:arg2",
1875                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1876                                     + M_PI / (gint)adj->value));
1877             SP_OBJECT((SPItem *) items->data)->updateRepr();
1878             modmade = true;
1879         }
1880     }
1881     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1882                                    _("Star: Change number of corners"));
1884     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1887 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1889     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1891     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1892         prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1893     }
1895     // quit if run by the attr_changed listener
1896     if (g_object_get_data( dataKludge, "freeze" )) {
1897         return;
1898     }
1900     // in turn, prevent listener from responding
1901     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1903     bool modmade = false;
1904     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1905     GSList const *items = selection->itemList();
1906     for (; items != NULL; items = items->next) {
1907         if (SP_IS_STAR((SPItem *) items->data)) {
1908             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1910             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1911             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1912             if (r2 < r1) {
1913                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1914             } else {
1915                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1916             }
1918             SP_OBJECT((SPItem *) items->data)->updateRepr();
1919             modmade = true;
1920         }
1921     }
1923     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1924                                    _("Star: Change spoke ratio"));
1926     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1929 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1931     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1932     bool flat = ege_select_one_action_get_active( act ) == 0;
1934     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1935         prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1936                                     flat ? "true" : "false" );
1937     }
1939     // quit if run by the attr_changed listener
1940     if (g_object_get_data( dataKludge, "freeze" )) {
1941         return;
1942     }
1944     // in turn, prevent listener from responding
1945     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1947     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1948     GSList const *items = selection->itemList();
1949     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1950     bool modmade = false;
1952     if ( prop_action ) {
1953         gtk_action_set_sensitive( prop_action, !flat );
1954     }
1956     for (; items != NULL; items = items->next) {
1957         if (SP_IS_STAR((SPItem *) items->data)) {
1958             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1959             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1960             SP_OBJECT((SPItem *) items->data)->updateRepr();
1961             modmade = true;
1962         }
1963     }
1965     if (modmade) {
1966         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1967                          flat ? _("Make polygon") : _("Make star"));
1968     }
1970     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1973 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1975     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1977     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1978         prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1979     }
1981     // quit if run by the attr_changed listener
1982     if (g_object_get_data( dataKludge, "freeze" )) {
1983         return;
1984     }
1986     // in turn, prevent listener from responding
1987     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1989     bool modmade = false;
1991     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1992     GSList const *items = selection->itemList();
1993     for (; items != NULL; items = items->next) {
1994         if (SP_IS_STAR((SPItem *) items->data)) {
1995             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1996             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1997             SP_OBJECT(items->data)->updateRepr();
1998             modmade = true;
1999         }
2000     }
2001     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2002                                    _("Star: Change rounding"));
2004     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2007 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2009     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2011     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2012         prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
2013     }
2015     // quit if run by the attr_changed listener
2016     if (g_object_get_data( dataKludge, "freeze" )) {
2017         return;
2018     }
2020     // in turn, prevent listener from responding
2021     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2023     bool modmade = false;
2025     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2026     GSList const *items = selection->itemList();
2027     for (; items != NULL; items = items->next) {
2028         if (SP_IS_STAR((SPItem *) items->data)) {
2029             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2030             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2031             SP_OBJECT(items->data)->updateRepr();
2032             modmade = true;
2033         }
2034     }
2035     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2036                                    _("Star: Change randomization"));
2038     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2042 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2043                                        gchar const */*old_value*/, gchar const */*new_value*/,
2044                                        bool /*is_interactive*/, gpointer data)
2046     GtkWidget *tbl = GTK_WIDGET(data);
2048     // quit if run by the _changed callbacks
2049     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2050         return;
2051     }
2053     // in turn, prevent callbacks from responding
2054     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2056     GtkAdjustment *adj = 0;
2058     gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2059     bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2061     if (!strcmp(name, "inkscape:randomized")) {
2062         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2063         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2064     } else if (!strcmp(name, "inkscape:rounded")) {
2065         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2066         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2067     } else if (!strcmp(name, "inkscape:flatsided")) {
2068         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2069         char const *flatsides = repr->attribute("inkscape:flatsided");
2070         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2071         if ( flatsides && !strcmp(flatsides,"false") ) {
2072             ege_select_one_action_set_active( flat_action, 1 );
2073             gtk_action_set_sensitive( prop_action, TRUE );
2074         } else {
2075             ege_select_one_action_set_active( flat_action, 0 );
2076             gtk_action_set_sensitive( prop_action, FALSE );
2077         }
2078     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2079         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2080         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2081         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2082         if (r2 < r1) {
2083             gtk_adjustment_set_value(adj, r2/r1);
2084         } else {
2085             gtk_adjustment_set_value(adj, r1/r2);
2086         }
2087     } else if (!strcmp(name, "sodipodi:sides")) {
2088         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2089         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2090     }
2092     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2096 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2098     NULL, /* child_added */
2099     NULL, /* child_removed */
2100     star_tb_event_attr_changed,
2101     NULL, /* content_changed */
2102     NULL  /* order_changed */
2103 };
2106 /**
2107  *  \param selection Should not be NULL.
2108  */
2109 static void
2110 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2112     int n_selected = 0;
2113     Inkscape::XML::Node *repr = NULL;
2115     purge_repr_listener( tbl, tbl );
2117     for (GSList const *items = selection->itemList();
2118          items != NULL;
2119          items = items->next)
2120     {
2121         if (SP_IS_STAR((SPItem *) items->data)) {
2122             n_selected++;
2123             repr = SP_OBJECT_REPR((SPItem *) items->data);
2124         }
2125     }
2127     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2129     if (n_selected == 0) {
2130         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2131     } else if (n_selected == 1) {
2132         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2134         if (repr) {
2135             g_object_set_data( tbl, "repr", repr );
2136             Inkscape::GC::anchor(repr);
2137             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2138             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2139         }
2140     } else {
2141         // FIXME: implement averaging of all parameters for multiple selected stars
2142         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2143         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2144     }
2148 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2150     // FIXME: in this and all other _default functions, set some flag telling the value_changed
2151     // callbacks to lump all the changes for all selected objects in one undo step
2153     GtkAdjustment *adj = 0;
2155     // fixme: make settable in prefs!
2156     gint mag = 5;
2157     gdouble prop = 0.5;
2158     gboolean flat = FALSE;
2159     gdouble randomized = 0;
2160     gdouble rounded = 0;
2162     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2163     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2165     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2166     gtk_action_set_sensitive( sb2, !flat );
2168     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2169     gtk_adjustment_set_value(adj, mag);
2170     gtk_adjustment_value_changed(adj);
2172     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2173     gtk_adjustment_set_value(adj, prop);
2174     gtk_adjustment_value_changed(adj);
2176     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2177     gtk_adjustment_set_value(adj, rounded);
2178     gtk_adjustment_value_changed(adj);
2180     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2181     gtk_adjustment_set_value(adj, randomized);
2182     gtk_adjustment_value_changed(adj);
2186 void
2187 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2189     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2190     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2191     GtkWidget *l = gtk_label_new(NULL);
2192     gtk_label_set_markup(GTK_LABEL(l), title);
2193     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2194     if ( GTK_IS_TOOLBAR(tbl) ) {
2195         gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2196     } else {
2197         gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2198     }
2199     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2203 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2205     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2207     {
2208         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2209         ege_output_action_set_use_markup( act, TRUE );
2210         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2211         g_object_set_data( holder, "mode_action", act );
2212     }
2214     {
2215         EgeAdjustmentAction* eact = 0;
2216         gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2217         bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2219         /* Flatsided checkbox */
2220         {
2221             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2223             GtkTreeIter iter;
2224             gtk_list_store_append( model, &iter );
2225             gtk_list_store_set( model, &iter,
2226                                 0, _("Polygon"),
2227                                 1, _("Regular polygon (with one handle) instead of a star"),
2228                                 2, "star_flat",
2229                                 -1 );
2231             gtk_list_store_append( model, &iter );
2232             gtk_list_store_set( model, &iter,
2233                                 0, _("Star"),
2234                                 1, _("Star instead of a regular polygon (with one handle)"),
2235                                 2, "star_angled",
2236                                 -1 );
2238             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2239             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2240             g_object_set_data( holder, "flat_action", act );
2242             ege_select_one_action_set_appearance( act, "full" );
2243             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2244             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2245             ege_select_one_action_set_icon_column( act, 2 );
2246             ege_select_one_action_set_icon_size( act, secondarySize );
2247             ege_select_one_action_set_tooltip_column( act, 1  );
2249             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2250             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2251         }
2253         /* Magnitude */
2254         {
2255         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2256         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2257         eact = create_adjustment_action( "MagnitudeAction",
2258                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2259                                          "tools.shapes.star", "magnitude", 3,
2260                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2261                                          3, 1024, 1, 5,
2262                                          labels, values, G_N_ELEMENTS(labels),
2263                                          sp_stb_magnitude_value_changed,
2264                                          1.0, 0 );
2265         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2266         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2267         }
2269         /* Spoke ratio */
2270         {
2271         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2272         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2273         eact = create_adjustment_action( "SpokeAction",
2274                                          _("Spoke ratio"), _("Spoke ratio:"),
2275                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2276                                          // Base radius is the same for the closest handle.
2277                                          _("Base radius to tip radius ratio"),
2278                                          "tools.shapes.star", "proportion", 0.5,
2279                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2280                                          0.01, 1.0, 0.01, 0.1,
2281                                          labels, values, G_N_ELEMENTS(labels),
2282                                          sp_stb_proportion_value_changed );
2283         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2284         g_object_set_data( holder, "prop_action", eact );
2285         }
2287         if ( !isFlatSided ) {
2288             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2289         } else {
2290             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2291         }
2293         /* Roundedness */
2294         {
2295         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2296         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2297         eact = create_adjustment_action( "RoundednessAction",
2298                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2299                                          "tools.shapes.star", "rounded", 0.0,
2300                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2301                                          -10.0, 10.0, 0.01, 0.1,
2302                                          labels, values, G_N_ELEMENTS(labels),
2303                                          sp_stb_rounded_value_changed );
2304         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2305         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2306         }
2308         /* Randomization */
2309         {
2310         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2311         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2312         eact = create_adjustment_action( "RandomizationAction",
2313                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2314                                          "tools.shapes.star", "randomized", 0.0,
2315                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2316                                          -10.0, 10.0, 0.001, 0.01,
2317                                          labels, values, G_N_ELEMENTS(labels),
2318                                          sp_stb_randomized_value_changed, 0.1, 3 );
2319         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2320         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2321         }
2322     }
2324     {
2325         /* Reset */
2326         {
2327             GtkAction* act = gtk_action_new( "StarResetAction",
2328                                              _("Defaults"),
2329                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2330                                              GTK_STOCK_CLEAR );
2331             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2332             gtk_action_group_add_action( mainActions, act );
2333             gtk_action_set_sensitive( act, TRUE );
2334         }
2335     }
2337     sigc::connection *connection = new sigc::connection(
2338         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2339         );
2340     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2341     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2345 //########################
2346 //##       Rect         ##
2347 //########################
2349 static void sp_rtb_sensitivize( GObject *tbl )
2351     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2352     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2353     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2355     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2356         gtk_action_set_sensitive( not_rounded, FALSE );
2357     } else {
2358         gtk_action_set_sensitive( not_rounded, TRUE );
2359     }
2363 static void
2364 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2365                           void (*setter)(SPRect *, gdouble))
2367     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2369     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2370     SPUnit const *unit = tracker->getActiveUnit();
2372     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2373         prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2374     }
2376     // quit if run by the attr_changed listener
2377     if (g_object_get_data( tbl, "freeze" )) {
2378         return;
2379     }
2381     // in turn, prevent listener from responding
2382     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2384     bool modmade = false;
2385     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2386     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2387         if (SP_IS_RECT(items->data)) {
2388             if (adj->value != 0) {
2389                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2390             } else {
2391                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2392             }
2393             modmade = true;
2394         }
2395     }
2397     sp_rtb_sensitivize( tbl );
2399     if (modmade) {
2400         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2401                                    _("Change rectangle"));
2402     }
2404     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2407 static void
2408 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2410     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2413 static void
2414 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2416     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2419 static void
2420 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2422     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2425 static void
2426 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2428     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2433 static void
2434 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2436     GtkAdjustment *adj = 0;
2438     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2439     gtk_adjustment_set_value(adj, 0.0);
2440     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2441     gtk_adjustment_value_changed(adj);
2443     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2444     gtk_adjustment_set_value(adj, 0.0);
2445     gtk_adjustment_value_changed(adj);
2447     sp_rtb_sensitivize( obj );
2450 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2451                                        gchar const */*old_value*/, gchar const */*new_value*/,
2452                                        bool /*is_interactive*/, gpointer data)
2454     GObject *tbl = G_OBJECT(data);
2456     // quit if run by the _changed callbacks
2457     if (g_object_get_data( tbl, "freeze" )) {
2458         return;
2459     }
2461     // in turn, prevent callbacks from responding
2462     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2464     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2465     SPUnit const *unit = tracker->getActiveUnit();
2467     gpointer item = g_object_get_data( tbl, "item" );
2468     if (item && SP_IS_RECT(item)) {
2469         {
2470             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2471             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2472             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2473         }
2475         {
2476             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2477             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2478             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2479         }
2481         {
2482             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2483             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2484             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2485         }
2487         {
2488             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2489             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2490             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2491         }
2492     }
2494     sp_rtb_sensitivize( tbl );
2496     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2500 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2501     NULL, /* child_added */
2502     NULL, /* child_removed */
2503     rect_tb_event_attr_changed,
2504     NULL, /* content_changed */
2505     NULL  /* order_changed */
2506 };
2508 /**
2509  *  \param selection should not be NULL.
2510  */
2511 static void
2512 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2514     int n_selected = 0;
2515     Inkscape::XML::Node *repr = NULL;
2516     SPItem *item = NULL;
2518     if ( g_object_get_data( tbl, "repr" ) ) {
2519         g_object_set_data( tbl, "item", NULL );
2520     }
2521     purge_repr_listener( tbl, tbl );
2523     for (GSList const *items = selection->itemList();
2524          items != NULL;
2525          items = items->next) {
2526         if (SP_IS_RECT((SPItem *) items->data)) {
2527             n_selected++;
2528             item = (SPItem *) items->data;
2529             repr = SP_OBJECT_REPR(item);
2530         }
2531     }
2533     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2535     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2537     if (n_selected == 0) {
2538         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2540         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2541         gtk_action_set_sensitive(w, FALSE);
2542         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2543         gtk_action_set_sensitive(h, FALSE);
2545     } else if (n_selected == 1) {
2546         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2547         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2549         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2550         gtk_action_set_sensitive(w, TRUE);
2551         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2552         gtk_action_set_sensitive(h, TRUE);
2554         if (repr) {
2555             g_object_set_data( tbl, "repr", repr );
2556             g_object_set_data( tbl, "item", item );
2557             Inkscape::GC::anchor(repr);
2558             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2559             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2560         }
2561     } else {
2562         // FIXME: implement averaging of all parameters for multiple selected
2563         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2564         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2565         sp_rtb_sensitivize( tbl );
2566     }
2570 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2572     EgeAdjustmentAction* eact = 0;
2573     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2575     {
2576         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2577         ege_output_action_set_use_markup( act, TRUE );
2578         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2579         g_object_set_data( holder, "mode_action", act );
2580     }
2582     // rx/ry units menu: create
2583     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2584     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2585     // fixme: add % meaning per cent of the width/height
2586     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2587     g_object_set_data( holder, "tracker", tracker );
2589     /* W */
2590     {
2591         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2592         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2593         eact = create_adjustment_action( "RectWidthAction",
2594                                          _("Width"), _("W:"), _("Width of rectangle"),
2595                                          "tools.shapes.rect", "width", 0,
2596                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2597                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2598                                          labels, values, G_N_ELEMENTS(labels),
2599                                          sp_rtb_width_value_changed );
2600         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2601         g_object_set_data( holder, "width_action", eact );
2602         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2603         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2604     }
2606     /* H */
2607     {
2608         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2609         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2610         eact = create_adjustment_action( "RectHeightAction",
2611                                          _("Height"), _("H:"), _("Height of rectangle"),
2612                                          "tools.shapes.rect", "height", 0,
2613                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2614                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2615                                          labels, values, G_N_ELEMENTS(labels),
2616                                          sp_rtb_height_value_changed );
2617         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2618         g_object_set_data( holder, "height_action", eact );
2619         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2620         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2621     }
2623     /* rx */
2624     {
2625         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2626         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2627         eact = create_adjustment_action( "RadiusXAction",
2628                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2629                                          "tools.shapes.rect", "rx", 0,
2630                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2631                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2632                                          labels, values, G_N_ELEMENTS(labels),
2633                                          sp_rtb_rx_value_changed);
2634         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2635         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2636     }
2638     /* ry */
2639     {
2640         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2641         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2642         eact = create_adjustment_action( "RadiusYAction",
2643                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2644                                          "tools.shapes.rect", "ry", 0,
2645                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2646                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2647                                          labels, values, G_N_ELEMENTS(labels),
2648                                          sp_rtb_ry_value_changed);
2649         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2650         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2651     }
2653     // add the units menu
2654     {
2655         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2656         gtk_action_group_add_action( mainActions, act );
2657     }
2659     /* Reset */
2660     {
2661         InkAction* inky = ink_action_new( "RectResetAction",
2662                                           _("Not rounded"),
2663                                           _("Make corners sharp"),
2664                                           "squared_corner",
2665                                           secondarySize );
2666         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2667         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2668         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2669         g_object_set_data( holder, "not_rounded", inky );
2670     }
2672     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2673     sp_rtb_sensitivize( holder );
2675     sigc::connection *connection = new sigc::connection(
2676         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2677         );
2678     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2679     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2682 //########################
2683 //##       3D Box       ##
2684 //########################
2686 // normalize angle so that it lies in the interval [0,360]
2687 static double box3d_normalize_angle (double a) {
2688     double angle = a + ((int) (a/360.0))*360;
2689     if (angle < 0) {
2690         angle += 360.0;
2691     }
2692     return angle;
2695 static void
2696 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2697                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2698     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2699     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2700     //       are reset).
2701     bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2703     if (is_infinite) {
2704         gtk_toggle_action_set_active(tact, TRUE);
2705         gtk_action_set_sensitive(act, TRUE);
2707         double angle = persp3d_get_infinite_angle(persp, axis);
2708         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2709             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2710         }
2711     } else {
2712         gtk_toggle_action_set_active(tact, FALSE);
2713         gtk_action_set_sensitive(act, FALSE);
2714     }
2717 static void
2718 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2719     if (!persp_repr) {
2720         g_print ("No perspective given to box3d_resync_toolbar().\n");
2721         return;
2722     }
2724     GtkWidget *tbl = GTK_WIDGET(data);
2725     GtkAdjustment *adj = 0;
2726     GtkAction *act = 0;
2727     GtkToggleAction *tact = 0;
2728     Persp3D *persp = persp3d_get_from_repr(persp_repr);
2729     {
2730         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2731         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2732         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2734         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2735     }
2736     {
2737         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2738         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2739         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2741         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2742     }
2743     {
2744         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2745         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2746         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2748         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2749     }
2752 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2753                                                   gchar const */*old_value*/, gchar const */*new_value*/,
2754                                                   bool /*is_interactive*/, gpointer data)
2756     GtkWidget *tbl = GTK_WIDGET(data);
2758     // quit if run by the attr_changed listener
2759     // note: it used to work without the differently called freeze_ attributes (here and in
2760     //       box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2761     if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2762         return;
2763     }
2765     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2766     // sp_document_maybe_done() when the document is undo insensitive)
2767     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2769     // TODO: Only update the appropriate part of the toolbar
2770 //    if (!strcmp(name, "inkscape:vp_z")) {
2771         box3d_resync_toolbar(repr, G_OBJECT(tbl));
2772 //    }
2774     Persp3D *persp = persp3d_get_from_repr(repr);
2775     persp3d_update_box_reprs(persp);
2777     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2780 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2782     NULL, /* child_added */
2783     NULL, /* child_removed */
2784     box3d_persp_tb_event_attr_changed,
2785     NULL, /* content_changed */
2786     NULL  /* order_changed */
2787 };
2789 /**
2790  *  \param selection Should not be NULL.
2791  */
2792 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2793 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2794 static void
2795 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2797     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2798     // disable the angle entry fields for this direction (otherwise entering a value in them should only
2799     // update the perspectives with infinite VPs and leave the other ones untouched).
2801     Inkscape::XML::Node *persp_repr = NULL;
2802     purge_repr_listener(tbl, tbl);
2804     SPItem *item = selection->singleItem();
2805     if (item && SP_IS_BOX3D(item)) {
2806         // FIXME: Also deal with multiple selected boxes
2807         SPBox3D *box = SP_BOX3D(item);
2808         Persp3D *persp = box3d_get_perspective(box);
2809         persp_repr = SP_OBJECT_REPR(persp);
2810         if (persp_repr) {
2811             g_object_set_data(tbl, "repr", persp_repr);
2812             Inkscape::GC::anchor(persp_repr);
2813             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2814             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2815         }
2817         inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2818         prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2820         box3d_resync_toolbar(persp_repr, tbl);
2821     }
2824 static void
2825 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2827     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2828     SPDocument *document = sp_desktop_document(desktop);
2830     // quit if run by the attr_changed listener
2831     // note: it used to work without the differently called freeze_ attributes (here and in
2832     //       box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2833     if (g_object_get_data( dataKludge, "freeze_attr" )) {
2834         return;
2835     }
2837     // in turn, prevent listener from responding
2838     g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2840     //Persp3D *persp = document->current_persp3d;
2841     std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
2842     if (sel_persps.empty()) {
2843         // this can happen when the document is created; we silently ignore it
2844         return;
2845     }
2846     Persp3D *persp = sel_persps.front();
2848     persp->tmat.set_infinite_direction (axis, adj->value);
2849     SP_OBJECT(persp)->updateRepr();
2851     // TODO: use the correct axis here, too
2852     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2854     g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2858 static void
2859 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2861     box3d_angle_value_changed(adj, dataKludge, Proj::X);
2864 static void
2865 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2867     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2870 static void
2871 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2873     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2877 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2879     // TODO: Take all selected perspectives into account
2880     std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
2881     if (sel_persps.empty()) {
2882         // this can happen when the document is created; we silently ignore it
2883         return;
2884     }
2885     Persp3D *persp = sel_persps.front();
2887     bool set_infinite = gtk_toggle_action_get_active(act);
2888     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2891 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2893     box3d_vp_state_changed(act, box3d_angle, Proj::X);
2896 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2898     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2901 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2903     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2906 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2908     EgeAdjustmentAction* eact = 0;
2909     SPDocument *document = sp_desktop_document (desktop);
2910     Persp3D *persp = document->current_persp3d;
2912     EgeAdjustmentAction* box3d_angle_x = 0;
2913     EgeAdjustmentAction* box3d_angle_y = 0;
2914     EgeAdjustmentAction* box3d_angle_z = 0;
2916     /* Angle X */
2917     {
2918         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2919         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2920         eact = create_adjustment_action( "3DBoxAngleXAction",
2921                                          _("Angle in X direction"), _("Angle X:"),
2922                                          // Translators: PL is short for 'perspective line'
2923                                          _("Angle of PLs in X direction"),
2924                                          "tools.shapes.3dbox", "box3d_angle_x", 30,
2925                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2926                                          -360.0, 360.0, 1.0, 10.0,
2927                                          labels, values, G_N_ELEMENTS(labels),
2928                                          box3d_angle_x_value_changed );
2929         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2930         g_object_set_data( holder, "box3d_angle_x_action", eact );
2931         box3d_angle_x = eact;
2932     }
2934     if (!persp3d_VP_is_finite(persp, Proj::X)) {
2935         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2936     } else {
2937         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2938     }
2941     /* VP X state */
2942     {
2943         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2944                                                       // Translators: VP is short for 'vanishing point'
2945                                                       _("State of VP in X direction"),
2946                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2947                                                       "toggle_vp_x",
2948                                                       Inkscape::ICON_SIZE_DECORATION );
2949         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2950         g_object_set_data( holder, "box3d_vp_x_state_action", act );
2951         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2952         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2953         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2954     }
2956     /* Angle Y */
2957     {
2958         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2959         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2960         eact = create_adjustment_action( "3DBoxAngleYAction",
2961                                          _("Angle in Y direction"), _("Angle Y:"),
2962                                          // Translators: PL is short for 'perspective line'
2963                                          _("Angle of PLs in Y direction"),
2964                                          "tools.shapes.3dbox", "box3d_angle_y", 30,
2965                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2966                                          -360.0, 360.0, 1.0, 10.0,
2967                                          labels, values, G_N_ELEMENTS(labels),
2968                                          box3d_angle_y_value_changed );
2969         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2970         g_object_set_data( holder, "box3d_angle_y_action", eact );
2971         box3d_angle_y = eact;
2972     }
2974     if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2975         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2976     } else {
2977         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2978     }
2980     /* VP Y state */
2981     {
2982         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2983                                                       // Translators: VP is short for 'vanishing point'
2984                                                       _("State of VP in Y direction"),
2985                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2986                                                       "toggle_vp_y",
2987                                                       Inkscape::ICON_SIZE_DECORATION );
2988         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2989         g_object_set_data( holder, "box3d_vp_y_state_action", act );
2990         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2991         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2992         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2993     }
2995     /* Angle Z */
2996     {
2997         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2998         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2999         eact = create_adjustment_action( "3DBoxAngleZAction",
3000                                          _("Angle in Z direction"), _("Angle Z:"),
3001                                          // Translators: PL is short for 'perspective line'
3002                                          _("Angle of PLs in Z direction"),
3003                                          "tools.shapes.3dbox", "box3d_angle_z", 30,
3004                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3005                                          -360.0, 360.0, 1.0, 10.0,
3006                                          labels, values, G_N_ELEMENTS(labels),
3007                                          box3d_angle_z_value_changed );
3008         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3009         g_object_set_data( holder, "box3d_angle_z_action", eact );
3010         box3d_angle_z = eact;
3011     }
3013     if (!persp3d_VP_is_finite(persp, Proj::Z)) {
3014         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3015     } else {
3016         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3017     }
3019     /* VP Z state */
3020     {
3021         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3022                                                       // Translators: VP is short for 'vanishing point'
3023                                                       _("State of VP in Z direction"),
3024                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3025                                                       "toggle_vp_z",
3026                                                       Inkscape::ICON_SIZE_DECORATION );
3027         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3028         g_object_set_data( holder, "box3d_vp_z_state_action", act );
3029         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3030         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3031         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3032     }
3034     sigc::connection *connection = new sigc::connection(
3035         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3036        );
3037     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3038     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3041 //########################
3042 //##       Spiral       ##
3043 //########################
3045 static void
3046 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
3048     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3050     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3051         prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
3052     }
3054     // quit if run by the attr_changed listener
3055     if (g_object_get_data( tbl, "freeze" )) {
3056         return;
3057     }
3059     // in turn, prevent listener from responding
3060     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3062     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3064     bool modmade = false;
3065     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3066          items != NULL;
3067          items = items->next)
3068     {
3069         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3070             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3071             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3072             SP_OBJECT((SPItem *) items->data)->updateRepr();
3073             modmade = true;
3074         }
3075     }
3077     g_free(namespaced_name);
3079     if (modmade) {
3080         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3081                                    _("Change spiral"));
3082     }
3084     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3087 static void
3088 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3090     sp_spl_tb_value_changed(adj, tbl, "revolution");
3093 static void
3094 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3096     sp_spl_tb_value_changed(adj, tbl, "expansion");
3099 static void
3100 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3102     sp_spl_tb_value_changed(adj, tbl, "t0");
3105 static void
3106 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3108     GtkWidget *tbl = GTK_WIDGET(obj);
3110     GtkAdjustment *adj;
3112     // fixme: make settable
3113     gdouble rev = 5;
3114     gdouble exp = 1.0;
3115     gdouble t0 = 0.0;
3117     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3118     gtk_adjustment_set_value(adj, rev);
3119     gtk_adjustment_value_changed(adj);
3121     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3122     gtk_adjustment_set_value(adj, exp);
3123     gtk_adjustment_value_changed(adj);
3125     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3126     gtk_adjustment_set_value(adj, t0);
3127     gtk_adjustment_value_changed(adj);
3129     spinbutton_defocus(GTK_OBJECT(tbl));
3133 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3134                                          gchar const */*old_value*/, gchar const */*new_value*/,
3135                                          bool /*is_interactive*/, gpointer data)
3137     GtkWidget *tbl = GTK_WIDGET(data);
3139     // quit if run by the _changed callbacks
3140     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3141         return;
3142     }
3144     // in turn, prevent callbacks from responding
3145     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3147     GtkAdjustment *adj;
3148     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3149     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3151     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3152     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3154     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3155     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3157     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3161 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3162     NULL, /* child_added */
3163     NULL, /* child_removed */
3164     spiral_tb_event_attr_changed,
3165     NULL, /* content_changed */
3166     NULL  /* order_changed */
3167 };
3169 static void
3170 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3172     int n_selected = 0;
3173     Inkscape::XML::Node *repr = NULL;
3175     purge_repr_listener( tbl, tbl );
3177     for (GSList const *items = selection->itemList();
3178          items != NULL;
3179          items = items->next)
3180     {
3181         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3182             n_selected++;
3183             repr = SP_OBJECT_REPR((SPItem *) items->data);
3184         }
3185     }
3187     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3189     if (n_selected == 0) {
3190         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3191     } else if (n_selected == 1) {
3192         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3194         if (repr) {
3195             g_object_set_data( tbl, "repr", repr );
3196             Inkscape::GC::anchor(repr);
3197             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3198             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3199         }
3200     } else {
3201         // FIXME: implement averaging of all parameters for multiple selected
3202         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3203         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3204     }
3208 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3210     EgeAdjustmentAction* eact = 0;
3211     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3213     {
3214         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3215         ege_output_action_set_use_markup( act, TRUE );
3216         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3217         g_object_set_data( holder, "mode_action", act );
3218     }
3220     /* Revolution */
3221     {
3222         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3223         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3224         eact = create_adjustment_action( "SpiralRevolutionAction",
3225                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3226                                          "tools.shapes.spiral", "revolution", 3.0,
3227                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3228                                          0.01, 1024.0, 0.1, 1.0,
3229                                          labels, values, G_N_ELEMENTS(labels),
3230                                          sp_spl_tb_revolution_value_changed, 1, 2);
3231         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3232     }
3234     /* Expansion */
3235     {
3236         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3237         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3238         eact = create_adjustment_action( "SpiralExpansionAction",
3239                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3240                                          "tools.shapes.spiral", "expansion", 1.0,
3241                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3242                                          0.0, 1000.0, 0.01, 1.0,
3243                                          labels, values, G_N_ELEMENTS(labels),
3244                                          sp_spl_tb_expansion_value_changed);
3245         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3246     }
3248     /* T0 */
3249     {
3250         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3251         gdouble values[] = {0, 0.5, 0.9};
3252         eact = create_adjustment_action( "SpiralT0Action",
3253                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3254                                          "tools.shapes.spiral", "t0", 0.0,
3255                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3256                                          0.0, 0.999, 0.01, 1.0,
3257                                          labels, values, G_N_ELEMENTS(labels),
3258                                          sp_spl_tb_t0_value_changed);
3259         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3260     }
3262     /* Reset */
3263     {
3264         InkAction* inky = ink_action_new( "SpiralResetAction",
3265                                           _("Defaults"),
3266                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3267                                           GTK_STOCK_CLEAR,
3268                                           secondarySize );
3269         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3270         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3271     }
3274     sigc::connection *connection = new sigc::connection(
3275         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3276         );
3277     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3278     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3281 //########################
3282 //##     Pen/Pencil     ##
3283 //########################
3285 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3286 static char const *
3287 freehand_tool_name(GObject *dataKludge)
3289     SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3290     return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3291              ? "tools.freehand.pen"
3292              : "tools.freehand.pencil" );
3295 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3297     gint mode = ege_select_one_action_get_active(act);
3299     prefs_set_int_attribute(freehand_tool_name(tbl), "freehand-mode", mode);
3301     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3303     // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3304     // preparatory work here
3305     if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3306         SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3307         sp_pen_context_set_polyline_mode(pc);
3308     }
3311 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3313     /* Freehand mode toggle buttons */
3314     {
3315         guint freehandMode = prefs_get_int_attribute(tool_is_pencil ? "tools.freehand.pencil" : "tools.freehand.pen", "freehand-mode", 0);
3316         Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3318         {
3319             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3321             GtkTreeIter iter;
3322             gtk_list_store_append( model, &iter );
3323             gtk_list_store_set( model, &iter,
3324                                 0, _("Bezier"),
3325                                 1, _("Create regular Bezier path"),
3326                                 2, "bezier_mode",
3327                                 -1 );
3329             gtk_list_store_append( model, &iter );
3330             gtk_list_store_set( model, &iter,
3331                                 0, _("Spiro"),
3332                                 1, _("Create Spiro path"),
3333                                 2, "spiro_splines_mode",
3334                                 -1 );
3336             if (!tool_is_pencil) {
3337                 gtk_list_store_append( model, &iter );
3338                 gtk_list_store_set( model, &iter,
3339                                     0, _("Zigzag"),
3340                                     1, _("Create a sequence of straight line segments"),
3341                                     2, "polylines_mode",
3342                                     -1 );
3344                 gtk_list_store_append( model, &iter );
3345                 gtk_list_store_set( model, &iter,
3346                                     0, _("Paraxial"),
3347                                     1, _("Create a sequence of paraxial line segments"),
3348                                     2, "paraxial_lines_mode",
3349                                     -1 );
3350             }
3352             EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3353                                                                 "FreehandModeActionPencil" :
3354                                                                 "FreehandModeActionPen",
3355                                                                 (_("Mode:")), ("Mode"), NULL, GTK_TREE_MODEL(model) );
3356             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3358             ege_select_one_action_set_appearance( act, "full" );
3359             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3360             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3361             ege_select_one_action_set_icon_column( act, 2 );
3362             ege_select_one_action_set_icon_size( act, secondarySize );
3363             ege_select_one_action_set_tooltip_column( act, 1  );
3365             ege_select_one_action_set_active( act, freehandMode);
3366             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3367         }
3368     }
3371 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3372     gint shape = ege_select_one_action_get_active( act );
3373     prefs_set_int_attribute(freehand_tool_name(dataKludge), "shape", shape);
3376 /**
3377  * \brief Generate the list of freehand advanced shape option entries.
3378  */
3379 GList * freehand_shape_dropdown_items_list() {
3380     GList *glist = NULL;
3382     glist = g_list_append (glist, _("None"));
3383     glist = g_list_append (glist, _("Triangle in"));
3384     glist = g_list_append (glist, _("Triangle out"));
3385     glist = g_list_append (glist, _("Ellipse"));
3386     glist = g_list_append (glist, _("From clipboard"));
3388     return glist;
3391 static void
3392 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3393     /*advanced shape options */
3394     {
3395         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3397         GList* items = 0;
3398         gint count = 0;
3399         for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3400         {
3401             GtkTreeIter iter;
3402             gtk_list_store_append( model, &iter );
3403             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3404             count++;
3405         }
3406         g_list_free( items );
3407         items = 0;
3408         EgeSelectOneAction* act1 = ege_select_one_action_new(
3409             tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3410             _("Shape:"), ("Shape"), NULL, GTK_TREE_MODEL(model));
3411         g_object_set( act1, "short_label", _("Shape:"), NULL );
3412         ege_select_one_action_set_appearance( act1, "compact" );
3413         ege_select_one_action_set_active( act1, prefs_get_int_attribute(tool_is_pencil ? "tools.freehand.pencil" : "tools.freehand.pen", "shape", 0) );
3414         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3415         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3416         g_object_set_data( holder, "shape_action", act1 );
3417     }
3420 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3422     sp_add_freehand_mode_toggle(mainActions, holder, false);
3423     freehand_add_advanced_shape_options(mainActions, holder, false);
3427 static void
3428 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3430     GtkWidget *tbl = GTK_WIDGET(obj);
3432     GtkAdjustment *adj;
3434     // fixme: make settable
3435     gdouble tolerance = 4;
3437     adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3438     gtk_adjustment_set_value(adj, tolerance);
3439     gtk_adjustment_value_changed(adj);
3441     spinbutton_defocus(GTK_OBJECT(tbl));
3444 static void
3445 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3448     // quit if run by the attr_changed listener
3449     if (g_object_get_data( tbl, "freeze" )) {
3450         return;
3451     }
3452     // in turn, prevent listener from responding
3453     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3454     prefs_set_double_attribute("tools.freehand.pencil",
3455                                "tolerance", adj->value);
3456     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3462 static void
3463 sp_pencil_tb_tolerance_value_changed_external(Inkscape::XML::Node */*repr*/,
3464                                               const gchar */*key*/,
3465                                               const gchar */*oldval*/,
3466                                               const gchar */*newval*/,
3467                                               bool /*is_interactive*/,
3468                                               void * data)
3470     GObject* tbl = G_OBJECT(data);
3471     if (g_object_get_data( tbl, "freeze" )) {
3472         return;
3473     }
3475     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3477     GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl,
3478                                                             "tolerance");
3480     double v = prefs_get_double_attribute("tools.freehand.pencil",
3481                                             "tolerance", adj->value);
3482     gtk_adjustment_set_value(adj, v);
3483     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3487 static Inkscape::XML::NodeEventVector pencil_node_events =
3489     NULL,
3490     NULL,
3491     sp_pencil_tb_tolerance_value_changed_external,
3492     NULL,
3493     NULL,
3494 };
3497 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3499     sp_add_freehand_mode_toggle(mainActions, holder, true);
3501     EgeAdjustmentAction* eact = 0;
3503     /* Tolerance */
3504     {
3505         gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
3506         gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
3507         eact = create_adjustment_action( "PencilToleranceAction",
3508                                          _("Smoothing:"), _("Smoothing: "),
3509                  _("How much smoothing (simplifying) is applied to the line"),
3510                                          "tools.freehand.pencil", "tolerance",
3511                                          3.0,
3512                                          GTK_WIDGET(desktop->canvas), NULL,
3513                                          holder, TRUE, "altx-pencil",
3514                                          1, 100.0, 0.5, 0,
3515                                          labels, values, G_N_ELEMENTS(labels),
3516                                          sp_pencil_tb_tolerance_value_changed,
3517                                          1, 2);
3518         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3519         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3521         Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE,
3522                                                       "tools.freehand.pencil");
3523         repr->addListener(&pencil_node_events, G_OBJECT(holder));
3524         g_object_set_data(G_OBJECT(holder), "repr", repr);
3526     }
3528     /* advanced shape options */
3529     freehand_add_advanced_shape_options(mainActions, holder, true);
3531     /* Reset */
3532     {
3533         InkAction* inky = ink_action_new( "PencilResetAction",
3534                                           _("Defaults"),
3535                                           _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3536                                           GTK_STOCK_CLEAR,
3537                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3538         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
3539         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3540     }
3542     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3547 //########################
3548 //##       Tweak        ##
3549 //########################
3551 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3553     prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3556 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3558     prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3561 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3563     prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3566 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3568     int mode = ege_select_one_action_get_active( act );
3569     prefs_set_int_attribute("tools.tweak", "mode", mode);
3571     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3572     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3573     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3574     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3575     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3576     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3577     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3578         if (doh) gtk_action_set_sensitive (doh, TRUE);
3579         if (dos) gtk_action_set_sensitive (dos, TRUE);
3580         if (dol) gtk_action_set_sensitive (dol, TRUE);
3581         if (doo) gtk_action_set_sensitive (doo, TRUE);
3582         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3583         if (fid) gtk_action_set_sensitive (fid, FALSE);
3584     } else {
3585         if (doh) gtk_action_set_sensitive (doh, FALSE);
3586         if (dos) gtk_action_set_sensitive (dos, FALSE);
3587         if (dol) gtk_action_set_sensitive (dol, FALSE);
3588         if (doo) gtk_action_set_sensitive (doo, FALSE);
3589         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3590         if (fid) gtk_action_set_sensitive (fid, TRUE);
3591     }
3594 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3596     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3599 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3600     bool show = gtk_toggle_action_get_active( act );
3601     prefs_set_int_attribute ("tools.tweak", "doh",  show ? 1 : 0);
3603 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3604     bool show = gtk_toggle_action_get_active( act );
3605     prefs_set_int_attribute ("tools.tweak", "dos",  show ? 1 : 0);
3607 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3608     bool show = gtk_toggle_action_get_active( act );
3609     prefs_set_int_attribute ("tools.tweak", "dol",  show ? 1 : 0);
3611 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3612     bool show = gtk_toggle_action_get_active( act );
3613     prefs_set_int_attribute ("tools.tweak", "doo",  show ? 1 : 0);
3616 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3618     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3620     {
3621         /* Width */
3622         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3623         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3624         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3625                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3626                                                               "tools.tweak", "width", 15,
3627                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3628                                                               1, 100, 1.0, 0.0,
3629                                                               labels, values, G_N_ELEMENTS(labels),
3630                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
3631         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3632         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3633         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3634     }
3637     {
3638         /* Force */
3639         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3640         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3641         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3642                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
3643                                                               "tools.tweak", "force", 20,
3644                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3645                                                               1, 100, 1.0, 0.0,
3646                                                               labels, values, G_N_ELEMENTS(labels),
3647                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
3648         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3649         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3650         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3651     }
3653     /* Mode */
3654     {
3655         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3657         GtkTreeIter iter;
3658         gtk_list_store_append( model, &iter );
3659         gtk_list_store_set( model, &iter,
3660                             0, _("Push mode"),
3661                             1, _("Push parts of paths in any direction"),
3662                             2, "tweak_push_mode",
3663                             -1 );
3665         gtk_list_store_append( model, &iter );
3666         gtk_list_store_set( model, &iter,
3667                             0, _("Shrink mode"),
3668                             1, _("Shrink (inset) parts of paths"),
3669                             2, "tweak_shrink_mode",
3670                             -1 );
3672         gtk_list_store_append( model, &iter );
3673         gtk_list_store_set( model, &iter,
3674                             0, _("Grow mode"),
3675                             1, _("Grow (outset) parts of paths"),
3676                             2, "tweak_grow_mode",
3677                             -1 );
3679         gtk_list_store_append( model, &iter );
3680         gtk_list_store_set( model, &iter,
3681                             0, _("Attract mode"),
3682                             1, _("Attract parts of paths towards cursor"),
3683                             2, "tweak_attract_mode",
3684                             -1 );
3686         gtk_list_store_append( model, &iter );
3687         gtk_list_store_set( model, &iter,
3688                             0, _("Repel mode"),
3689                             1, _("Repel parts of paths from cursor"),
3690                             2, "tweak_repel_mode",
3691                             -1 );
3693         gtk_list_store_append( model, &iter );
3694         gtk_list_store_set( model, &iter,
3695                             0, _("Roughen mode"),
3696                             1, _("Roughen parts of paths"),
3697                             2, "tweak_roughen_mode",
3698                             -1 );
3700         gtk_list_store_append( model, &iter );
3701         gtk_list_store_set( model, &iter,
3702                             0, _("Color paint mode"),
3703                             1, _("Paint the tool's color upon selected objects"),
3704                             2, "tweak_colorpaint_mode",
3705                             -1 );
3707         gtk_list_store_append( model, &iter );
3708         gtk_list_store_set( model, &iter,
3709                             0, _("Color jitter mode"),
3710                             1, _("Jitter the colors of selected objects"),
3711                             2, "tweak_colorjitter_mode",
3712                             -1 );
3714         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3715         g_object_set( act, "short_label", _("Mode:"), NULL );
3716         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3717         g_object_set_data( holder, "mode_action", act );
3719         ege_select_one_action_set_appearance( act, "full" );
3720         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3721         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3722         ege_select_one_action_set_icon_column( act, 2 );
3723         ege_select_one_action_set_icon_size( act, secondarySize );
3724         ege_select_one_action_set_tooltip_column( act, 1  );
3726         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3727         ege_select_one_action_set_active( act, mode );
3728         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3730         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3731     }
3733     guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3735     {
3736         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3737         ege_output_action_set_use_markup( act, TRUE );
3738         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3739         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3740             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3741         g_object_set_data( holder, "tweak_channels_label", act);
3742     }
3744     {
3745         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3746                                                       _("Hue"),
3747                                                       _("In color mode, act on objects' hue"),
3748                                                       NULL,
3749                                                       Inkscape::ICON_SIZE_DECORATION );
3750         //TRANSLATORS:  "H" here stands for hue
3751         g_object_set( act, "short_label", _("H"), NULL );
3752         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3753         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3754         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3755         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3756             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3757         g_object_set_data( holder, "tweak_doh", act);
3758     }
3759     {
3760         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3761                                                       _("Saturation"),
3762                                                       _("In color mode, act on objects' saturation"),
3763                                                       NULL,
3764                                                       Inkscape::ICON_SIZE_DECORATION );
3765         //TRANSLATORS: "S" here stands for Saturation
3766         g_object_set( act, "short_label", _("S"), NULL );
3767         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3768         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3769         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3770         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3771             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3772         g_object_set_data( holder, "tweak_dos", act );
3773     }
3774     {
3775         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3776                                                       _("Lightness"),
3777                                                       _("In color mode, act on objects' lightness"),
3778                                                       NULL,
3779                                                       Inkscape::ICON_SIZE_DECORATION );
3780         //TRANSLATORS: "L" here stands for Lightness
3781         g_object_set( act, "short_label", _("L"), NULL );
3782         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3783         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3784         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3785         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3786             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3787         g_object_set_data( holder, "tweak_dol", act );
3788     }
3789     {
3790         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3791                                                       _("Opacity"),
3792                                                       _("In color mode, act on objects' opacity"),
3793                                                       NULL,
3794                                                       Inkscape::ICON_SIZE_DECORATION );
3795         //TRANSLATORS: "O" here stands for Opacity
3796         g_object_set( act, "short_label", _("O"), NULL );
3797         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3798         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3799         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3800         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3801             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3802         g_object_set_data( holder, "tweak_doo", act );
3803     }
3805     {   /* Fidelity */
3806         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3807         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3808         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3809                                                               _("Fidelity"), _("Fidelity:"),
3810                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3811                                                               "tools.tweak", "fidelity", 50,
3812                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3813                                                               1, 100, 1.0, 10.0,
3814                                                               labels, values, G_N_ELEMENTS(labels),
3815                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
3816         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3817         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3818         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3819             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3820         g_object_set_data( holder, "tweak_fidelity", eact );
3821     }
3824     /* Use Pressure button */
3825     {
3826         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3827                                                       _("Pressure"),
3828                                                       _("Use the pressure of the input device to alter the force of tweak action"),
3829                                                       "use_pressure",
3830                                                       Inkscape::ICON_SIZE_DECORATION );
3831         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3832         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3833         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3834     }
3839 //########################
3840 //##     Calligraphy    ##
3841 //########################
3842 static void update_presets_list (GObject *tbl)
3844     if (g_object_get_data(tbl, "presets_blocked"))
3845         return;
3847     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3848     if (!sel) {
3849         ege_select_one_action_set_active(sel, 0);
3850         return;
3851     }
3853     int total_prefs = pref_path_number_of_children("tools.calligraphic.preset");
3855     for (int i = 1; i <= total_prefs; i++) {
3856         gchar *preset_path = get_pref_nth_child("tools.calligraphic.preset", i);
3857         Inkscape::XML::Node *preset_repr = inkscape_get_repr(INKSCAPE, preset_path);
3859         bool match = true;
3861         for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
3862               iter; 
3863               ++iter ) {
3864             const gchar *attr_name = g_quark_to_string(iter->key);
3865             if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
3866                 continue;
3867             void *widget = g_object_get_data(tbl, attr_name);
3868             if (widget) {
3869                 if (GTK_IS_ADJUSTMENT(widget)) {
3870                     double v = prefs_get_double_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
3871                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
3872                     //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
3873                     if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
3874                         match = false;
3875                         break;
3876                     }
3877                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
3878                     int v = prefs_get_int_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
3879                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
3880                     //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
3881                     if (gtk_toggle_action_get_active(toggle) != v) {
3882                         match = false;
3883                         break;
3884                     }
3885                 } 
3886             } 
3887         }
3889         if (match) {
3890             // newly added item is at the same index as the 
3891             // save command, so we need to change twice for it to take effect
3892             ege_select_one_action_set_active(sel, 0);
3893             ege_select_one_action_set_active(sel, i);
3894             return;
3895         }
3896     }
3898     // no match found
3899     ege_select_one_action_set_active(sel, 0);
3902 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3904     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value * 0.01 );
3905     update_presets_list(tbl);
3908 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3910     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value * 0.01 );
3911     update_presets_list(tbl);
3914 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3916     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3917     update_presets_list(tbl);
3920 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3922     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3923     update_presets_list(tbl);
3926 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3928     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value * 0.01 );
3929     update_presets_list(tbl);
3932 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3934     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value * 0.01);
3935     update_presets_list(tbl);
3938 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3940     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value * 0.01 );
3941     update_presets_list(tbl);
3944 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3946     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3947     update_presets_list(tbl);
3950 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
3952     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3953     update_presets_list(tbl);
3956 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
3958     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3959     update_presets_list(tbl);
3962 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
3964     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
3965     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3966     update_presets_list(tbl);
3967     if (calligraphy_angle )
3968         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3972 static gchar const *const widget_names[] = {
3973     "width",
3974     "mass",
3975     "wiggle",
3976     "angle",
3977     "thinning",
3978     "tremor",
3979     "flatness",
3980     "cap_rounding",
3981     "usepressure",
3982     "tracebackground",
3983     "usetilt"
3984 };
3987 static void sp_dcc_build_presets_list(GObject *tbl) 
3989     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
3991     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3992     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
3993     gtk_list_store_clear (model);
3995     {
3996         GtkTreeIter iter;
3997         gtk_list_store_append( model, &iter );
3998         gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
3999     }
4001     //TODO: switch back to prefs API
4002     Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
4003     Inkscape::XML::Node *child_repr = sp_repr_children(repr);
4004     int ii=1;
4005     while (child_repr) {
4006         GtkTreeIter iter;
4007         char *preset_name = (char *) child_repr->attribute("name");
4008         gtk_list_store_append( model, &iter );
4009         gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );
4010         child_repr = sp_repr_next(child_repr);
4011     }
4013     {
4014         GtkTreeIter iter;
4015         gtk_list_store_append( model, &iter );
4016         gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4017         g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4018     }
4020     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4022     update_presets_list (tbl);
4025 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4027     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4028     if (! desktop) return;
4030     if (g_object_get_data(tbl, "presets_blocked")) 
4031         return;
4033     Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
4034     if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) {
4035         // dialog cancelled
4036         update_presets_list (tbl);
4037         return;
4038     }
4039     Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
4041     if (!profile_name.c_str() || *profile_name.c_str() == 0) {
4042         // empty name entered
4043         update_presets_list (tbl);
4044         return;
4045     }
4047     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4049     int new_index = -1;
4050     gchar *pref_path = NULL;
4051     int total_prefs = pref_path_number_of_children("tools.calligraphic.preset");
4053     for (int i = 1; i <= total_prefs; i++) {
4054         gchar *path = get_pref_nth_child("tools.calligraphic.preset", i);
4055         const gchar *name = prefs_get_string_attribute(path, "name");
4056         if (name && !strcmp(name, profile_name.c_str())) {
4057             // we already have preset with this name, replace its values
4058             new_index = i;
4059             pref_path = g_strdup(path);
4060             break;
4061         }
4062     }
4064     if (new_index == -1) {
4065         // no preset with this name, create 
4066         new_index = total_prefs + 1;
4067         gchar *profile_id = g_strdup_printf("dcc%d", new_index);
4068         pref_path = create_pref("tools.calligraphic.preset", profile_id);
4069         g_free(profile_id);
4070     }
4072     for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4073         gchar const *const widget_name = widget_names[i];
4074         void *widget = g_object_get_data(tbl, widget_name);
4075         if (widget) {
4076             if (GTK_IS_ADJUSTMENT(widget)) {
4077                 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4078                 double v = gtk_adjustment_get_value(adj);
4079                 prefs_set_double_attribute(pref_path, widget_name, v);
4080                 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4081             } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4082                 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4083                 int v = gtk_toggle_action_get_active(toggle);
4084                 prefs_set_int_attribute(pref_path, widget_name, v);
4085                 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4086             } else {
4087                g_warning("Unknown widget type for preset: %s\n", widget_name);
4088             }
4089         } else {
4090             g_warning("Bad key when writing preset: %s\n", widget_name);
4091         }
4092     }
4093     prefs_set_string_attribute(pref_path, "name", profile_name.c_str());
4095     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4097     sp_dcc_build_presets_list (tbl);
4099     g_free(pref_path);
4103 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4105     gint preset_index = ege_select_one_action_get_active( act );
4106     gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4108     if (preset_index == save_presets_index) {
4109         // this is the Save command
4110         sp_dcc_save_profile(NULL, tbl);
4111         return;
4112     }
4114     if (g_object_get_data(tbl, "presets_blocked")) 
4115         return;
4117     gchar *const preset_path = get_pref_nth_child("tools.calligraphic.preset", preset_index);
4119     if (preset_path) {
4120         g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE)); //temporarily block the selector so no one will updadte it while we're reading it
4122         Inkscape::XML::Node *preset_repr = inkscape_get_repr(INKSCAPE, preset_path);
4124         for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
4125               iter; 
4126               ++iter ) {
4127             const gchar *attr_name = g_quark_to_string(iter->key);
4128             if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
4129                 continue;
4130             void *widget = g_object_get_data(tbl, attr_name);
4131             if (widget) {
4132                 if (GTK_IS_ADJUSTMENT(widget)) {
4133                     double v = prefs_get_double_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4134                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4135                     gtk_adjustment_set_value(adj, v);
4136                     //std::cout << "set adj " << attr_name << " to " << v << "\n";
4137                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4138                     int v = prefs_get_int_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4139                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4140                     gtk_toggle_action_set_active(toggle, v);
4141                     //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4142                 } else {
4143                     g_warning("Unknown widget type for preset: %s\n", attr_name);
4144                 }
4145             } else {
4146                 g_warning("Bad key found in a preset record: %s\n", attr_name);
4147             }
4148         }
4149         g_free(preset_path);
4150         g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4151     }
4156 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4158     {
4159         g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4161         EgeAdjustmentAction* calligraphy_angle = 0;
4163         {
4164         /* Width */
4165         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4166         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4167         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4168                                                               _("Pen Width"), _("Width:"),
4169                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
4170                                                               "tools.calligraphic", "width", 15,
4171                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4172                                                               1, 100, 1.0, 0.0,
4173                                                               labels, values, G_N_ELEMENTS(labels),
4174                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
4175         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4176         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4177         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4178         }
4180         {
4181         /* Thinning */
4182             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4183             gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4184         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4185                                                               _("Stroke Thinning"), _("Thinning:"),
4186                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4187                                                               "tools.calligraphic", "thinning", 10,
4188                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4189                                                               -100, 100, 1, 0.1,
4190                                                               labels, values, G_N_ELEMENTS(labels),
4191                                                               sp_ddc_velthin_value_changed, 0.01, 0, 100);
4192         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4193         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4194         }
4196         {
4197         /* Angle */
4198         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4199         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4200         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4201                                                               _("Pen Angle"), _("Angle:"),
4202                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4203                                                               "tools.calligraphic", "angle", 30,
4204                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4205                                                               -90.0, 90.0, 1.0, 10.0,
4206                                                               labels, values, G_N_ELEMENTS(labels),
4207                                                               sp_ddc_angle_value_changed, 1, 0 );
4208         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4209         g_object_set_data( holder, "angle_action", eact );
4210         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4211         calligraphy_angle = eact;
4212         }
4214         {
4215         /* Fixation */
4216             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4217         gdouble values[] = {0, 20, 40, 60, 90, 100};
4218         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4219                                                               _("Fixation"), _("Fixation:"),
4220                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
4221                                                               "tools.calligraphic", "flatness", 90,
4222                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4223                                                               0.0, 100, 1.0, 10.0,
4224                                                               labels, values, G_N_ELEMENTS(labels),
4225                                                               sp_ddc_flatness_value_changed, 0.01, 0, 100 );
4226         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4227         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4228         }
4230         {
4231         /* Cap Rounding */
4232             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4233         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4234         // TRANSLATORS: "cap" means "end" (both start and finish) here
4235         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4236                                                               _("Cap rounding"), _("Caps:"),
4237                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4238                                                               "tools.calligraphic", "cap_rounding", 0.0,
4239                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4240                                                               0.0, 5.0, 0.01, 0.1,
4241                                                               labels, values, G_N_ELEMENTS(labels),
4242                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4243         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4244         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4245         }
4247         {
4248         /* Tremor */
4249             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4250         gdouble values[] = {0, 10, 20, 40, 60, 100};
4251         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4252                                                               _("Stroke Tremor"), _("Tremor:"),
4253                                                               _("Increase to make strokes rugged and trembling"),
4254                                                               "tools.calligraphic", "tremor", 0.0,
4255                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4256                                                               0.0, 100, 1, 0.0,
4257                                                               labels, values, G_N_ELEMENTS(labels),
4258                                                               sp_ddc_tremor_value_changed, 0.01, 0, 100 );
4260         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4261         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4262         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4263         }
4265         {
4266         /* Wiggle */
4267         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4268         gdouble values[] = {0, 20, 40, 60, 100};
4269         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4270                                                               _("Pen Wiggle"), _("Wiggle:"),
4271                                                               _("Increase to make the pen waver and wiggle"),
4272                                                               "tools.calligraphic", "wiggle", 0.0,
4273                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4274                                                               0.0, 100, 1, 0.0,
4275                                                               labels, values, G_N_ELEMENTS(labels),
4276                                                               sp_ddc_wiggle_value_changed, 0.01, 0, 100 );
4277         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4278         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4279         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4280         }
4282         {
4283         /* Mass */
4284             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4285         gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4286         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4287                                                               _("Pen Mass"), _("Mass:"),
4288                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
4289                                                               "tools.calligraphic", "mass", 2.0,
4290                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4291                                                               0.0, 100, 1, 0.0,
4292                                                               labels, values, G_N_ELEMENTS(labels),
4293                                                               sp_ddc_mass_value_changed, 0.01, 0, 100 );
4294         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4295         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4296         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4297         }
4300         /* Trace Background button */
4301         {
4302             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4303                                                           _("Trace Background"),
4304                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4305                                                           "trace_background",
4306                                                           Inkscape::ICON_SIZE_DECORATION );
4307             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4308             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4309             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
4310             g_object_set_data( holder, "tracebackground", act );
4311         }
4313         /* Use Pressure button */
4314         {
4315             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4316                                                           _("Pressure"),
4317                                                           _("Use the pressure of the input device to alter the width of the pen"),
4318                                                           "use_pressure",
4319                                                           Inkscape::ICON_SIZE_DECORATION );
4320             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4321             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4322             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
4323             g_object_set_data( holder, "usepressure", act );
4324         }
4326         /* Use Tilt button */
4327         {
4328             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4329                                                           _("Tilt"),
4330                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
4331                                                           "use_tilt",
4332                                                           Inkscape::ICON_SIZE_DECORATION );
4333             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4334             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4335             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4336             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4337             g_object_set_data( holder, "usetilt", act );
4338         }
4340         /*calligraphic profile */
4341         {
4342             GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
4343             EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
4344             ege_select_one_action_set_appearance (act1, "compact");
4345             g_object_set_data (holder, "profile_selector", act1 );
4347             g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
4349             sp_dcc_build_presets_list (holder);
4351             g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
4352             gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
4353         }
4354     }
4358 //########################
4359 //##    Circle / Arc    ##
4360 //########################
4362 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4364     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4365     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4367     if (v1 == 0 && v2 == 0) {
4368         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4369             gtk_action_set_sensitive( ocb, FALSE );
4370             gtk_action_set_sensitive( make_whole, FALSE );
4371         }
4372     } else {
4373         gtk_action_set_sensitive( ocb, TRUE );
4374         gtk_action_set_sensitive( make_whole, TRUE );
4375     }
4378 static void
4379 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4381     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4383     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4384         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
4385     }
4387     // quit if run by the attr_changed listener
4388     if (g_object_get_data( tbl, "freeze" )) {
4389         return;
4390     }
4392     // in turn, prevent listener from responding
4393     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4395     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4397     bool modmade = false;
4398     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4399          items != NULL;
4400          items = items->next)
4401     {
4402         SPItem *item = SP_ITEM(items->data);
4404         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4406             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4407             SPArc *arc = SP_ARC(item);
4409             if (!strcmp(value_name, "start"))
4410                 ge->start = (adj->value * M_PI)/ 180;
4411             else
4412                 ge->end = (adj->value * M_PI)/ 180;
4414             sp_genericellipse_normalize(ge);
4415             ((SPObject *)arc)->updateRepr();
4416             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4418             modmade = true;
4419         }
4420     }
4422     g_free(namespaced_name);
4424     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4426     sp_arctb_sensitivize( tbl, adj->value, other->value );
4428     if (modmade) {
4429         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4430                                    _("Arc: Change start/end"));
4431     }
4433     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4437 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
4439     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
4442 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4444     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
4448 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4450     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4451     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4452         if ( ege_select_one_action_get_active( act ) != 0 ) {
4453             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
4454         } else {
4455             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
4456         }
4457     }
4459     // quit if run by the attr_changed listener
4460     if (g_object_get_data( tbl, "freeze" )) {
4461         return;
4462     }
4464     // in turn, prevent listener from responding
4465     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4467     bool modmade = false;
4469     if ( ege_select_one_action_get_active(act) != 0 ) {
4470         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4471              items != NULL;
4472              items = items->next)
4473         {
4474             if (SP_IS_ARC((SPItem *) items->data)) {
4475                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4476                 repr->setAttribute("sodipodi:open", "true");
4477                 SP_OBJECT((SPItem *) items->data)->updateRepr();
4478                 modmade = true;
4479             }
4480         }
4481     } else {
4482         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4483              items != NULL;
4484              items = items->next)
4485         {
4486             if (SP_IS_ARC((SPItem *) items->data))    {
4487                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4488                 repr->setAttribute("sodipodi:open", NULL);
4489                 SP_OBJECT((SPItem *) items->data)->updateRepr();
4490                 modmade = true;
4491             }
4492         }
4493     }
4495     if (modmade) {
4496         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4497                                    _("Arc: Change open/closed"));
4498     }
4500     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4503 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4505     GtkAdjustment *adj;
4506     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
4507     gtk_adjustment_set_value(adj, 0.0);
4508     gtk_adjustment_value_changed(adj);
4510     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
4511     gtk_adjustment_set_value(adj, 0.0);
4512     gtk_adjustment_value_changed(adj);
4514     spinbutton_defocus( GTK_OBJECT(obj) );
4517 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
4518                                       gchar const */*old_value*/, gchar const */*new_value*/,
4519                                       bool /*is_interactive*/, gpointer data)
4521     GObject *tbl = G_OBJECT(data);
4523     // quit if run by the _changed callbacks
4524     if (g_object_get_data( tbl, "freeze" )) {
4525         return;
4526     }
4528     // in turn, prevent callbacks from responding
4529     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4531     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4532     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4534     GtkAdjustment *adj1,*adj2;
4535     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4536     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4537     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4538     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4540     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4542     char const *openstr = NULL;
4543     openstr = repr->attribute("sodipodi:open");
4544     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4546     if (openstr) {
4547         ege_select_one_action_set_active( ocb, 1 );
4548     } else {
4549         ege_select_one_action_set_active( ocb, 0 );
4550     }
4552     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4555 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4556     NULL, /* child_added */
4557     NULL, /* child_removed */
4558     arc_tb_event_attr_changed,
4559     NULL, /* content_changed */
4560     NULL  /* order_changed */
4561 };
4564 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4566     int n_selected = 0;
4567     Inkscape::XML::Node *repr = NULL;
4569     purge_repr_listener( tbl, tbl );
4571     for (GSList const *items = selection->itemList();
4572          items != NULL;
4573          items = items->next)
4574     {
4575         if (SP_IS_ARC((SPItem *) items->data)) {
4576             n_selected++;
4577             repr = SP_OBJECT_REPR((SPItem *) items->data);
4578         }
4579     }
4581     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4583     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4584     if (n_selected == 0) {
4585         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4586     } else if (n_selected == 1) {
4587         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4588         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4590         if (repr) {
4591             g_object_set_data( tbl, "repr", repr );
4592             Inkscape::GC::anchor(repr);
4593             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4594             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4595         }
4596     } else {
4597         // FIXME: implement averaging of all parameters for multiple selected
4598         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4599         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4600         sp_arctb_sensitivize( tbl, 1, 0 );
4601     }
4605 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4607     EgeAdjustmentAction* eact = 0;
4608     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
4611     {
4612         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4613         ege_output_action_set_use_markup( act, TRUE );
4614         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4615         g_object_set_data( holder, "mode_action", act );
4616     }
4618     /* Start */
4619     {
4620         eact = create_adjustment_action( "ArcStartAction",
4621                                          _("Start"), _("Start:"),
4622                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
4623                                          "tools.shapes.arc", "start", 0.0,
4624                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4625                                          -360.0, 360.0, 1.0, 10.0,
4626                                          0, 0, 0,
4627                                          sp_arctb_start_value_changed);
4628         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4629     }
4631     /* End */
4632     {
4633         eact = create_adjustment_action( "ArcEndAction",
4634                                          _("End"), _("End:"),
4635                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
4636                                          "tools.shapes.arc", "end", 0.0,
4637                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4638                                          -360.0, 360.0, 1.0, 10.0,
4639                                          0, 0, 0,
4640                                          sp_arctb_end_value_changed);
4641         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4642     }
4644     /* Segments / Pie checkbox */
4645     {
4646         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4648         GtkTreeIter iter;
4649         gtk_list_store_append( model, &iter );
4650         gtk_list_store_set( model, &iter,
4651                             0, _("Closed arc"),
4652                             1, _("Switch to segment (closed shape with two radii)"),
4653                             2, "circle_closed_arc",
4654                             -1 );
4656         gtk_list_store_append( model, &iter );
4657         gtk_list_store_set( model, &iter,
4658                             0, _("Open Arc"),
4659                             1, _("Switch to arc (unclosed shape)"),
4660                             2, "circle_open_arc",
4661                             -1 );
4663         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4664         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4665         g_object_set_data( holder, "open_action", act );
4667         ege_select_one_action_set_appearance( act, "full" );
4668         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4669         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4670         ege_select_one_action_set_icon_column( act, 2 );
4671         ege_select_one_action_set_icon_size( act, secondarySize );
4672         ege_select_one_action_set_tooltip_column( act, 1  );
4674         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4675         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4676         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4677         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4678     }
4680     /* Make Whole */
4681     {
4682         InkAction* inky = ink_action_new( "ArcResetAction",
4683                                           _("Make whole"),
4684                                           _("Make the shape a whole ellipse, not arc or segment"),
4685                                           "reset_circle",
4686                                           secondarySize );
4687         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4688         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4689         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4690         g_object_set_data( holder, "make_whole", inky );
4691     }
4693     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4694     // sensitivize make whole and open checkbox
4695     {
4696         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4697         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4698         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4699     }
4702     sigc::connection *connection = new sigc::connection(
4703         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4704         );
4705     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4706     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4712 // toggle button callbacks and updaters
4714 //########################
4715 //##      Dropper       ##
4716 //########################
4718 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4719     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4720     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4721     if ( set_action ) {
4722         if ( gtk_toggle_action_get_active( act ) ) {
4723             gtk_action_set_sensitive( set_action, TRUE );
4724         } else {
4725             gtk_action_set_sensitive( set_action, FALSE );
4726         }
4727     }
4729     spinbutton_defocus(GTK_OBJECT(tbl));
4732 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4733     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4734     spinbutton_defocus(GTK_OBJECT(tbl));
4738 /**
4739  * Dropper auxiliary toolbar construction and setup.
4740  *
4741  * TODO: Would like to add swatch of current color.
4742  * TODO: Add queue of last 5 or so colors selected with new swatches so that
4743  *       can drag and drop places. Will provide a nice mixing palette.
4744  */
4745 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4747     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4749     {
4750         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4751         ege_output_action_set_use_markup( act, TRUE );
4752         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4753     }
4755     {
4756         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4757                                                       _("Pick opacity"),
4758                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4759                                                       NULL,
4760                                                       Inkscape::ICON_SIZE_DECORATION );
4761         g_object_set( act, "short_label", _("Pick"), NULL );
4762         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4763         g_object_set_data( holder, "pick_action", act );
4764         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4765         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4766     }
4768     {
4769         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4770                                                       _("Assign opacity"),
4771                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4772                                                       NULL,
4773                                                       Inkscape::ICON_SIZE_DECORATION );
4774         g_object_set( act, "short_label", _("Assign"), NULL );
4775         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4776         g_object_set_data( holder, "set_action", act );
4777         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4778         // make sure it's disabled if we're not picking alpha
4779         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4780         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4781     }
4785 //########################
4786 //##      LPETool       ##
4787 //########################
4789 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
4791 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
4792 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
4794     using namespace Inkscape::LivePathEffect;
4796     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
4798     // only take action if run by the attr_changed listener
4799     if (!g_object_get_data(tbl, "freeze")) {
4800         // in turn, prevent listener from responding
4801         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
4803         // TODO: how can we set *all* actions inactive (such that no sutool is activated?)
4804         gint lpeToolMode = ege_select_one_action_get_active(act);
4805         if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4806             prefs_set_int_attribute( "tools.lpetool", "mode", lpeToolMode );
4807         }
4809         EffectType type = lpesubtools[lpeToolMode];
4810         SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
4812         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
4813     }
4816 static void
4817 sp_lpetool_test_value_changed(GtkAdjustment *adj, GObject *tbl)
4819     g_print ("sp_lpetool_test_value_changed()\n");
4820     using namespace Inkscape::LivePathEffect;
4822     // quit if run by the attr_changed listener
4823     if (g_object_get_data( tbl, "freeze" )) {
4824         return;
4825     }
4827     // in turn, prevent listener from responding
4828     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
4830     LPEAngleBisector *lpeab = static_cast<LPEAngleBisector *>(g_object_get_data(tbl, "currentlpe"));
4831     if (!lpeab) {
4832         g_print ("no LPE!\n");
4833     } else {
4834         g_print ("LPE found. Adjusting left length\n");
4835         SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
4836         lpeab->length_left.param_set_value(gtk_adjustment_get_value(adj));
4837         sp_lpe_item_update_patheffect(lpeitem, true, true);
4838     }
4840     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4843 void
4844 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
4846     using namespace Inkscape::LivePathEffect;
4847     g_print ("sp_lpetool_toolbox_sel_changed()");
4848     {
4849         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "lpetool_test_action" ) );
4850         SPItem *item = selection->singleItem();
4851         if (item && SP_IS_LPE_ITEM(item)) {
4852             g_print (" - item found\n");
4853             SPLPEItem *lpeitem = SP_LPE_ITEM(item);
4854             Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
4855             if (lpe && lpe->effectType() == ANGLE_BISECTOR) {
4856                 LPEAngleBisector *lpeab = dynamic_cast<LPEAngleBisector*>(lpe);
4857                 g_object_set_data(tbl, "currentlpe", lpeab);
4858                 g_object_set_data(tbl, "currentlpeitem", lpeitem);
4859                 gtk_action_set_sensitive(w, TRUE);
4860             } else {
4861                 g_object_set_data(tbl, "currentlpe", NULL);
4862                 g_object_set_data(tbl, "currentlpeitem", NULL);
4863                 gtk_action_set_sensitive(w, FALSE);
4864             }
4865         } else {
4866             g_print (" - unsetting item\n");
4867             g_object_set_data(tbl, "currentlpe", NULL);
4868             g_object_set_data(tbl, "currentlpeitem", NULL);
4869             gtk_action_set_sensitive(w, FALSE);
4870         }
4871     }
4872     g_print ("\n");
4875 static void
4876 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
4877     SPDesktop *desktop = static_cast<SPDesktop *>(data);
4879     bool show = gtk_toggle_action_get_active( act );
4880     prefs_set_int_attribute ("tools.lpetool", "show_bbox",  show ? 1 : 0);
4882     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
4883         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4884         lpetool_context_reset_limiting_bbox(lc);
4885     }
4888 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4890     /** Automatically create a list of LPEs that get added to the toolbar **/
4891     {
4892         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4894         GtkTreeIter iter;
4896         // the first toggle button represents the state that no subtool is active (remove this when
4897         // this can be modeled by EgeSelectOneAction or some other action)
4898         gtk_list_store_append( model, &iter );
4899         gtk_list_store_set( model, &iter,
4900                             0, _("All inactive"),
4901                             1, _("No geometric tool is active"),
4902                             2, _("all_inactive"),
4903                             -1 );
4905         Inkscape::LivePathEffect::EffectType type;
4906         for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
4907             type =  lpesubtools[i];
4908             gtk_list_store_append( model, &iter );
4909             gtk_list_store_set( model, &iter,
4910                                 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
4911                                 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
4912                                 2, Inkscape::LivePathEffect::LPETypeConverter.get_key(type).c_str(),
4913                                 -1 );
4914         }
4916         EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4917         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4918         g_object_set_data( holder, "lpetool_mode_action", act );
4920         ege_select_one_action_set_appearance( act, "full" );
4921         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4922         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4923         ege_select_one_action_set_icon_column( act, 2 );
4924         ege_select_one_action_set_tooltip_column( act, 1  );
4926         gint lpeToolMode = prefs_get_int_attribute("tools.lpetool", "mode", 0);
4927         ege_select_one_action_set_active( act, lpeToolMode );
4928         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
4929     }
4931     /* Show limiting bounding box */
4932     {
4933         InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
4934                                                       _("Show limiting bounding box"),
4935                                                       _("Show bounding box (is used to cut infinite lines)"),
4936                                                       "lpetool_show_bbox",
4937                                                       Inkscape::ICON_SIZE_DECORATION );
4938         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4939         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
4940         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.lpetool", "show_bbox", 1 ) );
4941     }
4943     /* Test action */
4944     /**
4945     {
4946         EgeAdjustmentAction* eact = 0;
4947         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
4948         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
4949         eact = create_adjustment_action( "TestLPEAction",
4950                                          _("Test value"), _("Test value:"), _("Test for interactive control widgets ..."),
4951                                          "tools.lpetool", "testvalue", 0,
4952                                          GTK_WIDGET(desktop->canvas), NULL us, holder, TRUE, "altx-lpetool",
4953                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
4954                                          labels, values, G_N_ELEMENTS(labels),
4955                                          sp_lpetool_test_value_changed );
4956         //tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
4957         g_object_set_data( holder, "lpetool_test_action", eact );
4958         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
4959         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4960     }
4961     **/
4963     //watch selection
4964     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
4966     sigc::connection *c_selection_changed =
4967         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
4968                               (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
4969     pool->add_connection ("selection-changed", c_selection_changed);
4972 //########################
4973 //##       Eraser       ##
4974 //########################
4976 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4978     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4979     gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
4980     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4981         prefs_set_int_attribute( "tools.eraser", "mode", eraserMode );
4982     }
4984     // only take action if run by the attr_changed listener
4985     if (!g_object_get_data( tbl, "freeze" )) {
4986         // in turn, prevent listener from responding
4987         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4989         if ( eraserMode != 0 ) {
4990         } else {
4991         }
4992         // TODO finish implementation
4994         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4995     }
4998 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5000     {
5001         /* Width */
5002         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5003         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5004         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5005                                                               _("Pen Width"), _("Width:"),
5006                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
5007                                                               "tools.eraser", "width", 15,
5008                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5009                                                               1, 100, 1.0, 0.0,
5010                                                               labels, values, G_N_ELEMENTS(labels),
5011                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
5012         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5013         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5014         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5015     }
5017     {
5018         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5020         GtkTreeIter iter;
5021         gtk_list_store_append( model, &iter );
5022         gtk_list_store_set( model, &iter,
5023                             0, _("Delete"),
5024                             1, _("Delete objects touched by the eraser"),
5025                             2, "delete_object",
5026                             -1 );
5028         gtk_list_store_append( model, &iter );
5029         gtk_list_store_set( model, &iter,
5030                             0, _("Cut"),
5031                             1, _("Cut out from objects"),
5032                             2, "difference",
5033                             -1 );
5035         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5036         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5037         g_object_set_data( holder, "eraser_mode_action", act );
5039         ege_select_one_action_set_appearance( act, "full" );
5040         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5041         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5042         ege_select_one_action_set_icon_column( act, 2 );
5043         ege_select_one_action_set_tooltip_column( act, 1  );
5045         gint eraserMode = (prefs_get_int_attribute("tools.eraser", "mode", 0) != 0) ? 1 : 0;
5046         ege_select_one_action_set_active( act, eraserMode );
5047         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
5048     }
5052 //########################
5053 //##    Text Toolbox    ##
5054 //########################
5055 /*
5056 static void
5057 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
5059     //Call back for letter sizing spinbutton
5062 static void
5063 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
5065     //Call back for line height spinbutton
5068 static void
5069 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5071     //Call back for horizontal kerning spinbutton
5074 static void
5075 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5077     //Call back for vertical kerning spinbutton
5080 static void
5081 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
5083     //Call back for letter rotation spinbutton
5084 }*/
5086 namespace {
5088 bool popdown_visible = false;
5089 bool popdown_hasfocus = false;
5091 void
5092 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
5094     SPStyle *query =
5095         sp_style_new (SP_ACTIVE_DOCUMENT);
5097 //    int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5099     int result_family =
5100         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5102     int result_style =
5103         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5105     int result_numbers =
5106         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5108     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5110     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5111     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
5112         // there are no texts in selection, read from prefs
5113  
5114         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
5115         if (repr) {
5116             sp_style_read_from_repr (query, repr);
5117             if (g_object_get_data(tbl, "text_style_from_prefs")) {
5118                 // do not reset the toolbar style from prefs if we already did it last time
5119                 sp_style_unref(query);
5120                 return;
5121             }
5122             g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
5123         } else {
5124             sp_style_unref(query);
5125             return;
5126         }
5127     } else {
5128         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
5129     }
5131     if (query->text)
5132     {
5133         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
5134             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5135             gtk_entry_set_text (GTK_ENTRY (entry), "");
5137         } else if (query->text->font_specification.value || query->text->font_family.value) {
5139             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5141             // Get the font that corresponds
5142             Glib::ustring familyName;
5144             font_instance * font = font_factory::Default()->FaceFromStyle(query);
5145             if (font) {
5146                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
5147                 font->Unref();
5148                 font = NULL;
5149             }
5151             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
5153             Gtk::TreePath path;
5154             try {
5155                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
5156             } catch (...) {
5157                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
5158                 sp_style_unref(query);
5159                 return;
5160             }
5162             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5163             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5165             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
5167             gtk_tree_selection_select_path (tselection, path.gobj());
5168             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5170             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
5171         }
5173         //Size
5174         {
5175             GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
5176             gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
5177             g_object_set_data(tbl, "size-block", gpointer(1));
5178             gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
5179             g_object_set_data(tbl, "size-block", gpointer(0));
5180             g_free(str);
5181         }
5183         //Anchor
5184         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
5185         {
5186             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
5187             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5188             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5189             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5190         }
5191         else
5192         {
5193             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
5194             {
5195                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
5196                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5197                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5198                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5199             }
5200             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
5201             {
5202                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
5203                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5204                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5205                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5206             }
5207             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
5208             {
5209                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
5210                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5211                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5212                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5213             }
5214         }
5216         //Style
5217         {
5218             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
5220             gboolean active = gtk_toggle_button_get_active (button);
5221             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
5223             if (active != check)
5224             {
5225                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5226                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5227                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5228             }
5229         }
5231         {
5232             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
5234             gboolean active = gtk_toggle_button_get_active (button);
5235             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
5237             if (active != check)
5238             {
5239                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5240                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5241                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5242             }
5243         }
5245         //Orientation
5246         //locking both buttons, changing one affect all group (both)
5247         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
5248         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5250         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
5251         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
5253         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
5254         {
5255             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5256         }
5257         else
5258         {
5259             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
5260         }
5261         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5262         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
5263     }
5265     sp_style_unref(query);
5268 void
5269 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
5271     sp_text_toolbox_selection_changed (selection, tbl);
5274 void
5275 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
5277     sp_text_toolbox_selection_changed (NULL, tbl);
5280 void
5281 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
5282                                 GObject             *tbl)
5284     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
5285     GtkTreeModel *model = 0;
5286     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5287     GtkTreeIter   iter;
5288     char         *family = 0;
5290     gdk_pointer_ungrab (GDK_CURRENT_TIME);
5291     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5293     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
5294         return;
5295     }
5297     gtk_tree_model_get (model, &iter, 0, &family, -1);
5299     if (g_object_get_data (G_OBJECT (selection), "block"))
5300     {
5301         gtk_entry_set_text (GTK_ENTRY (entry), family);
5302         return;
5303     }
5305     gtk_entry_set_text (GTK_ENTRY (entry), family);
5307     SPStyle *query =
5308         sp_style_new (SP_ACTIVE_DOCUMENT);
5310     int result_fontspec =
5311         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5313     //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5315     SPCSSAttr *css = sp_repr_css_attr_new ();
5318     // First try to get the font spec from the stored value
5319     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
5321     if (fontSpec.empty()) {
5322         // Construct a new font specification if it does not yet exist
5323         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5324         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5325         fontFromStyle->Unref();
5326     }
5328     if (!fontSpec.empty()) {
5329         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
5330         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
5331             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
5332             if (font) {
5333                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5335                 // Set all the these just in case they were altered when finding the best
5336                 // match for the new family and old style...
5338                 gchar c[256];
5340                 font->Family(c, 256);
5341                 sp_repr_css_set_property (css, "font-family", c);
5343                 font->Attribute( "weight", c, 256);
5344                 sp_repr_css_set_property (css, "font-weight", c);
5346                 font->Attribute("style", c, 256);
5347                 sp_repr_css_set_property (css, "font-style", c);
5349                 font->Attribute("stretch", c, 256);
5350                 sp_repr_css_set_property (css, "font-stretch", c);
5352                 font->Attribute("variant", c, 256);
5353                 sp_repr_css_set_property (css, "font-variant", c);
5355                 font->Unref();
5356             }
5357         }
5358     }
5360     // If querying returned nothing, set the default style of the tool (for new texts)
5361     if (result_fontspec == QUERY_STYLE_NOTHING)
5362     {
5363         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5364         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
5365     }
5366     else
5367     {
5368         sp_desktop_set_style (desktop, css, true, true);
5369     }
5371     sp_style_unref(query);
5373     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5374                                    _("Text: Change font family"));
5375     sp_repr_css_attr_unref (css);
5376     g_free(family);
5377     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5379     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5382 /* This is where execution comes when the contents of the font family box have been completed
5383    by the press of the return key */
5384 void
5385 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
5386                                        GObject      *tbl)
5388     const char *family = gtk_entry_get_text (entry);   // Fetch the requested font family
5390 // Try to match that to a known font. If not, then leave current font alone and remain focused on text box
5391     try {
5392         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
5393         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5394         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5395         gtk_tree_selection_select_path (selection, path.gobj());
5396         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5397         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5398     } catch (...) {
5399         if (family && strlen (family))
5400         {
5401             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5402         }
5403     }
5406 void
5407 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
5408                                    gpointer          data)
5410     if (g_object_get_data (G_OBJECT (button), "block")) return;
5411     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
5412     int prop = GPOINTER_TO_INT(data);
5414     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5415     SPCSSAttr *css = sp_repr_css_attr_new ();
5417     switch (prop)
5418     {
5419         case 0:
5420         {
5421             sp_repr_css_set_property (css, "text-anchor", "start");
5422             sp_repr_css_set_property (css, "text-align", "start");
5423             break;
5424         }
5425         case 1:
5426         {
5427             sp_repr_css_set_property (css, "text-anchor", "middle");
5428             sp_repr_css_set_property (css, "text-align", "center");
5429             break;
5430         }
5432         case 2:
5433         {
5434             sp_repr_css_set_property (css, "text-anchor", "end");
5435             sp_repr_css_set_property (css, "text-align", "end");
5436             break;
5437         }
5439         case 3:
5440         {
5441             sp_repr_css_set_property (css, "text-anchor", "start");
5442             sp_repr_css_set_property (css, "text-align", "justify");
5443             break;
5444         }
5445     }
5447     SPStyle *query =
5448         sp_style_new (SP_ACTIVE_DOCUMENT);
5449     int result_numbers =
5450         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5452     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5453     if (result_numbers == QUERY_STYLE_NOTHING)
5454     {
5455         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5456     }
5458     sp_style_unref(query);
5460     sp_desktop_set_style (desktop, css, true, true);
5461     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5462                                    _("Text: Change alignment"));
5463     sp_repr_css_attr_unref (css);
5465     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5468 void
5469 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
5470                                gpointer          data)
5472     if (g_object_get_data (G_OBJECT (button), "block")) return;
5474     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5475     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5476     int          prop       = GPOINTER_TO_INT(data);
5477     bool         active     = gtk_toggle_button_get_active (button);
5479     SPStyle *query =
5480         sp_style_new (SP_ACTIVE_DOCUMENT);
5482     int result_fontspec =
5483         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5485     //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5486     //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5487     //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5489     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
5490     Glib::ustring newFontSpec = "";
5492     if (fontSpec.empty()) {
5493         // Construct a new font specification if it does not yet exist
5494         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5495         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5496         fontFromStyle->Unref();
5497     }
5499     switch (prop)
5500     {
5501         case 0:
5502         {
5503             if (!fontSpec.empty()) {
5504                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
5505             }
5506             if (fontSpec != newFontSpec) {
5507                 // Don't even set the bold if the font didn't exist on the system
5508                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
5509             }
5510             break;
5511         }
5513         case 1:
5514         {
5515             if (!fontSpec.empty()) {
5516                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
5517             }
5518             if (fontSpec != newFontSpec) {
5519                 // Don't even set the italic if the font didn't exist on the system
5520                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
5521             }
5522             break;
5523         }
5524     }
5526     if (!newFontSpec.empty()) {
5527         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5528     }
5530     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5531     if (result_fontspec == QUERY_STYLE_NOTHING)
5532     {
5533         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5534     }
5536     sp_style_unref(query);
5538     sp_desktop_set_style (desktop, css, true, true);
5539     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5540                                    _("Text: Change font style"));
5541     sp_repr_css_attr_unref (css);
5543     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5546 void
5547 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
5548                                      gpointer         data)
5550     if (g_object_get_data (G_OBJECT (button), "block")) {
5551         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5552         return;
5553     }
5555     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5556     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5557     int          prop       = GPOINTER_TO_INT(data);
5559     switch (prop)
5560     {
5561         case 0:
5562         {
5563             sp_repr_css_set_property (css, "writing-mode", "lr");
5564             break;
5565         }
5567         case 1:
5568         {
5569             sp_repr_css_set_property (css, "writing-mode", "tb");
5570             break;
5571         }
5572     }
5574     SPStyle *query =
5575         sp_style_new (SP_ACTIVE_DOCUMENT);
5576     int result_numbers =
5577         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5579     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5580     if (result_numbers == QUERY_STYLE_NOTHING)
5581     {
5582         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5583     }
5585     sp_desktop_set_style (desktop, css, true, true);
5586     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5587                                    _("Text: Change orientation"));
5588     sp_repr_css_attr_unref (css);
5590     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5593 gboolean
5594 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5596     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5597     if (!desktop) return FALSE;
5599     switch (get_group0_keyval (event)) {
5600         case GDK_Escape: // defocus
5601             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5602             sp_text_toolbox_selection_changed (NULL, tbl); // update
5603             return TRUE; // I consumed the event
5604             break;
5605     }
5606     return FALSE;
5609 gboolean
5610 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5612     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5613     if (!desktop) return FALSE;
5615     switch (get_group0_keyval (event)) {
5616         case GDK_KP_Enter:
5617         case GDK_Return:
5618         case GDK_Escape: // defocus
5619             gtk_widget_hide (w);
5620             popdown_visible = false;
5621             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5622             return TRUE; // I consumed the event
5623             break;
5624         case GDK_w:
5625         case GDK_W:
5626             if (event->state & GDK_CONTROL_MASK) {
5627                 gtk_widget_hide (w);
5628                 popdown_visible = false;
5629                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5630                 return TRUE; // I consumed the event
5631             }
5632             break;
5633     }
5634     return FALSE;
5638 void
5639 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
5640                                GObject     *tbl)
5642     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5644     if (g_object_get_data (tbl, "size-block")) return;
5646     // If this is not from selecting a size in the list (in which case get_active will give the
5647     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5648     // process this event. This fixes GTK's stupid insistence on sending an activate change every
5649     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5650     if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5651         return;
5653     gdouble value = -1;
5654     {
5655         gchar *endptr;
5656         gchar *const text = gtk_combo_box_get_active_text(cbox);
5657         if (text) {
5658             value = g_strtod(text, &endptr);
5659             if (endptr == text) {  // Conversion failed, non-numeric input.
5660                 value = -1;
5661             }
5662             g_free(text);
5663         }
5664     }
5665     if (value <= 0) {
5666         return; // could not parse value
5667     }
5669     SPCSSAttr *css = sp_repr_css_attr_new ();
5670     Inkscape::CSSOStringStream osfs;
5671     osfs << value;
5672     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5674     SPStyle *query =
5675         sp_style_new (SP_ACTIVE_DOCUMENT);
5676     int result_numbers =
5677         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5679     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5680     if (result_numbers == QUERY_STYLE_NOTHING)
5681     {
5682         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5683     }
5685     sp_style_unref(query);
5687     sp_desktop_set_style (desktop, css, true, true);
5688     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5689                                    _("Text: Change font size"));
5690     sp_repr_css_attr_unref (css);
5692     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5695 gboolean
5696 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5698     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5699     if (!desktop) return FALSE;
5701     if (!g_object_get_data (tbl, "esc-pressed")) {
5702         g_object_set_data (tbl, "enter-pressed", gpointer(1));
5703         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5704         sp_text_toolbox_size_changed (cbox, tbl);
5705         g_object_set_data (tbl, "enter-pressed", gpointer(0));
5706     }
5707     return FALSE; // I consumed the event
5711 gboolean
5712 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5714     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5715     if (!desktop) return FALSE;
5717     switch (get_group0_keyval (event)) {
5718         case GDK_Escape: // defocus
5719             g_object_set_data (tbl, "esc-pressed", gpointer(1));
5720             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5721             g_object_set_data (tbl, "esc-pressed", gpointer(0));
5722             return TRUE; // I consumed the event
5723             break;
5724         case GDK_Return: // defocus
5725         case GDK_KP_Enter:
5726             g_object_set_data (tbl, "enter-pressed", gpointer(1));
5727             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5728             sp_text_toolbox_size_changed (cbox, tbl);
5729             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5730             g_object_set_data (tbl, "enter-pressed", gpointer(0));
5731             return TRUE; // I consumed the event
5732             break;
5733     }
5734     return FALSE;
5737 void
5738 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
5739                                          GObject            *tbl)
5741     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
5742     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5743     int x, y;
5745     if (!popdown_visible)
5746     {
5747         gdk_window_get_origin (widget->window, &x, &y);
5748         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
5749         gtk_widget_show_all (popdown);
5750         //sp_transientize (popdown);
5752         gdk_pointer_grab (widget->window, TRUE,
5753                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
5754                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
5755                                         GDK_POINTER_MOTION_MASK),
5756                           NULL, NULL, GDK_CURRENT_TIME);
5758         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
5760         popdown_visible = true;
5761     }
5762     else
5763     {
5764         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5765         gdk_pointer_ungrab (GDK_CURRENT_TIME);
5766         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5767         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5768         gtk_widget_hide (popdown);
5769         popdown_visible = false;
5770     }
5773 gboolean
5774 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
5775                                  GdkEventFocus    */*event*/,
5776                                  GObject          */*tbl*/)
5778     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
5779     return FALSE;
5782 gboolean
5783 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
5784                                    GdkEventFocus    */*event*/,
5785                                    GObject          */*tbl*/)
5787     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5789     if (popdown_hasfocus) {
5790         gtk_widget_hide (popdown);
5791         popdown_hasfocus = false;
5792         popdown_visible = false;
5793         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5794         return TRUE;
5795     }
5796     return FALSE;
5799 gboolean
5800 sp_text_toolbox_popdown_focus_in (GtkWidget        */*popdown*/,
5801                                    GdkEventFocus    */*event*/,
5802                                    GObject          */*tbl*/)
5804     popdown_hasfocus = true;
5805     return TRUE;
5809 void
5810 cell_data_func  (GtkTreeViewColumn */*column*/,
5811                  GtkCellRenderer   *cell,
5812                  GtkTreeModel      *tree_model,
5813                  GtkTreeIter       *iter,
5814                  gpointer           /*data*/)
5816     gchar *family;
5817     gtk_tree_model_get(tree_model, iter, 0, &family, -1);
5818     gchar *const family_escaped = g_markup_escape_text(family, -1);
5820     static char const *const sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
5821     gchar *const sample_escaped = g_markup_escape_text(sample, -1);
5823     std::stringstream markup;
5824     markup << family_escaped << "  <span foreground='darkgray' font_family='"
5825            << family_escaped << "'>" << sample_escaped << "</span>";
5826     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
5828     g_free(family);
5829     g_free(family_escaped);
5830     g_free(sample_escaped);
5833 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
5834     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
5835     if (completion) {
5836         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
5837         g_object_unref (completion);
5838     }
5841 GtkWidget*
5842 sp_text_toolbox_new (SPDesktop *desktop)
5844     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
5845     GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
5847     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
5848     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
5850     GtkTooltips *tt = gtk_tooltips_new();
5851     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
5853     ////////////Family
5854     //Window
5855     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5856     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
5858     //Entry
5859     GtkWidget           *entry = gtk_entry_new ();
5860     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
5861     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
5862     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
5863     gtk_entry_completion_set_text_column (completion, 0);
5864     gtk_entry_completion_set_minimum_key_length (completion, 1);
5865     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
5866     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
5867     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
5868     gtk_toolbar_append_widget( tbl, entry, "", "" );
5869     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
5871     //Button
5872     GtkWidget   *button = gtk_button_new ();
5873     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
5874     gtk_toolbar_append_widget( tbl, button, "", "");
5876     //Popdown
5877     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
5878     GtkWidget           *treeview = gtk_tree_view_new ();
5880     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
5881     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
5882     gtk_tree_view_column_pack_start (column, cell, FALSE);
5883     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
5884     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5885     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5887     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5888     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5889     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5891     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5893     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5894     gtk_container_add (GTK_CONTAINER (sw), treeview);
5896     gtk_container_add (GTK_CONTAINER (window), sw);
5897     gtk_widget_set_size_request (window, 300, 450);
5899     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5900     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5901     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5903     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5905     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5906     g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5907     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5909     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5910     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5912     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5913     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5914     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5915     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5916     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5918     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
5919     GtkWidget *box = gtk_event_box_new ();
5920     gtk_container_add (GTK_CONTAINER (box), image);
5921     gtk_toolbar_append_widget( tbl, box, "", "");
5922     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5923     GtkTooltips *tooltips = gtk_tooltips_new ();
5924     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5925     gtk_widget_hide (GTK_WIDGET (box));
5926     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
5928     ////////////Size
5929     gchar const *const sizes[] = {
5930         "4", "6", "8", "9", "10", "11", "12", "13", "14",
5931         "16", "18", "20", "22", "24", "28",
5932         "32", "36", "40", "48", "56", "64", "72", "144"
5933     };
5935     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
5936     for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
5937         gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
5938     }
5939     gtk_widget_set_size_request (cbox, 80, -1);
5940     gtk_toolbar_append_widget( tbl, cbox, "", "");
5941     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
5942     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
5943     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
5944     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
5946     ////////////Text anchor
5947     GtkWidget *group   = gtk_radio_button_new (NULL);
5948     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
5949     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
5951     // left
5952     GtkWidget *rbutton = group;
5953     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5954     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
5955     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5957     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5958     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
5959     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
5960     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
5962     // center
5963     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5964     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5965     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
5966     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5968     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5969     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
5970     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5971     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5973     // right
5974     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5975     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5976     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
5977     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5979     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5980     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
5981     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5982     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5984     // fill
5985     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5986     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5987     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
5988     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5990     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5991     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
5992     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
5993     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
5995     gtk_toolbar_append_widget( tbl, row, "", "");
5997     //spacer
5998     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6000     ////////////Text style
6001     row = gtk_hbox_new (FALSE, 4);
6003     // bold
6004     rbutton = gtk_toggle_button_new ();
6005     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6006     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
6007     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6008     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
6010     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6011     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
6012     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
6014     // italic
6015     rbutton = gtk_toggle_button_new ();
6016     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6017     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
6018     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6019     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
6021     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6022     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
6023     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
6025     gtk_toolbar_append_widget( tbl, row, "", "");
6027     //spacer
6028     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6030     ////////////Text orientation
6031     group   = gtk_radio_button_new (NULL);
6032     row     = gtk_hbox_new (FALSE, 4);
6033     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
6035     // horizontal
6036     rbutton = group;
6037     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6038     gtk_container_add           (GTK_CONTAINER (rbutton),
6039                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
6040     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6041     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
6043     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6044     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
6045     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
6047     // vertical
6048     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6049     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6050     gtk_container_add           (GTK_CONTAINER (rbutton),
6051                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
6052     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6053     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
6055     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6056     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
6057     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
6058     gtk_toolbar_append_widget( tbl, row, "", "" );
6061     //watch selection
6062     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
6064     sigc::connection *c_selection_changed =
6065         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
6066                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
6067     pool->add_connection ("selection-changed", c_selection_changed);
6069     sigc::connection *c_selection_modified =
6070         new sigc::connection (sp_desktop_selection (desktop)->connectModified
6071                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
6072     pool->add_connection ("selection-modified", c_selection_modified);
6074     sigc::connection *c_subselection_changed =
6075         new sigc::connection (desktop->connectToolSubselectionChanged
6076                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
6077     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
6079     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
6082     gtk_widget_show_all( GTK_WIDGET(tbl) );
6084     return GTK_WIDGET(tbl);
6085 } // end of sp_text_toolbox_new()
6087 }//<unnamed> namespace
6090 //#########################
6091 //##      Connector      ##
6092 //#########################
6094 static void sp_connector_path_set_avoid(void)
6096     cc_selection_set_avoid(true);
6100 static void sp_connector_path_set_ignore(void)
6102     cc_selection_set_avoid(false);
6107 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
6109     // quit if run by the _changed callbacks
6110     if (g_object_get_data( tbl, "freeze" )) {
6111         return;
6112     }
6114     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
6115     SPDocument *doc = sp_desktop_document(desktop);
6117     if (!sp_document_get_undo_sensitive(doc))
6118     {
6119         return;
6120     }
6122     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6124     if ( repr->attribute("inkscape:connector-spacing") ) {
6125         gdouble priorValue = gtk_adjustment_get_value(adj);
6126         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
6127         if ( priorValue == gtk_adjustment_get_value(adj) ) {
6128             return;
6129         }
6130     } else if ( adj->value == defaultConnSpacing ) {
6131         return;
6132     }
6134     // in turn, prevent callbacks from responding
6135     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6137     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
6138     SP_OBJECT(desktop->namedview)->updateRepr();
6140     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
6141     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
6142         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
6143         NR::Matrix m = NR::identity();
6144         avoid_item_move(&m, item);
6145     }
6147     if (items) {
6148         g_slist_free(items);
6149     }
6151     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
6152             _("Change connector spacing"));
6154     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6156     spinbutton_defocus(GTK_OBJECT(tbl));
6159 static void sp_connector_graph_layout(void)
6161     if (!SP_ACTIVE_DESKTOP) return;
6163     // hack for clones, see comment in align-and-distribute.cpp
6164     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
6165     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
6167     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
6169     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
6171     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
6174 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6176     if ( gtk_toggle_action_get_active( act ) ) {
6177         prefs_set_string_attribute("tools.connector", "directedlayout",
6178                 "true");
6179     } else {
6180         prefs_set_string_attribute("tools.connector", "directedlayout",
6181                 "false");
6182     }
6185 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6187     if ( gtk_toggle_action_get_active( act ) ) {
6188         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
6189                 "true");
6190     } else {
6191         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
6192                 "false");
6193     }
6197 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
6199     prefs_set_double_attribute("tools.connector", "length", adj->value);
6200     spinbutton_defocus(GTK_OBJECT(tbl));
6203 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
6204                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
6205                                             bool /*is_interactive*/, gpointer data)
6207     GtkWidget *tbl = GTK_WIDGET(data);
6209     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6210         return;
6211     }
6212     if (strcmp(name, "inkscape:connector-spacing") != 0) {
6213         return;
6214     }
6216     GtkAdjustment *adj = (GtkAdjustment*)
6217             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
6218     gdouble spacing = defaultConnSpacing;
6219     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
6221     gtk_adjustment_set_value(adj, spacing);
6225 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
6226     NULL, /* child_added */
6227     NULL, /* child_removed */
6228     connector_tb_event_attr_changed,
6229     NULL, /* content_changed */
6230     NULL  /* order_changed */
6231 };
6234 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
6236     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
6238     {
6239         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
6240                                           _("Avoid"),
6241                                           _("Make connectors avoid selected objects"),
6242                                           "connector_avoid",
6243                                           secondarySize );
6244         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
6245         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6246     }
6248     {
6249         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
6250                                           _("Ignore"),
6251                                           _("Make connectors ignore selected objects"),
6252                                           "connector_ignore",
6253                                           secondarySize );
6254         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
6255         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6256     }
6258     EgeAdjustmentAction* eact = 0;
6260     // Spacing spinbox
6261     eact = create_adjustment_action( "ConnectorSpacingAction",
6262                                      _("Connector Spacing"), _("Spacing:"),
6263                                      _("The amount of space left around objects by auto-routing connectors"),
6264                                      "tools.connector", "spacing", defaultConnSpacing,
6265                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
6266                                      0, 100, 1.0, 10.0,
6267                                      0, 0, 0,
6268                                      connector_spacing_changed, 1, 0 );
6269     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6271     // Graph (connector network) layout
6272     {
6273         InkAction* inky = ink_action_new( "ConnectorGraphAction",
6274                                           _("Graph"),
6275                                           _("Nicely arrange selected connector network"),
6276                                           "graph_layout",
6277                                           secondarySize );
6278         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
6279         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6280     }
6282     // Default connector length spinbox
6283     eact = create_adjustment_action( "ConnectorLengthAction",
6284                                      _("Connector Length"), _("Length:"),
6285                                      _("Ideal length for connectors when layout is applied"),
6286                                      "tools.connector", "length", 100,
6287                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
6288                                      10, 1000, 10.0, 100.0,
6289                                      0, 0, 0,
6290                                      connector_length_changed, 1, 0 );
6291     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6294     // Directed edges toggle button
6295     {
6296         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
6297                                                       _("Downwards"),
6298                                                       _("Make connectors with end-markers (arrows) point downwards"),
6299                                                       "directed_graph",
6300                                                       Inkscape::ICON_SIZE_DECORATION );
6301         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6303         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
6304         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6305                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6307         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
6308     }
6310     // Avoid overlaps toggle button
6311     {
6312         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
6313                                                       _("Remove overlaps"),
6314                                                       _("Do not allow overlapping shapes"),
6315                                                       "remove_overlaps",
6316                                                       Inkscape::ICON_SIZE_DECORATION );
6317         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6319         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
6320         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6321                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6323         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
6324     }
6326     // Code to watch for changes to the connector-spacing attribute in
6327     // the XML.
6328     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6329     g_assert(repr != NULL);
6331     purge_repr_listener( holder, holder );
6333     if (repr) {
6334         g_object_set_data( holder, "repr", repr );
6335         Inkscape::GC::anchor(repr);
6336         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
6337         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
6338     }
6339 } // end of sp_connector_toolbox_prep()
6342 //#########################
6343 //##     Paintbucket     ##
6344 //#########################
6346 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
6348     gint channels = ege_select_one_action_get_active( act );
6349     flood_channels_set_channels( channels );
6352 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
6354     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
6357 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
6359     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
6362 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
6364     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
6365     SPUnit const *unit = tracker->getActiveUnit();
6367     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
6369     prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
6372 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
6374     // FIXME: make defaults settable via Inkscape Options
6375     struct KeyValue {
6376         char const *key;
6377         double value;
6378     } const key_values[] = {
6379         {"threshold", 15},
6380         {"offset", 0.0}
6381     };
6383     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
6384         KeyValue const &kv = key_values[i];
6385         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
6386         if ( adj ) {
6387             gtk_adjustment_set_value(adj, kv.value);
6388         }
6389     }
6391     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
6392     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
6393     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
6394     ege_select_one_action_set_active( autogap_action, 0 );
6397 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6399     EgeAdjustmentAction* eact = 0;
6401     {
6402         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6404         GList* items = 0;
6405         gint count = 0;
6406         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
6407         {
6408             GtkTreeIter iter;
6409             gtk_list_store_append( model, &iter );
6410             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6411             count++;
6412         }
6413         g_list_free( items );
6414         items = 0;
6415         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
6416         g_object_set( act1, "short_label", _("Fill by:"), NULL );
6417         ege_select_one_action_set_appearance( act1, "compact" );
6418         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
6419         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
6420         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
6421         g_object_set_data( holder, "channels_action", act1 );
6422     }
6424     // Spacing spinbox
6425     {
6426         eact = create_adjustment_action(
6427             "ThresholdAction",
6428             _("Fill Threshold"), _("Threshold:"),
6429             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
6430             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
6431             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
6432             0, 0, 0,
6433             paintbucket_threshold_changed, 1, 0 );
6435         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6436         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6437     }
6439     // Create the units menu.
6440     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
6441     const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
6442     if (stored_unit)
6443         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
6444     g_object_set_data( holder, "tracker", tracker );
6445     {
6446         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
6447         gtk_action_group_add_action( mainActions, act );
6448     }
6450     // Offset spinbox
6451     {
6452         eact = create_adjustment_action(
6453             "OffsetAction",
6454             _("Grow/shrink by"), _("Grow/shrink by:"),
6455             _("The amount to grow (positive) or shrink (negative) the created fill path"),
6456             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
6457             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
6458             0, 0, 0,
6459             paintbucket_offset_changed, 1, 2);
6460         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
6462         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6463     }
6465     /* Auto Gap */
6466     {
6467         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6469         GList* items = 0;
6470         gint count = 0;
6471         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
6472         {
6473             GtkTreeIter iter;
6474             gtk_list_store_append( model, &iter );
6475             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6476             count++;
6477         }
6478         g_list_free( items );
6479         items = 0;
6480         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
6481         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
6482         ege_select_one_action_set_appearance( act2, "compact" );
6483         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
6484         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
6485         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
6486         g_object_set_data( holder, "autogap_action", act2 );
6487     }
6489     /* Reset */
6490     {
6491         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
6492                                           _("Defaults"),
6493                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
6494                                           GTK_STOCK_CLEAR );
6495         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
6496         gtk_action_group_add_action( mainActions, act );
6497         gtk_action_set_sensitive( act, TRUE );
6498     }
6502 /*
6503   Local Variables:
6504   mode:c++
6505   c-file-style:"stroustrup"
6506   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
6507   indent-tabs-mode:nil
6508   fill-column:99
6509   End:
6510 */
6511 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :