Code

71c797bffd1452e40dfc57393d94cf36dfcc6b99
[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 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
4793     using namespace Inkscape::LivePathEffect;
4795     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
4797     // TODO: how can we set *all* actions inactive (such that no sutool is activated?)
4798     gint lpeToolMode = ege_select_one_action_get_active(act);
4799     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4800         prefs_set_int_attribute( "tools.lpetool", "mode", lpeToolMode );
4801     }
4802     //EffectType type = lpesubtools[lpeToolMode];
4803     //SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
4805     // only take action if run by the attr_changed listener
4806     if (!g_object_get_data( tbl, "freeze" )) {
4807         // in turn, prevent listener from responding
4808         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4810         // this is now done in sp_lpetool_context_root_handler()
4811         /**
4812         // activate the LPE corresponding to the chosen subtool
4813         if (type != INVALID_LPE) {
4814             //lc->tool_state = LPETOOL_STATE_PEN;
4815             sp_pen_context_wait_for_LPE_mouse_clicks(pc, type, Effect::acceptsNumClicks(type));
4816         }
4817         // TODO: how can we take LPEs into account that don't expect any 'pre-clicks'?
4818         **/
4820         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4821     }
4824 static void
4825 sp_lpetool_test_value_changed(GtkAdjustment *adj, GObject *tbl)
4827     g_print ("sp_lpetool_test_value_changed()\n");
4828     using namespace Inkscape::LivePathEffect;
4830     // quit if run by the attr_changed listener
4831     if (g_object_get_data( tbl, "freeze" )) {
4832         return;
4833     }
4835     // in turn, prevent listener from responding
4836     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
4838     LPEAngleBisector *lpeab = static_cast<LPEAngleBisector *>(g_object_get_data(tbl, "currentlpe"));
4839     if (!lpeab) {
4840         g_print ("no LPE!\n");
4841     } else {
4842         g_print ("LPE found. Adjusting left length\n");
4843         SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
4844         lpeab->length_left.param_set_value(gtk_adjustment_get_value(adj));
4845         sp_lpe_item_update_patheffect(lpeitem, true, true);
4846     }
4848     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4851 void
4852 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
4854     using namespace Inkscape::LivePathEffect;
4855     g_print ("sp_lpetool_toolbox_sel_changed()");
4856     {
4857         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "lpetool_test_action" ) );
4858         SPItem *item = selection->singleItem();
4859         if (item && SP_IS_LPE_ITEM(item)) {
4860             g_print (" - item found\n");
4861             SPLPEItem *lpeitem = SP_LPE_ITEM(item);
4862             Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
4863             if (lpe && lpe->effectType() == ANGLE_BISECTOR) {
4864                 LPEAngleBisector *lpeab = dynamic_cast<LPEAngleBisector*>(lpe);
4865                 g_object_set_data(tbl, "currentlpe", lpeab);
4866                 g_object_set_data(tbl, "currentlpeitem", lpeitem);
4867                 gtk_action_set_sensitive(w, TRUE);
4868             } else {
4869                 g_object_set_data(tbl, "currentlpe", NULL);
4870                 g_object_set_data(tbl, "currentlpeitem", NULL);
4871                 gtk_action_set_sensitive(w, FALSE);
4872             }
4873         } else {
4874             g_print (" - unsetting item\n");
4875             g_object_set_data(tbl, "currentlpe", NULL);
4876             g_object_set_data(tbl, "currentlpeitem", NULL);
4877             gtk_action_set_sensitive(w, FALSE);
4878         }
4879     }
4880     g_print ("\n");
4883 static void
4884 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
4885     SPDesktop *desktop = static_cast<SPDesktop *>(data);
4887     bool show = gtk_toggle_action_get_active( act );
4888     prefs_set_int_attribute ("tools.lpetool", "show_bbox",  show ? 1 : 0);
4890     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
4891         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4892         lpetool_context_reset_limiting_bbox(lc);
4893     }
4896 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4898     /** Automatically create a list of LPEs that get added to the toolbar **/
4899     {
4900         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4902         GtkTreeIter iter;
4904         // the first toggle button represents the state that no subtool is active (remove this when
4905         // this can be modeled by EgeSelectOneAction or some other action)
4906         gtk_list_store_append( model, &iter );
4907         gtk_list_store_set( model, &iter,
4908                             0, _("All inactive"),
4909                             1, _("No geometric tool is active"),
4910                             2, _("all_inactive"),
4911                             -1 );
4913         Inkscape::LivePathEffect::EffectType type;
4914         for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
4915             type =  lpesubtools[i];
4916             gtk_list_store_append( model, &iter );
4917             gtk_list_store_set( model, &iter,
4918                                 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
4919                                 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
4920                                 2, Inkscape::LivePathEffect::LPETypeConverter.get_key(type).c_str(),
4921                                 -1 );
4922         }
4924         EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4925         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4926         g_object_set_data( holder, "lpetool_mode_action", act );
4928         ege_select_one_action_set_appearance( act, "full" );
4929         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4930         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4931         ege_select_one_action_set_icon_column( act, 2 );
4932         ege_select_one_action_set_tooltip_column( act, 1  );
4934         gint lpeToolMode = prefs_get_int_attribute("tools.lpetool", "mode", 0);
4935         ege_select_one_action_set_active( act, lpeToolMode );
4936         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
4937     }
4939     /* Show limiting bounding box */
4940     {
4941         InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
4942                                                       _("Show limiting bounding box"),
4943                                                       _("Show bounding box (is used to cut infinite lines)"),
4944                                                       "lpetool_show_bbox",
4945                                                       Inkscape::ICON_SIZE_DECORATION );
4946         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4947         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
4948         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.lpetool", "show_bbox", 1 ) );
4949     }
4951     /* Test action */
4952     /**
4953     {
4954         EgeAdjustmentAction* eact = 0;
4955         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
4956         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
4957         eact = create_adjustment_action( "TestLPEAction",
4958                                          _("Test value"), _("Test value:"), _("Test for interactive control widgets ..."),
4959                                          "tools.lpetool", "testvalue", 0,
4960                                          GTK_WIDGET(desktop->canvas), NULL us, holder, TRUE, "altx-lpetool",
4961                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
4962                                          labels, values, G_N_ELEMENTS(labels),
4963                                          sp_lpetool_test_value_changed );
4964         //tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
4965         g_object_set_data( holder, "lpetool_test_action", eact );
4966         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
4967         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4968     }
4969     **/
4971     //watch selection
4972     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
4974     sigc::connection *c_selection_changed =
4975         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
4976                               (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
4977     pool->add_connection ("selection-changed", c_selection_changed);
4980 //########################
4981 //##       Eraser       ##
4982 //########################
4984 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4986     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4987     gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
4988     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4989         prefs_set_int_attribute( "tools.eraser", "mode", eraserMode );
4990     }
4992     // only take action if run by the attr_changed listener
4993     if (!g_object_get_data( tbl, "freeze" )) {
4994         // in turn, prevent listener from responding
4995         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4997         if ( eraserMode != 0 ) {
4998         } else {
4999         }
5000         // TODO finish implementation
5002         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5003     }
5006 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5008     {
5009         /* Width */
5010         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5011         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5012         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5013                                                               _("Pen Width"), _("Width:"),
5014                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
5015                                                               "tools.eraser", "width", 15,
5016                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5017                                                               1, 100, 1.0, 0.0,
5018                                                               labels, values, G_N_ELEMENTS(labels),
5019                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
5020         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5021         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5022         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5023     }
5025     {
5026         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5028         GtkTreeIter iter;
5029         gtk_list_store_append( model, &iter );
5030         gtk_list_store_set( model, &iter,
5031                             0, _("Delete"),
5032                             1, _("Delete objects touched by the eraser"),
5033                             2, "delete_object",
5034                             -1 );
5036         gtk_list_store_append( model, &iter );
5037         gtk_list_store_set( model, &iter,
5038                             0, _("Cut"),
5039                             1, _("Cut out from objects"),
5040                             2, "difference",
5041                             -1 );
5043         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5044         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5045         g_object_set_data( holder, "eraser_mode_action", act );
5047         ege_select_one_action_set_appearance( act, "full" );
5048         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5049         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5050         ege_select_one_action_set_icon_column( act, 2 );
5051         ege_select_one_action_set_tooltip_column( act, 1  );
5053         gint eraserMode = (prefs_get_int_attribute("tools.eraser", "mode", 0) != 0) ? 1 : 0;
5054         ege_select_one_action_set_active( act, eraserMode );
5055         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
5056     }
5060 //########################
5061 //##    Text Toolbox    ##
5062 //########################
5063 /*
5064 static void
5065 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
5067     //Call back for letter sizing spinbutton
5070 static void
5071 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
5073     //Call back for line height spinbutton
5076 static void
5077 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5079     //Call back for horizontal kerning spinbutton
5082 static void
5083 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5085     //Call back for vertical kerning spinbutton
5088 static void
5089 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
5091     //Call back for letter rotation spinbutton
5092 }*/
5094 namespace {
5096 bool popdown_visible = false;
5097 bool popdown_hasfocus = false;
5099 void
5100 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
5102     SPStyle *query =
5103         sp_style_new (SP_ACTIVE_DOCUMENT);
5105 //    int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5107     int result_family =
5108         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5110     int result_style =
5111         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5113     int result_numbers =
5114         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5116     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5118     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5119     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
5120         // there are no texts in selection, read from prefs
5121  
5122         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
5123         if (repr) {
5124             sp_style_read_from_repr (query, repr);
5125             if (g_object_get_data(tbl, "text_style_from_prefs")) {
5126                 // do not reset the toolbar style from prefs if we already did it last time
5127                 sp_style_unref(query);
5128                 return;
5129             }
5130             g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
5131         } else {
5132             sp_style_unref(query);
5133             return;
5134         }
5135     } else {
5136         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
5137     }
5139     if (query->text)
5140     {
5141         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
5142             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5143             gtk_entry_set_text (GTK_ENTRY (entry), "");
5145         } else if (query->text->font_specification.value || query->text->font_family.value) {
5147             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5149             // Get the font that corresponds
5150             Glib::ustring familyName;
5152             font_instance * font = font_factory::Default()->FaceFromStyle(query);
5153             if (font) {
5154                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
5155                 font->Unref();
5156                 font = NULL;
5157             }
5159             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
5161             Gtk::TreePath path;
5162             try {
5163                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
5164             } catch (...) {
5165                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
5166                 sp_style_unref(query);
5167                 return;
5168             }
5170             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5171             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5173             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
5175             gtk_tree_selection_select_path (tselection, path.gobj());
5176             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5178             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
5179         }
5181         //Size
5182         {
5183             GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
5184             gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
5185             g_object_set_data(tbl, "size-block", gpointer(1));
5186             gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
5187             g_object_set_data(tbl, "size-block", gpointer(0));
5188             g_free(str);
5189         }
5191         //Anchor
5192         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
5193         {
5194             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
5195             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5196             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5197             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5198         }
5199         else
5200         {
5201             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
5202             {
5203                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
5204                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5205                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5206                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5207             }
5208             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
5209             {
5210                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
5211                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5212                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5213                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5214             }
5215             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
5216             {
5217                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
5218                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5219                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5220                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5221             }
5222         }
5224         //Style
5225         {
5226             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
5228             gboolean active = gtk_toggle_button_get_active (button);
5229             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
5231             if (active != check)
5232             {
5233                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5234                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5235                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5236             }
5237         }
5239         {
5240             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
5242             gboolean active = gtk_toggle_button_get_active (button);
5243             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
5245             if (active != check)
5246             {
5247                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5248                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5249                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5250             }
5251         }
5253         //Orientation
5254         //locking both buttons, changing one affect all group (both)
5255         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
5256         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5258         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
5259         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
5261         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
5262         {
5263             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5264         }
5265         else
5266         {
5267             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
5268         }
5269         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5270         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
5271     }
5273     sp_style_unref(query);
5276 void
5277 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
5279     sp_text_toolbox_selection_changed (selection, tbl);
5282 void
5283 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
5285     sp_text_toolbox_selection_changed (NULL, tbl);
5288 void
5289 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
5290                                 GObject             *tbl)
5292     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
5293     GtkTreeModel *model = 0;
5294     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5295     GtkTreeIter   iter;
5296     char         *family = 0;
5298     gdk_pointer_ungrab (GDK_CURRENT_TIME);
5299     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5301     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
5302         return;
5303     }
5305     gtk_tree_model_get (model, &iter, 0, &family, -1);
5307     if (g_object_get_data (G_OBJECT (selection), "block"))
5308     {
5309         gtk_entry_set_text (GTK_ENTRY (entry), family);
5310         return;
5311     }
5313     gtk_entry_set_text (GTK_ENTRY (entry), family);
5315     SPStyle *query =
5316         sp_style_new (SP_ACTIVE_DOCUMENT);
5318     int result_fontspec =
5319         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5321     //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5323     SPCSSAttr *css = sp_repr_css_attr_new ();
5326     // First try to get the font spec from the stored value
5327     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
5329     if (fontSpec.empty()) {
5330         // Construct a new font specification if it does not yet exist
5331         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5332         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5333         fontFromStyle->Unref();
5334     }
5336     if (!fontSpec.empty()) {
5337         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
5338         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
5339             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
5340             if (font) {
5341                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5343                 // Set all the these just in case they were altered when finding the best
5344                 // match for the new family and old style...
5346                 gchar c[256];
5348                 font->Family(c, 256);
5349                 sp_repr_css_set_property (css, "font-family", c);
5351                 font->Attribute( "weight", c, 256);
5352                 sp_repr_css_set_property (css, "font-weight", c);
5354                 font->Attribute("style", c, 256);
5355                 sp_repr_css_set_property (css, "font-style", c);
5357                 font->Attribute("stretch", c, 256);
5358                 sp_repr_css_set_property (css, "font-stretch", c);
5360                 font->Attribute("variant", c, 256);
5361                 sp_repr_css_set_property (css, "font-variant", c);
5363                 font->Unref();
5364             }
5365         }
5366     }
5368     // If querying returned nothing, set the default style of the tool (for new texts)
5369     if (result_fontspec == QUERY_STYLE_NOTHING)
5370     {
5371         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5372         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
5373     }
5374     else
5375     {
5376         sp_desktop_set_style (desktop, css, true, true);
5377     }
5379     sp_style_unref(query);
5381     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5382                                    _("Text: Change font family"));
5383     sp_repr_css_attr_unref (css);
5384     g_free(family);
5385     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5387     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5390 /* This is where execution comes when the contents of the font family box have been completed
5391    by the press of the return key */
5392 void
5393 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
5394                                        GObject      *tbl)
5396     const char *family = gtk_entry_get_text (entry);   // Fetch the requested font family
5398 // Try to match that to a known font. If not, then leave current font alone and remain focused on text box
5399     try {
5400         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
5401         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5402         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5403         gtk_tree_selection_select_path (selection, path.gobj());
5404         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5405         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5406     } catch (...) {
5407         if (family && strlen (family))
5408         {
5409             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5410         }
5411     }
5414 void
5415 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
5416                                    gpointer          data)
5418     if (g_object_get_data (G_OBJECT (button), "block")) return;
5419     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
5420     int prop = GPOINTER_TO_INT(data);
5422     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5423     SPCSSAttr *css = sp_repr_css_attr_new ();
5425     switch (prop)
5426     {
5427         case 0:
5428         {
5429             sp_repr_css_set_property (css, "text-anchor", "start");
5430             sp_repr_css_set_property (css, "text-align", "start");
5431             break;
5432         }
5433         case 1:
5434         {
5435             sp_repr_css_set_property (css, "text-anchor", "middle");
5436             sp_repr_css_set_property (css, "text-align", "center");
5437             break;
5438         }
5440         case 2:
5441         {
5442             sp_repr_css_set_property (css, "text-anchor", "end");
5443             sp_repr_css_set_property (css, "text-align", "end");
5444             break;
5445         }
5447         case 3:
5448         {
5449             sp_repr_css_set_property (css, "text-anchor", "start");
5450             sp_repr_css_set_property (css, "text-align", "justify");
5451             break;
5452         }
5453     }
5455     SPStyle *query =
5456         sp_style_new (SP_ACTIVE_DOCUMENT);
5457     int result_numbers =
5458         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5460     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5461     if (result_numbers == QUERY_STYLE_NOTHING)
5462     {
5463         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5464     }
5466     sp_style_unref(query);
5468     sp_desktop_set_style (desktop, css, true, true);
5469     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5470                                    _("Text: Change alignment"));
5471     sp_repr_css_attr_unref (css);
5473     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5476 void
5477 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
5478                                gpointer          data)
5480     if (g_object_get_data (G_OBJECT (button), "block")) return;
5482     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5483     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5484     int          prop       = GPOINTER_TO_INT(data);
5485     bool         active     = gtk_toggle_button_get_active (button);
5487     SPStyle *query =
5488         sp_style_new (SP_ACTIVE_DOCUMENT);
5490     int result_fontspec =
5491         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5493     //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5494     //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5495     //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5497     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
5498     Glib::ustring newFontSpec = "";
5500     if (fontSpec.empty()) {
5501         // Construct a new font specification if it does not yet exist
5502         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5503         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5504         fontFromStyle->Unref();
5505     }
5507     switch (prop)
5508     {
5509         case 0:
5510         {
5511             if (!fontSpec.empty()) {
5512                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
5513             }
5514             if (fontSpec != newFontSpec) {
5515                 // Don't even set the bold if the font didn't exist on the system
5516                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
5517             }
5518             break;
5519         }
5521         case 1:
5522         {
5523             if (!fontSpec.empty()) {
5524                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
5525             }
5526             if (fontSpec != newFontSpec) {
5527                 // Don't even set the italic if the font didn't exist on the system
5528                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
5529             }
5530             break;
5531         }
5532     }
5534     if (!newFontSpec.empty()) {
5535         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5536     }
5538     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5539     if (result_fontspec == QUERY_STYLE_NOTHING)
5540     {
5541         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5542     }
5544     sp_style_unref(query);
5546     sp_desktop_set_style (desktop, css, true, true);
5547     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5548                                    _("Text: Change font style"));
5549     sp_repr_css_attr_unref (css);
5551     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5554 void
5555 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
5556                                      gpointer         data)
5558     if (g_object_get_data (G_OBJECT (button), "block")) {
5559         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5560         return;
5561     }
5563     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5564     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5565     int          prop       = GPOINTER_TO_INT(data);
5567     switch (prop)
5568     {
5569         case 0:
5570         {
5571             sp_repr_css_set_property (css, "writing-mode", "lr");
5572             break;
5573         }
5575         case 1:
5576         {
5577             sp_repr_css_set_property (css, "writing-mode", "tb");
5578             break;
5579         }
5580     }
5582     SPStyle *query =
5583         sp_style_new (SP_ACTIVE_DOCUMENT);
5584     int result_numbers =
5585         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5587     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5588     if (result_numbers == QUERY_STYLE_NOTHING)
5589     {
5590         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5591     }
5593     sp_desktop_set_style (desktop, css, true, true);
5594     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5595                                    _("Text: Change orientation"));
5596     sp_repr_css_attr_unref (css);
5598     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5601 gboolean
5602 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5604     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5605     if (!desktop) return FALSE;
5607     switch (get_group0_keyval (event)) {
5608         case GDK_Escape: // defocus
5609             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5610             sp_text_toolbox_selection_changed (NULL, tbl); // update
5611             return TRUE; // I consumed the event
5612             break;
5613     }
5614     return FALSE;
5617 gboolean
5618 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5620     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5621     if (!desktop) return FALSE;
5623     switch (get_group0_keyval (event)) {
5624         case GDK_KP_Enter:
5625         case GDK_Return:
5626         case GDK_Escape: // defocus
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             break;
5632         case GDK_w:
5633         case GDK_W:
5634             if (event->state & GDK_CONTROL_MASK) {
5635                 gtk_widget_hide (w);
5636                 popdown_visible = false;
5637                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5638                 return TRUE; // I consumed the event
5639             }
5640             break;
5641     }
5642     return FALSE;
5646 void
5647 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
5648                                GObject     *tbl)
5650     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5652     if (g_object_get_data (tbl, "size-block")) return;
5654     // If this is not from selecting a size in the list (in which case get_active will give the
5655     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5656     // process this event. This fixes GTK's stupid insistence on sending an activate change every
5657     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5658     if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5659         return;
5661     gdouble value = -1;
5662     {
5663         gchar *endptr;
5664         gchar *const text = gtk_combo_box_get_active_text(cbox);
5665         if (text) {
5666             value = g_strtod(text, &endptr);
5667             if (endptr == text) {  // Conversion failed, non-numeric input.
5668                 value = -1;
5669             }
5670             g_free(text);
5671         }
5672     }
5673     if (value <= 0) {
5674         return; // could not parse value
5675     }
5677     SPCSSAttr *css = sp_repr_css_attr_new ();
5678     Inkscape::CSSOStringStream osfs;
5679     osfs << value;
5680     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5682     SPStyle *query =
5683         sp_style_new (SP_ACTIVE_DOCUMENT);
5684     int result_numbers =
5685         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5687     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5688     if (result_numbers == QUERY_STYLE_NOTHING)
5689     {
5690         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5691     }
5693     sp_style_unref(query);
5695     sp_desktop_set_style (desktop, css, true, true);
5696     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5697                                    _("Text: Change font size"));
5698     sp_repr_css_attr_unref (css);
5700     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5703 gboolean
5704 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5706     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5707     if (!desktop) return FALSE;
5709     if (!g_object_get_data (tbl, "esc-pressed")) {
5710         g_object_set_data (tbl, "enter-pressed", gpointer(1));
5711         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5712         sp_text_toolbox_size_changed (cbox, tbl);
5713         g_object_set_data (tbl, "enter-pressed", gpointer(0));
5714     }
5715     return FALSE; // I consumed the event
5719 gboolean
5720 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5722     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5723     if (!desktop) return FALSE;
5725     switch (get_group0_keyval (event)) {
5726         case GDK_Escape: // defocus
5727             g_object_set_data (tbl, "esc-pressed", gpointer(1));
5728             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5729             g_object_set_data (tbl, "esc-pressed", gpointer(0));
5730             return TRUE; // I consumed the event
5731             break;
5732         case GDK_Return: // defocus
5733         case GDK_KP_Enter:
5734             g_object_set_data (tbl, "enter-pressed", gpointer(1));
5735             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5736             sp_text_toolbox_size_changed (cbox, tbl);
5737             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5738             g_object_set_data (tbl, "enter-pressed", gpointer(0));
5739             return TRUE; // I consumed the event
5740             break;
5741     }
5742     return FALSE;
5745 void
5746 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
5747                                          GObject            *tbl)
5749     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
5750     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5751     int x, y;
5753     if (!popdown_visible)
5754     {
5755         gdk_window_get_origin (widget->window, &x, &y);
5756         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
5757         gtk_widget_show_all (popdown);
5758         //sp_transientize (popdown);
5760         gdk_pointer_grab (widget->window, TRUE,
5761                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
5762                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
5763                                         GDK_POINTER_MOTION_MASK),
5764                           NULL, NULL, GDK_CURRENT_TIME);
5766         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
5768         popdown_visible = true;
5769     }
5770     else
5771     {
5772         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5773         gdk_pointer_ungrab (GDK_CURRENT_TIME);
5774         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5775         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5776         gtk_widget_hide (popdown);
5777         popdown_visible = false;
5778     }
5781 gboolean
5782 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
5783                                  GdkEventFocus    */*event*/,
5784                                  GObject          */*tbl*/)
5786     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
5787     return FALSE;
5790 gboolean
5791 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
5792                                    GdkEventFocus    */*event*/,
5793                                    GObject          */*tbl*/)
5795     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5797     if (popdown_hasfocus) {
5798         gtk_widget_hide (popdown);
5799         popdown_hasfocus = false;
5800         popdown_visible = false;
5801         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5802         return TRUE;
5803     }
5804     return FALSE;
5807 gboolean
5808 sp_text_toolbox_popdown_focus_in (GtkWidget        */*popdown*/,
5809                                    GdkEventFocus    */*event*/,
5810                                    GObject          */*tbl*/)
5812     popdown_hasfocus = true;
5813     return TRUE;
5817 void
5818 cell_data_func  (GtkTreeViewColumn */*column*/,
5819                  GtkCellRenderer   *cell,
5820                  GtkTreeModel      *tree_model,
5821                  GtkTreeIter       *iter,
5822                  gpointer           /*data*/)
5824     gchar *family;
5825     gtk_tree_model_get(tree_model, iter, 0, &family, -1);
5826     gchar *const family_escaped = g_markup_escape_text(family, -1);
5828     static char const *const sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
5829     gchar *const sample_escaped = g_markup_escape_text(sample, -1);
5831     std::stringstream markup;
5832     markup << family_escaped << "  <span foreground='darkgray' font_family='"
5833            << family_escaped << "'>" << sample_escaped << "</span>";
5834     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
5836     g_free(family);
5837     g_free(family_escaped);
5838     g_free(sample_escaped);
5841 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
5842     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
5843     if (completion) {
5844         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
5845         g_object_unref (completion);
5846     }
5849 GtkWidget*
5850 sp_text_toolbox_new (SPDesktop *desktop)
5852     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
5853     GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
5855     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
5856     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
5858     GtkTooltips *tt = gtk_tooltips_new();
5859     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
5861     ////////////Family
5862     //Window
5863     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5864     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
5866     //Entry
5867     GtkWidget           *entry = gtk_entry_new ();
5868     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
5869     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
5870     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
5871     gtk_entry_completion_set_text_column (completion, 0);
5872     gtk_entry_completion_set_minimum_key_length (completion, 1);
5873     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
5874     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
5875     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
5876     gtk_toolbar_append_widget( tbl, entry, "", "" );
5877     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
5879     //Button
5880     GtkWidget   *button = gtk_button_new ();
5881     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
5882     gtk_toolbar_append_widget( tbl, button, "", "");
5884     //Popdown
5885     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
5886     GtkWidget           *treeview = gtk_tree_view_new ();
5888     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
5889     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
5890     gtk_tree_view_column_pack_start (column, cell, FALSE);
5891     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
5892     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5893     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5895     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5896     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5897     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5899     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5901     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5902     gtk_container_add (GTK_CONTAINER (sw), treeview);
5904     gtk_container_add (GTK_CONTAINER (window), sw);
5905     gtk_widget_set_size_request (window, 300, 450);
5907     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5908     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5909     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5911     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5913     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5914     g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5915     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5917     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5918     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5920     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5921     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5922     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5923     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5924     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5926     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
5927     GtkWidget *box = gtk_event_box_new ();
5928     gtk_container_add (GTK_CONTAINER (box), image);
5929     gtk_toolbar_append_widget( tbl, box, "", "");
5930     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5931     GtkTooltips *tooltips = gtk_tooltips_new ();
5932     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5933     gtk_widget_hide (GTK_WIDGET (box));
5934     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
5936     ////////////Size
5937     gchar const *const sizes[] = {
5938         "4", "6", "8", "9", "10", "11", "12", "13", "14",
5939         "16", "18", "20", "22", "24", "28",
5940         "32", "36", "40", "48", "56", "64", "72", "144"
5941     };
5943     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
5944     for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
5945         gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
5946     }
5947     gtk_widget_set_size_request (cbox, 80, -1);
5948     gtk_toolbar_append_widget( tbl, cbox, "", "");
5949     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
5950     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
5951     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
5952     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
5954     ////////////Text anchor
5955     GtkWidget *group   = gtk_radio_button_new (NULL);
5956     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
5957     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
5959     // left
5960     GtkWidget *rbutton = group;
5961     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5962     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
5963     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5965     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5966     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
5967     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
5968     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
5970     // center
5971     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5972     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5973     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
5974     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5976     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5977     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
5978     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5979     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5981     // right
5982     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5983     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5984     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
5985     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5987     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5988     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
5989     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5990     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5992     // fill
5993     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5994     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5995     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
5996     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5998     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5999     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
6000     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
6001     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
6003     gtk_toolbar_append_widget( tbl, row, "", "");
6005     //spacer
6006     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6008     ////////////Text style
6009     row = gtk_hbox_new (FALSE, 4);
6011     // bold
6012     rbutton = gtk_toggle_button_new ();
6013     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6014     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
6015     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6016     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
6018     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6019     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
6020     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
6022     // italic
6023     rbutton = gtk_toggle_button_new ();
6024     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6025     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
6026     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6027     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
6029     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6030     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
6031     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
6033     gtk_toolbar_append_widget( tbl, row, "", "");
6035     //spacer
6036     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6038     ////////////Text orientation
6039     group   = gtk_radio_button_new (NULL);
6040     row     = gtk_hbox_new (FALSE, 4);
6041     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
6043     // horizontal
6044     rbutton = group;
6045     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6046     gtk_container_add           (GTK_CONTAINER (rbutton),
6047                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
6048     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6049     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
6051     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6052     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
6053     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
6055     // vertical
6056     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6057     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6058     gtk_container_add           (GTK_CONTAINER (rbutton),
6059                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
6060     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6061     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
6063     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6064     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
6065     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
6066     gtk_toolbar_append_widget( tbl, row, "", "" );
6069     //watch selection
6070     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
6072     sigc::connection *c_selection_changed =
6073         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
6074                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
6075     pool->add_connection ("selection-changed", c_selection_changed);
6077     sigc::connection *c_selection_modified =
6078         new sigc::connection (sp_desktop_selection (desktop)->connectModified
6079                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
6080     pool->add_connection ("selection-modified", c_selection_modified);
6082     sigc::connection *c_subselection_changed =
6083         new sigc::connection (desktop->connectToolSubselectionChanged
6084                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
6085     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
6087     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
6090     gtk_widget_show_all( GTK_WIDGET(tbl) );
6092     return GTK_WIDGET(tbl);
6093 } // end of sp_text_toolbox_new()
6095 }//<unnamed> namespace
6098 //#########################
6099 //##      Connector      ##
6100 //#########################
6102 static void sp_connector_path_set_avoid(void)
6104     cc_selection_set_avoid(true);
6108 static void sp_connector_path_set_ignore(void)
6110     cc_selection_set_avoid(false);
6115 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
6117     // quit if run by the _changed callbacks
6118     if (g_object_get_data( tbl, "freeze" )) {
6119         return;
6120     }
6122     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
6123     SPDocument *doc = sp_desktop_document(desktop);
6125     if (!sp_document_get_undo_sensitive(doc))
6126     {
6127         return;
6128     }
6130     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6132     if ( repr->attribute("inkscape:connector-spacing") ) {
6133         gdouble priorValue = gtk_adjustment_get_value(adj);
6134         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
6135         if ( priorValue == gtk_adjustment_get_value(adj) ) {
6136             return;
6137         }
6138     } else if ( adj->value == defaultConnSpacing ) {
6139         return;
6140     }
6142     // in turn, prevent callbacks from responding
6143     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6145     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
6146     SP_OBJECT(desktop->namedview)->updateRepr();
6148     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
6149     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
6150         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
6151         NR::Matrix m = NR::identity();
6152         avoid_item_move(&m, item);
6153     }
6155     if (items) {
6156         g_slist_free(items);
6157     }
6159     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
6160             _("Change connector spacing"));
6162     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6164     spinbutton_defocus(GTK_OBJECT(tbl));
6167 static void sp_connector_graph_layout(void)
6169     if (!SP_ACTIVE_DESKTOP) return;
6171     // hack for clones, see comment in align-and-distribute.cpp
6172     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
6173     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
6175     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
6177     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
6179     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
6182 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6184     if ( gtk_toggle_action_get_active( act ) ) {
6185         prefs_set_string_attribute("tools.connector", "directedlayout",
6186                 "true");
6187     } else {
6188         prefs_set_string_attribute("tools.connector", "directedlayout",
6189                 "false");
6190     }
6193 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6195     if ( gtk_toggle_action_get_active( act ) ) {
6196         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
6197                 "true");
6198     } else {
6199         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
6200                 "false");
6201     }
6205 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
6207     prefs_set_double_attribute("tools.connector", "length", adj->value);
6208     spinbutton_defocus(GTK_OBJECT(tbl));
6211 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
6212                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
6213                                             bool /*is_interactive*/, gpointer data)
6215     GtkWidget *tbl = GTK_WIDGET(data);
6217     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6218         return;
6219     }
6220     if (strcmp(name, "inkscape:connector-spacing") != 0) {
6221         return;
6222     }
6224     GtkAdjustment *adj = (GtkAdjustment*)
6225             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
6226     gdouble spacing = defaultConnSpacing;
6227     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
6229     gtk_adjustment_set_value(adj, spacing);
6233 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
6234     NULL, /* child_added */
6235     NULL, /* child_removed */
6236     connector_tb_event_attr_changed,
6237     NULL, /* content_changed */
6238     NULL  /* order_changed */
6239 };
6242 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
6244     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
6246     {
6247         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
6248                                           _("Avoid"),
6249                                           _("Make connectors avoid selected objects"),
6250                                           "connector_avoid",
6251                                           secondarySize );
6252         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
6253         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6254     }
6256     {
6257         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
6258                                           _("Ignore"),
6259                                           _("Make connectors ignore selected objects"),
6260                                           "connector_ignore",
6261                                           secondarySize );
6262         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
6263         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6264     }
6266     EgeAdjustmentAction* eact = 0;
6268     // Spacing spinbox
6269     eact = create_adjustment_action( "ConnectorSpacingAction",
6270                                      _("Connector Spacing"), _("Spacing:"),
6271                                      _("The amount of space left around objects by auto-routing connectors"),
6272                                      "tools.connector", "spacing", defaultConnSpacing,
6273                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
6274                                      0, 100, 1.0, 10.0,
6275                                      0, 0, 0,
6276                                      connector_spacing_changed, 1, 0 );
6277     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6279     // Graph (connector network) layout
6280     {
6281         InkAction* inky = ink_action_new( "ConnectorGraphAction",
6282                                           _("Graph"),
6283                                           _("Nicely arrange selected connector network"),
6284                                           "graph_layout",
6285                                           secondarySize );
6286         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
6287         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6288     }
6290     // Default connector length spinbox
6291     eact = create_adjustment_action( "ConnectorLengthAction",
6292                                      _("Connector Length"), _("Length:"),
6293                                      _("Ideal length for connectors when layout is applied"),
6294                                      "tools.connector", "length", 100,
6295                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
6296                                      10, 1000, 10.0, 100.0,
6297                                      0, 0, 0,
6298                                      connector_length_changed, 1, 0 );
6299     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6302     // Directed edges toggle button
6303     {
6304         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
6305                                                       _("Downwards"),
6306                                                       _("Make connectors with end-markers (arrows) point downwards"),
6307                                                       "directed_graph",
6308                                                       Inkscape::ICON_SIZE_DECORATION );
6309         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6311         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
6312         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6313                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6315         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
6316     }
6318     // Avoid overlaps toggle button
6319     {
6320         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
6321                                                       _("Remove overlaps"),
6322                                                       _("Do not allow overlapping shapes"),
6323                                                       "remove_overlaps",
6324                                                       Inkscape::ICON_SIZE_DECORATION );
6325         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6327         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
6328         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6329                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6331         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
6332     }
6334     // Code to watch for changes to the connector-spacing attribute in
6335     // the XML.
6336     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6337     g_assert(repr != NULL);
6339     purge_repr_listener( holder, holder );
6341     if (repr) {
6342         g_object_set_data( holder, "repr", repr );
6343         Inkscape::GC::anchor(repr);
6344         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
6345         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
6346     }
6347 } // end of sp_connector_toolbox_prep()
6350 //#########################
6351 //##     Paintbucket     ##
6352 //#########################
6354 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
6356     gint channels = ege_select_one_action_get_active( act );
6357     flood_channels_set_channels( channels );
6360 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
6362     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
6365 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
6367     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
6370 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
6372     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
6373     SPUnit const *unit = tracker->getActiveUnit();
6375     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
6377     prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
6380 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
6382     // FIXME: make defaults settable via Inkscape Options
6383     struct KeyValue {
6384         char const *key;
6385         double value;
6386     } const key_values[] = {
6387         {"threshold", 15},
6388         {"offset", 0.0}
6389     };
6391     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
6392         KeyValue const &kv = key_values[i];
6393         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
6394         if ( adj ) {
6395             gtk_adjustment_set_value(adj, kv.value);
6396         }
6397     }
6399     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
6400     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
6401     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
6402     ege_select_one_action_set_active( autogap_action, 0 );
6405 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6407     EgeAdjustmentAction* eact = 0;
6409     {
6410         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6412         GList* items = 0;
6413         gint count = 0;
6414         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
6415         {
6416             GtkTreeIter iter;
6417             gtk_list_store_append( model, &iter );
6418             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6419             count++;
6420         }
6421         g_list_free( items );
6422         items = 0;
6423         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
6424         g_object_set( act1, "short_label", _("Fill by:"), NULL );
6425         ege_select_one_action_set_appearance( act1, "compact" );
6426         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
6427         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
6428         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
6429         g_object_set_data( holder, "channels_action", act1 );
6430     }
6432     // Spacing spinbox
6433     {
6434         eact = create_adjustment_action(
6435             "ThresholdAction",
6436             _("Fill Threshold"), _("Threshold:"),
6437             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
6438             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
6439             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
6440             0, 0, 0,
6441             paintbucket_threshold_changed, 1, 0 );
6443         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6444         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6445     }
6447     // Create the units menu.
6448     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
6449     const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
6450     if (stored_unit)
6451         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
6452     g_object_set_data( holder, "tracker", tracker );
6453     {
6454         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
6455         gtk_action_group_add_action( mainActions, act );
6456     }
6458     // Offset spinbox
6459     {
6460         eact = create_adjustment_action(
6461             "OffsetAction",
6462             _("Grow/shrink by"), _("Grow/shrink by:"),
6463             _("The amount to grow (positive) or shrink (negative) the created fill path"),
6464             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
6465             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
6466             0, 0, 0,
6467             paintbucket_offset_changed, 1, 2);
6468         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
6470         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6471     }
6473     /* Auto Gap */
6474     {
6475         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6477         GList* items = 0;
6478         gint count = 0;
6479         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
6480         {
6481             GtkTreeIter iter;
6482             gtk_list_store_append( model, &iter );
6483             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6484             count++;
6485         }
6486         g_list_free( items );
6487         items = 0;
6488         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
6489         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
6490         ege_select_one_action_set_appearance( act2, "compact" );
6491         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
6492         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
6493         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
6494         g_object_set_data( holder, "autogap_action", act2 );
6495     }
6497     /* Reset */
6498     {
6499         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
6500                                           _("Defaults"),
6501                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
6502                                           GTK_STOCK_CLEAR );
6503         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
6504         gtk_action_group_add_action( mainActions, act );
6505         gtk_action_set_sensitive( act, TRUE );
6506     }
6510 /*
6511   Local Variables:
6512   mode:c++
6513   c-file-style:"stroustrup"
6514   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
6515   indent-tabs-mode:nil
6516   fill-column:99
6517   End:
6518 */
6519 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :