Code

Better position for Geometry tool in toolbox
[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         "  </toolbar>"
448         "  <toolbar name='DropperToolbar'>"
449         "    <toolitem action='DropperOpacityAction' />"
450         "    <toolitem action='DropperPickAlphaAction' />"
451         "    <toolitem action='DropperSetAlphaAction' />"
452         "  </toolbar>"
454         "  <toolbar name='ConnectorToolbar'>"
455         "    <toolitem action='ConnectorAvoidAction' />"
456         "    <toolitem action='ConnectorIgnoreAction' />"
457         "    <toolitem action='ConnectorSpacingAction' />"
458         "    <toolitem action='ConnectorGraphAction' />"
459         "    <toolitem action='ConnectorLengthAction' />"
460         "    <toolitem action='ConnectorDirectedAction' />"
461         "    <toolitem action='ConnectorOverlapAction' />"
462         "  </toolbar>"
464         "</ui>"
467 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
469 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
471 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
472 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
474 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
475 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
477 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
478 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
481 GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick( GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
482                                                               Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
483                                                               Inkscape::UI::View::View *view, GtkTooltips *tt);
485 class VerbAction : public Gtk::Action {
486 public:
487     static Glib::RefPtr<VerbAction> create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
489     virtual ~VerbAction();
490     virtual void set_active(bool active = true);
492 protected:
493     virtual Gtk::Widget* create_menu_item_vfunc();
494     virtual Gtk::Widget* create_tool_item_vfunc();
496     virtual void connect_proxy_vfunc(Gtk::Widget* proxy);
497     virtual void disconnect_proxy_vfunc(Gtk::Widget* proxy);
499     virtual void on_activate();
501 private:
502     Inkscape::Verb* verb;
503     Inkscape::Verb* verb2;
504     Inkscape::UI::View::View *view;
505     GtkTooltips *tooltips;
506     bool active;
508     VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
509 };
512 Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips)
514     Glib::RefPtr<VerbAction> result;
515     SPAction *action = verb->get_action(view);
516     if ( action ) {
517         //SPAction* action2 = verb2 ? verb2->get_action(view) : 0;
518         result = Glib::RefPtr<VerbAction>(new VerbAction(verb, verb2, view, tooltips));
519     }
521     return result;
524 VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips) :
525     Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(verb->get_image()), Glib::ustring(_(verb->get_name())), Glib::ustring(_(verb->get_tip()))),
526     verb(verb),
527     verb2(verb2),
528     view(view),
529     tooltips(tooltips),
530     active(false)
534 VerbAction::~VerbAction()
538 Gtk::Widget* VerbAction::create_menu_item_vfunc()
540 // First call in to get the icon rendered if present in SVG
541     Gtk::Widget *widget = sp_icon_get_icon( property_stock_id().get_value().get_string(), Inkscape::ICON_SIZE_MENU );
542     delete widget;
543     widget = 0;
545     Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
546 //     g_message("create_menu_item_vfunc() = %p  for '%s'", widg, verb->get_id());
547     return widg;
550 Gtk::Widget* VerbAction::create_tool_item_vfunc()
552 //     Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
553     Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
554     GtkWidget* toolbox = 0;
555     GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
556                                                                           SP_BUTTON_TYPE_TOGGLE,
557                                                                           verb,
558                                                                           verb2,
559                                                                           view,
560                                                                           tooltips );
561     if ( active ) {
562         sp_button_toggle_set_down( SP_BUTTON(button), active);
563     }
564     gtk_widget_show_all( button );
565     Gtk::Widget* wrapped = Glib::wrap(button);
566     Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
567     holder->add(*wrapped);
569 //     g_message("create_tool_item_vfunc() = %p  for '%s'", holder, verb->get_id());
570     return holder;
573 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
575 //     g_message("connect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
576     Gtk::Action::connect_proxy_vfunc(proxy);
579 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
581 //     g_message("disconnect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
582     Gtk::Action::disconnect_proxy_vfunc(proxy);
585 void VerbAction::set_active(bool active)
587     this->active = active;
588     Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
589     for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
590         Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
591         if (ti) {
592             // *should* have one child that is the SPButton
593             Gtk::Widget* child = ti->get_child();
594             if ( child && SP_IS_BUTTON(child->gobj()) ) {
595                 SPButton* button = SP_BUTTON(child->gobj());
596                 sp_button_toggle_set_down( button, active );
597             }
598         }
599     }
602 void VerbAction::on_activate()
604     if ( verb ) {
605         SPAction *action = verb->get_action(view);
606         if ( action ) {
607             sp_action_perform(action, 0);
608         }
609     }
612 /* Global text entry widgets necessary for update */
613 /* GtkWidget *dropper_rgb_entry,
614           *dropper_opacity_entry ; */
615 // should be made a private member once this is converted to class
617 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
618     connection->disconnect();
619     delete connection;
622 static void purge_repr_listener( GObject* obj, GObject* tbl )
624     (void)obj;
625     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
626     if (oldrepr) { // remove old listener
627         sp_repr_remove_listener_by_data(oldrepr, tbl);
628         Inkscape::GC::release(oldrepr);
629         oldrepr = 0;
630         g_object_set_data( tbl, "repr", NULL );
631     }
634 GtkWidget *
635 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
636                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
637                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
639     SPAction *action = verb->get_action(view);
640     if (!action) return NULL;
642     SPAction *doubleclick_action;
643     if (doubleclick_verb)
644         doubleclick_action = doubleclick_verb->get_action(view);
645     else
646         doubleclick_action = NULL;
648     /* fixme: Handle sensitive/unsensitive */
649     /* fixme: Implement sp_button_new_from_action */
650     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
651     gtk_widget_show(b);
654     unsigned int shortcut = sp_shortcut_get_primary(verb);
655     if (shortcut) {
656         gchar key[256];
657         sp_ui_shortcut_string(shortcut, key);
658         gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
659         if ( t ) {
660             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
661         }
662         g_free(tip);
663     } else {
664         if ( t ) {
665             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
666         }
667     }
669     return b;
673 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
675     SPAction* targetAction = SP_ACTION(user_data);
676     if ( targetAction ) {
677         sp_action_perform( targetAction, NULL );
678     }
681 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
683     if ( data ) {
684         GtkAction* act = GTK_ACTION(data);
685         gtk_action_set_sensitive( act, sensitive );
686     }
689 static SPActionEventVector action_event_vector = {
690     {NULL},
691     NULL,
692     NULL,
693     sp_action_action_set_sensitive,
694     NULL,
695     NULL
696 };
698 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
700     GtkAction* act = 0;
702     SPAction* targetAction = verb->get_action(view);
703     InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size  );
704     act = GTK_ACTION(inky);
705     gtk_action_set_sensitive( act, targetAction->sensitive );
707     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
709     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
710     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
712     return act;
715 Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
717     Inkscape::UI::View::View *view = desktop;
718     gint verbsToUse[] = {
719         // disabled until we have icons for them:
720         //find
721         //SP_VERB_EDIT_TILE,
722         //SP_VERB_EDIT_UNTILE,
723         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
724         SP_VERB_DIALOG_DISPLAY,
725         SP_VERB_DIALOG_FILL_STROKE,
726         SP_VERB_DIALOG_NAMEDVIEW,
727         SP_VERB_DIALOG_TEXT,
728         SP_VERB_DIALOG_XML_EDITOR,
729         SP_VERB_EDIT_CLONE,
730         SP_VERB_EDIT_COPY,
731         SP_VERB_EDIT_CUT,
732         SP_VERB_EDIT_DUPLICATE,
733         SP_VERB_EDIT_PASTE,
734         SP_VERB_EDIT_REDO,
735         SP_VERB_EDIT_UNDO,
736         SP_VERB_EDIT_UNLINK_CLONE,
737         SP_VERB_FILE_EXPORT,
738         SP_VERB_FILE_IMPORT,
739         SP_VERB_FILE_NEW,
740         SP_VERB_FILE_OPEN,
741         SP_VERB_FILE_PRINT,
742         SP_VERB_FILE_SAVE,
743         SP_VERB_OBJECT_TO_CURVE,
744         SP_VERB_SELECTION_GROUP,
745         SP_VERB_SELECTION_OUTLINE,
746         SP_VERB_SELECTION_UNGROUP,
747         SP_VERB_ZOOM_1_1,
748         SP_VERB_ZOOM_1_2,
749         SP_VERB_ZOOM_2_1,
750         SP_VERB_ZOOM_DRAWING,
751         SP_VERB_ZOOM_IN,
752         SP_VERB_ZOOM_NEXT,
753         SP_VERB_ZOOM_OUT,
754         SP_VERB_ZOOM_PAGE,
755         SP_VERB_ZOOM_PAGE_WIDTH,
756         SP_VERB_ZOOM_PREV,
757         SP_VERB_ZOOM_SELECTION,
758     };
760     Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
762     static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
763     Glib::RefPtr<Gtk::ActionGroup> mainActions;
764     if ( groups.find(desktop) != groups.end() ) {
765         mainActions = groups[desktop];
766     }
768     if ( !mainActions ) {
769         mainActions = Gtk::ActionGroup::create("main");
770         groups[desktop] = mainActions;
771     }
773     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
774         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
775         if ( verb ) {
776             if (!mainActions->get_action(verb->get_id())) {
777                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
778                 mainActions->add(Glib::wrap(act));
779             }
780         }
781     }
783     if ( !mainActions->get_action("ToolZoom") ) {
784         GtkTooltips *tt = gtk_tooltips_new();
785         for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
786             Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
787             if ( va ) {
788                 mainActions->add(va);
789                 if ( i == 0 ) {
790                     va->set_active(true);
791                 }
792             }
793         }
794     }
797     return mainActions;
801 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
803     gtk_widget_set_size_request( widget,
804                                  widget->allocation.width,
805                                  widget->allocation.height );
808 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
810     gtk_widget_set_size_request( widget, -1, -1 );
815 GtkWidget *
816 sp_tool_toolbox_new()
818     GtkTooltips *tt = gtk_tooltips_new();
819     GtkWidget* tb = gtk_toolbar_new();
820     gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
821     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
823     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
824     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
826     gtk_widget_set_sensitive(tb, FALSE);
828     GtkWidget *hb = gtk_handle_box_new();
829     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
830     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
831     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
833     gtk_container_add(GTK_CONTAINER(hb), tb);
834     gtk_widget_show(GTK_WIDGET(tb));
836     sigc::connection* conn = new sigc::connection;
837     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
839     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
840     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
842     return hb;
845 GtkWidget *
846 sp_aux_toolbox_new()
848     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
850     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
852     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
854     gtk_widget_set_sensitive(tb, FALSE);
856     GtkWidget *hb = gtk_handle_box_new();
857     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
858     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
859     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
861     gtk_container_add(GTK_CONTAINER(hb), tb);
862     gtk_widget_show(GTK_WIDGET(tb));
864     sigc::connection* conn = new sigc::connection;
865     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
867     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
868     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
870     return hb;
873 //####################################
874 //# Commands Bar
875 //####################################
877 GtkWidget *
878 sp_commands_toolbox_new()
880     GtkWidget *tb = gtk_toolbar_new();
882     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
883     gtk_widget_set_sensitive(tb, FALSE);
885     GtkWidget *hb = gtk_handle_box_new();
886     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
887     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
888     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
890     gtk_container_add(GTK_CONTAINER(hb), tb);
891     gtk_widget_show(GTK_WIDGET(tb));
893     sigc::connection* conn = new sigc::connection;
894     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
896     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
897     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
899     return hb;
903 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
904                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
905                                                        gchar const *path, gchar const *data, gdouble def,
906                                                        GtkWidget *focusTarget,
907                                                        GtkWidget *us,
908                                                        GObject *dataKludge,
909                                                        gboolean altx, gchar const *altx_mark,
910                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
911                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
912                                                        void (*callback)(GtkAdjustment *, GObject *),
913                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
915     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
916                                                              lower, upper, step, page, page ) );
917     if (us) {
918         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
919     }
921     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
923     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
924     if ( shortLabel ) {
925         g_object_set( act, "short_label", shortLabel, NULL );
926     }
928     if ( (descrCount > 0) && descrLabels && descrValues ) {
929         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
930     }
932     if ( focusTarget ) {
933         ege_adjustment_action_set_focuswidget( act, focusTarget );
934     }
936     if ( altx && altx_mark ) {
937         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
938     }
940     if ( dataKludge ) {
941         g_object_set_data( dataKludge, data, adj );
942     }
944     // Using a cast just to make sure we pass in the right kind of function pointer
945     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
947     return act;
951 //####################################
952 //# node editing callbacks
953 //####################################
955 /**
956  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
957  */
958 static ShapeEditor *get_current_shape_editor()
960     if (!SP_ACTIVE_DESKTOP) {
961         return NULL;
962     }
964     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
966     if (!SP_IS_NODE_CONTEXT(event_context)) {
967         return NULL;
968     }
970     return SP_NODE_CONTEXT(event_context)->shape_editor;
974 void
975 sp_node_path_edit_add(void)
977     ShapeEditor *shape_editor = get_current_shape_editor();
978     if (shape_editor) shape_editor->add_node();
981 void
982 sp_node_path_edit_delete(void)
984     ShapeEditor *shape_editor = get_current_shape_editor();
985     if (shape_editor) shape_editor->delete_nodes_preserving_shape();
988 void
989 sp_node_path_edit_delete_segment(void)
991     ShapeEditor *shape_editor = get_current_shape_editor();
992     if (shape_editor) shape_editor->delete_segment();
995 void
996 sp_node_path_edit_break(void)
998     ShapeEditor *shape_editor = get_current_shape_editor();
999     if (shape_editor) shape_editor->break_at_nodes();
1002 void
1003 sp_node_path_edit_join(void)
1005     ShapeEditor *shape_editor = get_current_shape_editor();
1006     if (shape_editor) shape_editor->join_nodes();
1009 void
1010 sp_node_path_edit_join_segment(void)
1012     ShapeEditor *shape_editor = get_current_shape_editor();
1013     if (shape_editor) shape_editor->join_segments();
1016 void
1017 sp_node_path_edit_toline(void)
1019     ShapeEditor *shape_editor = get_current_shape_editor();
1020     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1023 void
1024 sp_node_path_edit_tocurve(void)
1026     ShapeEditor *shape_editor = get_current_shape_editor();
1027     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1030 void
1031 sp_node_path_edit_cusp(void)
1033     ShapeEditor *shape_editor = get_current_shape_editor();
1034     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1037 void
1038 sp_node_path_edit_smooth(void)
1040     ShapeEditor *shape_editor = get_current_shape_editor();
1041     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1044 void
1045 sp_node_path_edit_symmetrical(void)
1047     ShapeEditor *shape_editor = get_current_shape_editor();
1048     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1051 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1052     bool show = gtk_toggle_action_get_active( act );
1053     prefs_set_int_attribute ("tools.nodes", "show_handles",  show ? 1 : 0);
1054     ShapeEditor *shape_editor = get_current_shape_editor();
1055     if (shape_editor) shape_editor->show_handles(show);
1058 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1059     bool show = gtk_toggle_action_get_active( act );
1060     prefs_set_int_attribute ("tools.nodes", "show_helperpath",  show ? 1 : 0);
1061     ShapeEditor *shape_editor = get_current_shape_editor();
1062     if (shape_editor) shape_editor->show_helperpath(show);
1065 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1066     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1069 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1070     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1073 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1074     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1077 /* is called when the node selection is modified */
1078 static void
1079 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1081     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1082     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1083     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1084     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1086     // quit if run by the attr_changed listener
1087     if (g_object_get_data( tbl, "freeze" )) {
1088         return;
1089     }
1091     // in turn, prevent listener from responding
1092     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1094     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1095     SPUnit const *unit = tracker->getActiveUnit();
1097     ShapeEditor *shape_editor = get_current_shape_editor();
1098     if (shape_editor && shape_editor->has_nodepath()) {
1099         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1100         int n_selected = 0;
1101         if (nodepath) {
1102             n_selected = nodepath->numSelected();
1103         }
1105         if (n_selected == 0) {
1106             gtk_action_set_sensitive(xact, FALSE);
1107             gtk_action_set_sensitive(yact, FALSE);
1108         } else {
1109             gtk_action_set_sensitive(xact, TRUE);
1110             gtk_action_set_sensitive(yact, TRUE);
1111             Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1112             Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1114             if (n_selected == 1) {
1115                 Geom::Point sel_node = nodepath->singleSelectedCoords();
1116                 if (oldx != sel_node[Geom::X] || oldy != sel_node[Geom::Y]) {
1117                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[Geom::X], *unit));
1118                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[Geom::Y], *unit));
1119                 }
1120             } else {
1121                 boost::optional<Geom::Coord> x = sp_node_selected_common_coord(nodepath, Geom::X);
1122                 boost::optional<Geom::Coord> y = sp_node_selected_common_coord(nodepath, Geom::Y);
1123                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1124                     /* Note: Currently x and y will always have a value, even if the coordinates of the
1125                        selected nodes don't coincide (in this case we use the coordinates of the center
1126                        of the bounding box). So the entries are never set to zero. */
1127                     // FIXME: Maybe we should clear the entry if several nodes are selected
1128                     //        instead of providing a kind of average value
1129                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1130                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1131                 }
1132             }
1133         }
1134     } else {
1135         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1136         gtk_action_set_sensitive(xact, FALSE);
1137         gtk_action_set_sensitive(yact, FALSE);
1138     }
1140     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1143 static void
1144 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1146     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1148     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1149     SPUnit const *unit = tracker->getActiveUnit();
1151     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1152         prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
1153     }
1155     // quit if run by the attr_changed listener
1156     if (g_object_get_data( tbl, "freeze" )) {
1157         return;
1158     }
1160     // in turn, prevent listener from responding
1161     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1163     ShapeEditor *shape_editor = get_current_shape_editor();
1164     if (shape_editor && shape_editor->has_nodepath()) {
1165         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1166         if (!strcmp(value_name, "x")) {
1167             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::X);
1168         }
1169         if (!strcmp(value_name, "y")) {
1170             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::Y);
1171         }
1172     }
1174     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1177 static void
1178 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1180     sp_node_path_value_changed(adj, tbl, "x");
1183 static void
1184 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1186     sp_node_path_value_changed(adj, tbl, "y");
1189 void
1190 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1192     {
1193     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1194     SPItem *item = selection->singleItem();
1195     if (item && SP_IS_LPE_ITEM(item)) {
1196        if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1197            gtk_action_set_sensitive(w, TRUE);
1198        } else {
1199            gtk_action_set_sensitive(w, FALSE);
1200        }
1201     } else {
1202        gtk_action_set_sensitive(w, FALSE);
1203     }
1204     }
1206     {
1207     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1208     SPItem *item = selection->singleItem();
1209     if (item && item->clip_ref && item->clip_ref->getObject()) {
1210        gtk_action_set_sensitive(w, TRUE);
1211     } else {
1212        gtk_action_set_sensitive(w, FALSE);
1213     }
1214     }
1216     {
1217     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1218     SPItem *item = selection->singleItem();
1219     if (item && item->mask_ref && item->mask_ref->getObject()) {
1220        gtk_action_set_sensitive(w, TRUE);
1221     } else {
1222        gtk_action_set_sensitive(w, FALSE);
1223     }
1224     }
1227 void
1228 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1230     sp_node_toolbox_sel_changed (selection, tbl);
1235 //################################
1236 //##    Node Editing Toolbox    ##
1237 //################################
1239 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1241     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1242     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1243     g_object_set_data( holder, "tracker", tracker );
1245     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
1247     {
1248         InkAction* inky = ink_action_new( "NodeInsertAction",
1249                                           _("Insert node"),
1250                                           _("Insert new nodes into selected segments"),
1251                                           "node_insert",
1252                                           secondarySize );
1253         g_object_set( inky, "short_label", _("Insert"), NULL );
1254         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1255         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1256     }
1258     {
1259         InkAction* inky = ink_action_new( "NodeDeleteAction",
1260                                           _("Delete node"),
1261                                           _("Delete selected nodes"),
1262                                           "node_delete",
1263                                           secondarySize );
1264         g_object_set( inky, "short_label", _("Delete"), NULL );
1265         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1266         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1267     }
1269     {
1270         InkAction* inky = ink_action_new( "NodeJoinAction",
1271                                           _("Join endnodes"),
1272                                           _("Join selected endnodes"),
1273                                           "node_join",
1274                                           secondarySize );
1275         g_object_set( inky, "short_label", _("Join"), NULL );
1276         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1277         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1278     }
1280     {
1281         InkAction* inky = ink_action_new( "NodeBreakAction",
1282                                           _("Break nodes"),
1283                                           _("Break path at selected nodes"),
1284                                           "node_break",
1285                                           secondarySize );
1286         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1287         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1288     }
1291     {
1292         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1293                                           _("Join with segment"),
1294                                           _("Join selected endnodes with a new segment"),
1295                                           "node_join_segment",
1296                                           secondarySize );
1297         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1298         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1299     }
1301     {
1302         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1303                                           _("Delete segment"),
1304                                           _("Delete segment between two non-endpoint nodes"),
1305                                           "node_delete_segment",
1306                                           secondarySize );
1307         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1308         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1309     }
1311     {
1312         InkAction* inky = ink_action_new( "NodeCuspAction",
1313                                           _("Node Cusp"),
1314                                           _("Make selected nodes corner"),
1315                                           "node_cusp",
1316                                           secondarySize );
1317         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1318         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1319     }
1321     {
1322         InkAction* inky = ink_action_new( "NodeSmoothAction",
1323                                           _("Node Smooth"),
1324                                           _("Make selected nodes smooth"),
1325                                           "node_smooth",
1326                                           secondarySize );
1327         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1328         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1329     }
1331     {
1332         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1333                                           _("Node Symmetric"),
1334                                           _("Make selected nodes symmetric"),
1335                                           "node_symmetric",
1336                                           secondarySize );
1337         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1338         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1339     }
1341     {
1342         InkAction* inky = ink_action_new( "NodeLineAction",
1343                                           _("Node Line"),
1344                                           _("Make selected segments lines"),
1345                                           "node_line",
1346                                           secondarySize );
1347         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1348         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1349     }
1351     {
1352         InkAction* inky = ink_action_new( "NodeCurveAction",
1353                                           _("Node Curve"),
1354                                           _("Make selected segments curves"),
1355                                           "node_curve",
1356                                           secondarySize );
1357         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1358         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1359     }
1361     {
1362         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1363                                                       _("Show Handles"),
1364                                                       _("Show the Bezier handles of selected nodes"),
1365                                                       "nodes_show_handles",
1366                                                       Inkscape::ICON_SIZE_DECORATION );
1367         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1368         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1369         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1370     }
1372     {
1373         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1374                                                       _("Show Outline"),
1375                                                       _("Show the outline of the path"),
1376                                                       "nodes_show_helperpath",
1377                                                       Inkscape::ICON_SIZE_DECORATION );
1378         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1379         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1380         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_helperpath", 0 ) );
1381     }
1383     {
1384         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1385                                           _("Next path effect parameter"),
1386                                           _("Show next path effect parameter for editing"),
1387                                           "edit_next_parameter",
1388                                           Inkscape::ICON_SIZE_DECORATION );
1389         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1390         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1391         g_object_set_data( holder, "nodes_lpeedit", inky);
1392     }
1394     {
1395         InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1396                                           _("Edit clipping path"),
1397                                           _("Edit the clipping path of the object"),
1398                                           "nodeedit-clippath",
1399                                           Inkscape::ICON_SIZE_DECORATION );
1400         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1401         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1402         g_object_set_data( holder, "nodes_clippathedit", inky);
1403     }
1405     {
1406         InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1407                                           _("Edit mask path"),
1408                                           _("Edit the mask of the object"),
1409                                           "nodeedit-mask",
1410                                           Inkscape::ICON_SIZE_DECORATION );
1411         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1412         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1413         g_object_set_data( holder, "nodes_maskedit", inky);
1414     }
1416     /* X coord of selected node(s) */
1417     {
1418         EgeAdjustmentAction* eact = 0;
1419         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1420         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1421         eact = create_adjustment_action( "NodeXAction",
1422                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1423                                          "tools.nodes", "Xcoord", 0,
1424                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1425                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1426                                          labels, values, G_N_ELEMENTS(labels),
1427                                          sp_node_path_x_value_changed );
1428         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1429         g_object_set_data( holder, "nodes_x_action", eact );
1430         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1431         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1432     }
1434     /* Y coord of selected node(s) */
1435     {
1436         EgeAdjustmentAction* eact = 0;
1437         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1438         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1439         eact = create_adjustment_action( "NodeYAction",
1440                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1441                                          "tools.nodes", "Ycoord", 0,
1442                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1443                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1444                                          labels, values, G_N_ELEMENTS(labels),
1445                                          sp_node_path_y_value_changed );
1446         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1447         g_object_set_data( holder, "nodes_y_action", eact );
1448         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1449         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1450     }
1452     // add the units menu
1453     {
1454         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1455         gtk_action_group_add_action( mainActions, act );
1456     }
1459     sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1461     //watch selection
1462     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1464     sigc::connection *c_selection_changed =
1465         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1466                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1467     pool->add_connection ("selection-changed", c_selection_changed);
1469     sigc::connection *c_selection_modified =
1470         new sigc::connection (sp_desktop_selection (desktop)->connectModified
1471                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1472     pool->add_connection ("selection-modified", c_selection_modified);
1474     sigc::connection *c_subselection_changed =
1475         new sigc::connection (desktop->connectToolSubselectionChanged
1476                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1477     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1479     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1481     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1482 } // end of sp_node_toolbox_prep()
1485 //########################
1486 //##    Zoom Toolbox    ##
1487 //########################
1489 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1491     // no custom GtkAction setup needed
1492 } // end of sp_zoom_toolbox_prep()
1494 void
1495 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1497     toolbox_set_desktop(toolbox,
1498                         desktop,
1499                         setup_tool_toolbox,
1500                         update_tool_toolbox,
1501                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1502                                                                          "event_context_connection")));
1506 void
1507 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1509     toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1510                         desktop,
1511                         setup_aux_toolbox,
1512                         update_aux_toolbox,
1513                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1514                                                                          "event_context_connection")));
1517 void
1518 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1520     toolbox_set_desktop(toolbox,
1521                         desktop,
1522                         setup_commands_toolbox,
1523                         update_commands_toolbox,
1524                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1525                                                                          "event_context_connection")));
1528 static void
1529 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1531     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1532     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1534     if (old_desktop) {
1535         GList *children, *iter;
1537         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1538         for ( iter = children ; iter ; iter = iter->next ) {
1539             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1540         }
1541         g_list_free(children);
1542     }
1544     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1546     if (desktop) {
1547         gtk_widget_set_sensitive(toolbox, TRUE);
1548         setup_func(toolbox, desktop);
1549         update_func(desktop, desktop->event_context, toolbox);
1550         *conn = desktop->connectEventContextChanged
1551             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1552     } else {
1553         gtk_widget_set_sensitive(toolbox, FALSE);
1554     }
1556 } // end of toolbox_set_desktop()
1559 static void
1560 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1562     gchar const * descr =
1563         "<ui>"
1564         "  <toolbar name='ToolToolbar'>"
1565         "    <toolitem action='ToolSelector' />"
1566         "    <toolitem action='ToolNode' />"
1567         "    <toolitem action='ToolTweak' />"
1568         "    <toolitem action='ToolZoom' />"
1569         "    <toolitem action='ToolRect' />"
1570         "    <toolitem action='Tool3DBox' />"
1571         "    <toolitem action='ToolArc' />"
1572         "    <toolitem action='ToolStar' />"
1573         "    <toolitem action='ToolSpiral' />"
1574         "    <toolitem action='ToolPencil' />"
1575         "    <toolitem action='ToolPen' />"
1576         "    <toolitem action='ToolCalligraphic' />"
1577         "    <toolitem action='ToolEraser' />"
1578         "    <toolitem action='ToolLPETool' />"
1579         "    <toolitem action='ToolPaintBucket' />"
1580         "    <toolitem action='ToolText' />"
1581         "    <toolitem action='ToolConnector' />"
1582         "    <toolitem action='ToolGradient' />"
1583         "    <toolitem action='ToolDropper' />"
1584         "  </toolbar>"
1585         "</ui>";
1586     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1587     GtkUIManager* mgr = gtk_ui_manager_new();
1588     GError* errVal = 0;
1590     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1591     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1593     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" );
1594     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1595         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1596     }
1597     Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1598     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1600     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
1601     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1603     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1605     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1606     if ( child ) {
1607         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1608     }
1610     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1611 //     Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1615 static void
1616 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1618     gchar const *const tname = ( eventcontext
1619                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1620                                  : NULL );
1621     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1623     for (int i = 0 ; tools[i].type_name ; i++ ) {
1624         Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1625         if ( act ) {
1626             bool setActive = tname && !strcmp(tname, tools[i].type_name);
1627             Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1628             if ( verbAct ) {
1629                 verbAct->set_active(setActive);
1630             }
1631         }
1632     }
1635 static void
1636 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1638     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1639     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1640     GtkUIManager* mgr = gtk_ui_manager_new();
1641     GError* errVal = 0;
1642     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1643     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1645     std::map<std::string, GtkWidget*> dataHolders;
1647     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1648         if ( aux_toolboxes[i].prep_func ) {
1649             // converted to GtkActions and UIManager
1651             GtkWidget* kludge = gtk_toolbar_new();
1652             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1653             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1654             dataHolders[aux_toolboxes[i].type_name] = kludge;
1655             aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1656         } else {
1658             GtkWidget *sub_toolbox = 0;
1659             if (aux_toolboxes[i].create_func == NULL)
1660                 sub_toolbox = sp_empty_toolbox_new(desktop);
1661             else {
1662                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1663             }
1665             gtk_size_group_add_widget( grouper, sub_toolbox );
1667             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1668             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1670         }
1671     }
1673     // Second pass to create toolbars *after* all GtkActions are created
1674     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1675         if ( aux_toolboxes[i].prep_func ) {
1676             // converted to GtkActions and UIManager
1678             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1680             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1681             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1683             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1684             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1685             g_free( tmp );
1686             tmp = 0;
1688             Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1689             if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1690                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1691             }
1692             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1695             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1697             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1698                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1699                 swatch->setDesktop( desktop );
1700                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1701                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1702                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1703                 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 );
1704             }
1706             gtk_widget_show_all( holder );
1707             sp_set_font_size_smaller( holder );
1709             gtk_size_group_add_widget( grouper, holder );
1711             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1712             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1713         }
1714     }
1716     g_object_unref( G_OBJECT(grouper) );
1719 static void
1720 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1722     gchar const *tname = ( eventcontext
1723                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1724                            : NULL );
1725     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1726         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1727         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1728             gtk_widget_show_all(sub_toolbox);
1729             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1730         } else {
1731             gtk_widget_hide(sub_toolbox);
1732         }
1733     }
1736 static void
1737 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1739     gchar const * descr =
1740         "<ui>"
1741         "  <toolbar name='CommandsToolbar'>"
1742         "    <toolitem action='FileNew' />"
1743         "    <toolitem action='FileOpen' />"
1744         "    <toolitem action='FileSave' />"
1745         "    <toolitem action='FilePrint' />"
1746         "    <separator />"
1747         "    <toolitem action='FileImport' />"
1748         "    <toolitem action='FileExport' />"
1749         "    <separator />"
1750         "    <toolitem action='EditUndo' />"
1751         "    <toolitem action='EditRedo' />"
1752         "    <separator />"
1753         "    <toolitem action='EditCopy' />"
1754         "    <toolitem action='EditCut' />"
1755         "    <toolitem action='EditPaste' />"
1756         "    <separator />"
1757         "    <toolitem action='ZoomSelection' />"
1758         "    <toolitem action='ZoomDrawing' />"
1759         "    <toolitem action='ZoomPage' />"
1760         "    <separator />"
1761         "    <toolitem action='EditDuplicate' />"
1762         "    <toolitem action='EditClone' />"
1763         "    <toolitem action='EditUnlinkClone' />"
1764         "    <separator />"
1765         "    <toolitem action='SelectionGroup' />"
1766         "    <toolitem action='SelectionUnGroup' />"
1767         "    <separator />"
1768         "    <toolitem action='DialogFillStroke' />"
1769         "    <toolitem action='DialogText' />"
1770         "    <toolitem action='DialogXMLEditor' />"
1771         "    <toolitem action='DialogAlignDistribute' />"
1772         "    <separator />"
1773         "    <toolitem action='DialogPreferences' />"
1774         "    <toolitem action='DialogDocumentProperties' />"
1775         "  </toolbar>"
1776         "</ui>";
1777     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1780     GtkUIManager* mgr = gtk_ui_manager_new();
1781     GError* errVal = 0;
1783     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1784     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1786     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1787     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1788         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1789     }
1791     Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1792     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1794     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1795     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1798     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1800     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1801     if ( child ) {
1802         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1803     }
1805     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1808 static void
1809 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1813 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1815     gtk_widget_show(toolbox_toplevel);
1816     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1818     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1819     if (!shown_toolbox) {
1820         return;
1821     }
1822     gtk_widget_show(toolbox);
1824     gtk_widget_show_all(shown_toolbox);
1827 static GtkWidget *
1828 sp_empty_toolbox_new(SPDesktop *desktop)
1830     GtkWidget *tbl = gtk_toolbar_new();
1831     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1832     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1834     gtk_widget_show_all(tbl);
1835     sp_set_font_size_smaller (tbl);
1837     return tbl;
1840 #define MODE_LABEL_WIDTH 70
1842 //########################
1843 //##       Star         ##
1844 //########################
1846 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1848     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1850     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1851         // do not remember prefs if this call is initiated by an undo change, because undoing object
1852         // creation sets bogus values to its attributes before it is deleted
1853         prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1854     }
1856     // quit if run by the attr_changed listener
1857     if (g_object_get_data( dataKludge, "freeze" )) {
1858         return;
1859     }
1861     // in turn, prevent listener from responding
1862     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1864     bool modmade = false;
1866     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1867     GSList const *items = selection->itemList();
1868     for (; items != NULL; items = items->next) {
1869         if (SP_IS_STAR((SPItem *) items->data)) {
1870             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1871             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1872             sp_repr_set_svg_double(repr, "sodipodi:arg2",
1873                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1874                                     + M_PI / (gint)adj->value));
1875             SP_OBJECT((SPItem *) items->data)->updateRepr();
1876             modmade = true;
1877         }
1878     }
1879     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1880                                    _("Star: Change number of corners"));
1882     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1885 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1887     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1889     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1890         prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1891     }
1893     // quit if run by the attr_changed listener
1894     if (g_object_get_data( dataKludge, "freeze" )) {
1895         return;
1896     }
1898     // in turn, prevent listener from responding
1899     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1901     bool modmade = false;
1902     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1903     GSList const *items = selection->itemList();
1904     for (; items != NULL; items = items->next) {
1905         if (SP_IS_STAR((SPItem *) items->data)) {
1906             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1908             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1909             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1910             if (r2 < r1) {
1911                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1912             } else {
1913                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1914             }
1916             SP_OBJECT((SPItem *) items->data)->updateRepr();
1917             modmade = true;
1918         }
1919     }
1921     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1922                                    _("Star: Change spoke ratio"));
1924     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1927 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1929     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1930     bool flat = ege_select_one_action_get_active( act ) == 0;
1932     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1933         prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1934                                     flat ? "true" : "false" );
1935     }
1937     // quit if run by the attr_changed listener
1938     if (g_object_get_data( dataKludge, "freeze" )) {
1939         return;
1940     }
1942     // in turn, prevent listener from responding
1943     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1945     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1946     GSList const *items = selection->itemList();
1947     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1948     bool modmade = false;
1950     if ( prop_action ) {
1951         gtk_action_set_sensitive( prop_action, !flat );
1952     }
1954     for (; items != NULL; items = items->next) {
1955         if (SP_IS_STAR((SPItem *) items->data)) {
1956             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1957             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1958             SP_OBJECT((SPItem *) items->data)->updateRepr();
1959             modmade = true;
1960         }
1961     }
1963     if (modmade) {
1964         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1965                          flat ? _("Make polygon") : _("Make star"));
1966     }
1968     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1971 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1973     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1975     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1976         prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1977     }
1979     // quit if run by the attr_changed listener
1980     if (g_object_get_data( dataKludge, "freeze" )) {
1981         return;
1982     }
1984     // in turn, prevent listener from responding
1985     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1987     bool modmade = false;
1989     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1990     GSList const *items = selection->itemList();
1991     for (; items != NULL; items = items->next) {
1992         if (SP_IS_STAR((SPItem *) items->data)) {
1993             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1994             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1995             SP_OBJECT(items->data)->updateRepr();
1996             modmade = true;
1997         }
1998     }
1999     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2000                                    _("Star: Change rounding"));
2002     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2005 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2007     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2009     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2010         prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
2011     }
2013     // quit if run by the attr_changed listener
2014     if (g_object_get_data( dataKludge, "freeze" )) {
2015         return;
2016     }
2018     // in turn, prevent listener from responding
2019     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2021     bool modmade = false;
2023     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2024     GSList const *items = selection->itemList();
2025     for (; items != NULL; items = items->next) {
2026         if (SP_IS_STAR((SPItem *) items->data)) {
2027             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2028             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2029             SP_OBJECT(items->data)->updateRepr();
2030             modmade = true;
2031         }
2032     }
2033     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2034                                    _("Star: Change randomization"));
2036     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2040 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2041                                        gchar const */*old_value*/, gchar const */*new_value*/,
2042                                        bool /*is_interactive*/, gpointer data)
2044     GtkWidget *tbl = GTK_WIDGET(data);
2046     // quit if run by the _changed callbacks
2047     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2048         return;
2049     }
2051     // in turn, prevent callbacks from responding
2052     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2054     GtkAdjustment *adj = 0;
2056     gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2057     bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2059     if (!strcmp(name, "inkscape:randomized")) {
2060         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2061         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2062     } else if (!strcmp(name, "inkscape:rounded")) {
2063         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2064         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2065     } else if (!strcmp(name, "inkscape:flatsided")) {
2066         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2067         char const *flatsides = repr->attribute("inkscape:flatsided");
2068         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2069         if ( flatsides && !strcmp(flatsides,"false") ) {
2070             ege_select_one_action_set_active( flat_action, 1 );
2071             gtk_action_set_sensitive( prop_action, TRUE );
2072         } else {
2073             ege_select_one_action_set_active( flat_action, 0 );
2074             gtk_action_set_sensitive( prop_action, FALSE );
2075         }
2076     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2077         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2078         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2079         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2080         if (r2 < r1) {
2081             gtk_adjustment_set_value(adj, r2/r1);
2082         } else {
2083             gtk_adjustment_set_value(adj, r1/r2);
2084         }
2085     } else if (!strcmp(name, "sodipodi:sides")) {
2086         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2087         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2088     }
2090     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2094 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2096     NULL, /* child_added */
2097     NULL, /* child_removed */
2098     star_tb_event_attr_changed,
2099     NULL, /* content_changed */
2100     NULL  /* order_changed */
2101 };
2104 /**
2105  *  \param selection Should not be NULL.
2106  */
2107 static void
2108 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2110     int n_selected = 0;
2111     Inkscape::XML::Node *repr = NULL;
2113     purge_repr_listener( tbl, tbl );
2115     for (GSList const *items = selection->itemList();
2116          items != NULL;
2117          items = items->next)
2118     {
2119         if (SP_IS_STAR((SPItem *) items->data)) {
2120             n_selected++;
2121             repr = SP_OBJECT_REPR((SPItem *) items->data);
2122         }
2123     }
2125     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2127     if (n_selected == 0) {
2128         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2129     } else if (n_selected == 1) {
2130         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2132         if (repr) {
2133             g_object_set_data( tbl, "repr", repr );
2134             Inkscape::GC::anchor(repr);
2135             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2136             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2137         }
2138     } else {
2139         // FIXME: implement averaging of all parameters for multiple selected stars
2140         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2141         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2142     }
2146 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2148     // FIXME: in this and all other _default functions, set some flag telling the value_changed
2149     // callbacks to lump all the changes for all selected objects in one undo step
2151     GtkAdjustment *adj = 0;
2153     // fixme: make settable in prefs!
2154     gint mag = 5;
2155     gdouble prop = 0.5;
2156     gboolean flat = FALSE;
2157     gdouble randomized = 0;
2158     gdouble rounded = 0;
2160     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2161     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2163     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2164     gtk_action_set_sensitive( sb2, !flat );
2166     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2167     gtk_adjustment_set_value(adj, mag);
2168     gtk_adjustment_value_changed(adj);
2170     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2171     gtk_adjustment_set_value(adj, prop);
2172     gtk_adjustment_value_changed(adj);
2174     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2175     gtk_adjustment_set_value(adj, rounded);
2176     gtk_adjustment_value_changed(adj);
2178     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2179     gtk_adjustment_set_value(adj, randomized);
2180     gtk_adjustment_value_changed(adj);
2184 void
2185 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2187     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2188     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2189     GtkWidget *l = gtk_label_new(NULL);
2190     gtk_label_set_markup(GTK_LABEL(l), title);
2191     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2192     if ( GTK_IS_TOOLBAR(tbl) ) {
2193         gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2194     } else {
2195         gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2196     }
2197     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2201 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2203     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2205     {
2206         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2207         ege_output_action_set_use_markup( act, TRUE );
2208         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2209         g_object_set_data( holder, "mode_action", act );
2210     }
2212     {
2213         EgeAdjustmentAction* eact = 0;
2214         gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2215         bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2217         /* Flatsided checkbox */
2218         {
2219             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2221             GtkTreeIter iter;
2222             gtk_list_store_append( model, &iter );
2223             gtk_list_store_set( model, &iter,
2224                                 0, _("Polygon"),
2225                                 1, _("Regular polygon (with one handle) instead of a star"),
2226                                 2, "star_flat",
2227                                 -1 );
2229             gtk_list_store_append( model, &iter );
2230             gtk_list_store_set( model, &iter,
2231                                 0, _("Star"),
2232                                 1, _("Star instead of a regular polygon (with one handle)"),
2233                                 2, "star_angled",
2234                                 -1 );
2236             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2237             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2238             g_object_set_data( holder, "flat_action", act );
2240             ege_select_one_action_set_appearance( act, "full" );
2241             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2242             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2243             ege_select_one_action_set_icon_column( act, 2 );
2244             ege_select_one_action_set_icon_size( act, secondarySize );
2245             ege_select_one_action_set_tooltip_column( act, 1  );
2247             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2248             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2249         }
2251         /* Magnitude */
2252         {
2253         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2254         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2255         eact = create_adjustment_action( "MagnitudeAction",
2256                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2257                                          "tools.shapes.star", "magnitude", 3,
2258                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2259                                          3, 1024, 1, 5,
2260                                          labels, values, G_N_ELEMENTS(labels),
2261                                          sp_stb_magnitude_value_changed,
2262                                          1.0, 0 );
2263         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2264         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2265         }
2267         /* Spoke ratio */
2268         {
2269         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2270         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2271         eact = create_adjustment_action( "SpokeAction",
2272                                          _("Spoke ratio"), _("Spoke ratio:"),
2273                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2274                                          // Base radius is the same for the closest handle.
2275                                          _("Base radius to tip radius ratio"),
2276                                          "tools.shapes.star", "proportion", 0.5,
2277                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2278                                          0.01, 1.0, 0.01, 0.1,
2279                                          labels, values, G_N_ELEMENTS(labels),
2280                                          sp_stb_proportion_value_changed );
2281         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2282         g_object_set_data( holder, "prop_action", eact );
2283         }
2285         if ( !isFlatSided ) {
2286             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2287         } else {
2288             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2289         }
2291         /* Roundedness */
2292         {
2293         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2294         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2295         eact = create_adjustment_action( "RoundednessAction",
2296                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2297                                          "tools.shapes.star", "rounded", 0.0,
2298                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2299                                          -10.0, 10.0, 0.01, 0.1,
2300                                          labels, values, G_N_ELEMENTS(labels),
2301                                          sp_stb_rounded_value_changed );
2302         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2303         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2304         }
2306         /* Randomization */
2307         {
2308         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2309         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2310         eact = create_adjustment_action( "RandomizationAction",
2311                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2312                                          "tools.shapes.star", "randomized", 0.0,
2313                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2314                                          -10.0, 10.0, 0.001, 0.01,
2315                                          labels, values, G_N_ELEMENTS(labels),
2316                                          sp_stb_randomized_value_changed, 0.1, 3 );
2317         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2318         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2319         }
2320     }
2322     {
2323         /* Reset */
2324         {
2325             GtkAction* act = gtk_action_new( "StarResetAction",
2326                                              _("Defaults"),
2327                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2328                                              GTK_STOCK_CLEAR );
2329             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2330             gtk_action_group_add_action( mainActions, act );
2331             gtk_action_set_sensitive( act, TRUE );
2332         }
2333     }
2335     sigc::connection *connection = new sigc::connection(
2336         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2337         );
2338     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2339     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2343 //########################
2344 //##       Rect         ##
2345 //########################
2347 static void sp_rtb_sensitivize( GObject *tbl )
2349     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2350     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2351     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2353     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2354         gtk_action_set_sensitive( not_rounded, FALSE );
2355     } else {
2356         gtk_action_set_sensitive( not_rounded, TRUE );
2357     }
2361 static void
2362 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2363                           void (*setter)(SPRect *, gdouble))
2365     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2367     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2368     SPUnit const *unit = tracker->getActiveUnit();
2370     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2371         prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2372     }
2374     // quit if run by the attr_changed listener
2375     if (g_object_get_data( tbl, "freeze" )) {
2376         return;
2377     }
2379     // in turn, prevent listener from responding
2380     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2382     bool modmade = false;
2383     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2384     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2385         if (SP_IS_RECT(items->data)) {
2386             if (adj->value != 0) {
2387                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2388             } else {
2389                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2390             }
2391             modmade = true;
2392         }
2393     }
2395     sp_rtb_sensitivize( tbl );
2397     if (modmade) {
2398         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2399                                    _("Change rectangle"));
2400     }
2402     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2405 static void
2406 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2408     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2411 static void
2412 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2414     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2417 static void
2418 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2420     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2423 static void
2424 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2426     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2431 static void
2432 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2434     GtkAdjustment *adj = 0;
2436     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2437     gtk_adjustment_set_value(adj, 0.0);
2438     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2439     gtk_adjustment_value_changed(adj);
2441     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2442     gtk_adjustment_set_value(adj, 0.0);
2443     gtk_adjustment_value_changed(adj);
2445     sp_rtb_sensitivize( obj );
2448 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2449                                        gchar const */*old_value*/, gchar const */*new_value*/,
2450                                        bool /*is_interactive*/, gpointer data)
2452     GObject *tbl = G_OBJECT(data);
2454     // quit if run by the _changed callbacks
2455     if (g_object_get_data( tbl, "freeze" )) {
2456         return;
2457     }
2459     // in turn, prevent callbacks from responding
2460     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2462     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2463     SPUnit const *unit = tracker->getActiveUnit();
2465     gpointer item = g_object_get_data( tbl, "item" );
2466     if (item && SP_IS_RECT(item)) {
2467         {
2468             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2469             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2470             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2471         }
2473         {
2474             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2475             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2476             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2477         }
2479         {
2480             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2481             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2482             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2483         }
2485         {
2486             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2487             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2488             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2489         }
2490     }
2492     sp_rtb_sensitivize( tbl );
2494     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2498 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2499     NULL, /* child_added */
2500     NULL, /* child_removed */
2501     rect_tb_event_attr_changed,
2502     NULL, /* content_changed */
2503     NULL  /* order_changed */
2504 };
2506 /**
2507  *  \param selection should not be NULL.
2508  */
2509 static void
2510 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2512     int n_selected = 0;
2513     Inkscape::XML::Node *repr = NULL;
2514     SPItem *item = NULL;
2516     if ( g_object_get_data( tbl, "repr" ) ) {
2517         g_object_set_data( tbl, "item", NULL );
2518     }
2519     purge_repr_listener( tbl, tbl );
2521     for (GSList const *items = selection->itemList();
2522          items != NULL;
2523          items = items->next) {
2524         if (SP_IS_RECT((SPItem *) items->data)) {
2525             n_selected++;
2526             item = (SPItem *) items->data;
2527             repr = SP_OBJECT_REPR(item);
2528         }
2529     }
2531     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2533     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2535     if (n_selected == 0) {
2536         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2538         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2539         gtk_action_set_sensitive(w, FALSE);
2540         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2541         gtk_action_set_sensitive(h, FALSE);
2543     } else if (n_selected == 1) {
2544         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2545         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2547         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2548         gtk_action_set_sensitive(w, TRUE);
2549         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2550         gtk_action_set_sensitive(h, TRUE);
2552         if (repr) {
2553             g_object_set_data( tbl, "repr", repr );
2554             g_object_set_data( tbl, "item", item );
2555             Inkscape::GC::anchor(repr);
2556             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2557             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2558         }
2559     } else {
2560         // FIXME: implement averaging of all parameters for multiple selected
2561         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2562         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2563         sp_rtb_sensitivize( tbl );
2564     }
2568 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2570     EgeAdjustmentAction* eact = 0;
2571     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2573     {
2574         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2575         ege_output_action_set_use_markup( act, TRUE );
2576         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2577         g_object_set_data( holder, "mode_action", act );
2578     }
2580     // rx/ry units menu: create
2581     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2582     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2583     // fixme: add % meaning per cent of the width/height
2584     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2585     g_object_set_data( holder, "tracker", tracker );
2587     /* W */
2588     {
2589         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2590         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2591         eact = create_adjustment_action( "RectWidthAction",
2592                                          _("Width"), _("W:"), _("Width of rectangle"),
2593                                          "tools.shapes.rect", "width", 0,
2594                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2595                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2596                                          labels, values, G_N_ELEMENTS(labels),
2597                                          sp_rtb_width_value_changed );
2598         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2599         g_object_set_data( holder, "width_action", eact );
2600         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2601         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2602     }
2604     /* H */
2605     {
2606         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2607         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2608         eact = create_adjustment_action( "RectHeightAction",
2609                                          _("Height"), _("H:"), _("Height of rectangle"),
2610                                          "tools.shapes.rect", "height", 0,
2611                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2612                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2613                                          labels, values, G_N_ELEMENTS(labels),
2614                                          sp_rtb_height_value_changed );
2615         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2616         g_object_set_data( holder, "height_action", eact );
2617         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2618         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2619     }
2621     /* rx */
2622     {
2623         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2624         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2625         eact = create_adjustment_action( "RadiusXAction",
2626                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2627                                          "tools.shapes.rect", "rx", 0,
2628                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2629                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2630                                          labels, values, G_N_ELEMENTS(labels),
2631                                          sp_rtb_rx_value_changed);
2632         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2633         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2634     }
2636     /* ry */
2637     {
2638         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2639         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2640         eact = create_adjustment_action( "RadiusYAction",
2641                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2642                                          "tools.shapes.rect", "ry", 0,
2643                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2644                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2645                                          labels, values, G_N_ELEMENTS(labels),
2646                                          sp_rtb_ry_value_changed);
2647         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2648         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2649     }
2651     // add the units menu
2652     {
2653         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2654         gtk_action_group_add_action( mainActions, act );
2655     }
2657     /* Reset */
2658     {
2659         InkAction* inky = ink_action_new( "RectResetAction",
2660                                           _("Not rounded"),
2661                                           _("Make corners sharp"),
2662                                           "squared_corner",
2663                                           secondarySize );
2664         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2665         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2666         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2667         g_object_set_data( holder, "not_rounded", inky );
2668     }
2670     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2671     sp_rtb_sensitivize( holder );
2673     sigc::connection *connection = new sigc::connection(
2674         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2675         );
2676     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2677     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2680 //########################
2681 //##       3D Box       ##
2682 //########################
2684 // normalize angle so that it lies in the interval [0,360]
2685 static double box3d_normalize_angle (double a) {
2686     double angle = a + ((int) (a/360.0))*360;
2687     if (angle < 0) {
2688         angle += 360.0;
2689     }
2690     return angle;
2693 static void
2694 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2695                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2696     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2697     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2698     //       are reset).
2699     bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2701     if (is_infinite) {
2702         gtk_toggle_action_set_active(tact, TRUE);
2703         gtk_action_set_sensitive(act, TRUE);
2705         double angle = persp3d_get_infinite_angle(persp, axis);
2706         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2707             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2708         }
2709     } else {
2710         gtk_toggle_action_set_active(tact, FALSE);
2711         gtk_action_set_sensitive(act, FALSE);
2712     }
2715 static void
2716 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2717     if (!persp_repr) {
2718         g_print ("No perspective given to box3d_resync_toolbar().\n");
2719         return;
2720     }
2722     GtkWidget *tbl = GTK_WIDGET(data);
2723     GtkAdjustment *adj = 0;
2724     GtkAction *act = 0;
2725     GtkToggleAction *tact = 0;
2726     Persp3D *persp = persp3d_get_from_repr(persp_repr);
2727     {
2728         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2729         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2730         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2732         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2733     }
2734     {
2735         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2736         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2737         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2739         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2740     }
2741     {
2742         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2743         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2744         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2746         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2747     }
2750 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2751                                                   gchar const */*old_value*/, gchar const */*new_value*/,
2752                                                   bool /*is_interactive*/, gpointer data)
2754     GtkWidget *tbl = GTK_WIDGET(data);
2756     // quit if run by the attr_changed listener
2757     // note: it used to work without the differently called freeze_ attributes (here and in
2758     //       box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2759     if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2760         return;
2761     }
2763     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2764     // sp_document_maybe_done() when the document is undo insensitive)
2765     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2767     // TODO: Only update the appropriate part of the toolbar
2768 //    if (!strcmp(name, "inkscape:vp_z")) {
2769         box3d_resync_toolbar(repr, G_OBJECT(tbl));
2770 //    }
2772     Persp3D *persp = persp3d_get_from_repr(repr);
2773     persp3d_update_box_reprs(persp);
2775     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2778 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2780     NULL, /* child_added */
2781     NULL, /* child_removed */
2782     box3d_persp_tb_event_attr_changed,
2783     NULL, /* content_changed */
2784     NULL  /* order_changed */
2785 };
2787 /**
2788  *  \param selection Should not be NULL.
2789  */
2790 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2791 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2792 static void
2793 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2795     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2796     // disable the angle entry fields for this direction (otherwise entering a value in them should only
2797     // update the perspectives with infinite VPs and leave the other ones untouched).
2799     Inkscape::XML::Node *persp_repr = NULL;
2800     purge_repr_listener(tbl, tbl);
2802     SPItem *item = selection->singleItem();
2803     if (item && SP_IS_BOX3D(item)) {
2804         // FIXME: Also deal with multiple selected boxes
2805         SPBox3D *box = SP_BOX3D(item);
2806         Persp3D *persp = box3d_get_perspective(box);
2807         persp_repr = SP_OBJECT_REPR(persp);
2808         if (persp_repr) {
2809             g_object_set_data(tbl, "repr", persp_repr);
2810             Inkscape::GC::anchor(persp_repr);
2811             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2812             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2813         }
2815         inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2816         prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2818         box3d_resync_toolbar(persp_repr, tbl);
2819     }
2822 static void
2823 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2825     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2826     SPDocument *document = sp_desktop_document(desktop);
2828     // quit if run by the attr_changed listener
2829     // note: it used to work without the differently called freeze_ attributes (here and in
2830     //       box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2831     if (g_object_get_data( dataKludge, "freeze_attr" )) {
2832         return;
2833     }
2835     // in turn, prevent listener from responding
2836     g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2838     //Persp3D *persp = document->current_persp3d;
2839     std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
2840     if (sel_persps.empty()) {
2841         // this can happen when the document is created; we silently ignore it
2842         return;
2843     }
2844     Persp3D *persp = sel_persps.front();
2846     persp->tmat.set_infinite_direction (axis, adj->value);
2847     SP_OBJECT(persp)->updateRepr();
2849     // TODO: use the correct axis here, too
2850     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2852     g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2856 static void
2857 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2859     box3d_angle_value_changed(adj, dataKludge, Proj::X);
2862 static void
2863 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2865     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2868 static void
2869 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2871     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2875 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2877     // TODO: Take all selected perspectives into account
2878     std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
2879     if (sel_persps.empty()) {
2880         // this can happen when the document is created; we silently ignore it
2881         return;
2882     }
2883     Persp3D *persp = sel_persps.front();
2885     bool set_infinite = gtk_toggle_action_get_active(act);
2886     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2889 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2891     box3d_vp_state_changed(act, box3d_angle, Proj::X);
2894 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2896     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2899 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2901     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2904 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2906     EgeAdjustmentAction* eact = 0;
2907     SPDocument *document = sp_desktop_document (desktop);
2908     Persp3D *persp = document->current_persp3d;
2910     EgeAdjustmentAction* box3d_angle_x = 0;
2911     EgeAdjustmentAction* box3d_angle_y = 0;
2912     EgeAdjustmentAction* box3d_angle_z = 0;
2914     /* Angle X */
2915     {
2916         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2917         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2918         eact = create_adjustment_action( "3DBoxAngleXAction",
2919                                          _("Angle in X direction"), _("Angle X:"),
2920                                          // Translators: PL is short for 'perspective line'
2921                                          _("Angle of PLs in X direction"),
2922                                          "tools.shapes.3dbox", "box3d_angle_x", 30,
2923                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2924                                          -360.0, 360.0, 1.0, 10.0,
2925                                          labels, values, G_N_ELEMENTS(labels),
2926                                          box3d_angle_x_value_changed );
2927         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2928         g_object_set_data( holder, "box3d_angle_x_action", eact );
2929         box3d_angle_x = eact;
2930     }
2932     if (!persp3d_VP_is_finite(persp, Proj::X)) {
2933         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2934     } else {
2935         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2936     }
2939     /* VP X state */
2940     {
2941         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2942                                                       // Translators: VP is short for 'vanishing point'
2943                                                       _("State of VP in X direction"),
2944                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2945                                                       "toggle_vp_x",
2946                                                       Inkscape::ICON_SIZE_DECORATION );
2947         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2948         g_object_set_data( holder, "box3d_vp_x_state_action", act );
2949         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2950         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2951         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2952     }
2954     /* Angle Y */
2955     {
2956         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2957         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2958         eact = create_adjustment_action( "3DBoxAngleYAction",
2959                                          _("Angle in Y direction"), _("Angle Y:"),
2960                                          // Translators: PL is short for 'perspective line'
2961                                          _("Angle of PLs in Y direction"),
2962                                          "tools.shapes.3dbox", "box3d_angle_y", 30,
2963                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2964                                          -360.0, 360.0, 1.0, 10.0,
2965                                          labels, values, G_N_ELEMENTS(labels),
2966                                          box3d_angle_y_value_changed );
2967         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2968         g_object_set_data( holder, "box3d_angle_y_action", eact );
2969         box3d_angle_y = eact;
2970     }
2972     if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2973         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2974     } else {
2975         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2976     }
2978     /* VP Y state */
2979     {
2980         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2981                                                       // Translators: VP is short for 'vanishing point'
2982                                                       _("State of VP in Y direction"),
2983                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2984                                                       "toggle_vp_y",
2985                                                       Inkscape::ICON_SIZE_DECORATION );
2986         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2987         g_object_set_data( holder, "box3d_vp_y_state_action", act );
2988         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2989         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2990         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2991     }
2993     /* Angle Z */
2994     {
2995         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2996         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2997         eact = create_adjustment_action( "3DBoxAngleZAction",
2998                                          _("Angle in Z direction"), _("Angle Z:"),
2999                                          // Translators: PL is short for 'perspective line'
3000                                          _("Angle of PLs in Z direction"),
3001                                          "tools.shapes.3dbox", "box3d_angle_z", 30,
3002                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3003                                          -360.0, 360.0, 1.0, 10.0,
3004                                          labels, values, G_N_ELEMENTS(labels),
3005                                          box3d_angle_z_value_changed );
3006         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3007         g_object_set_data( holder, "box3d_angle_z_action", eact );
3008         box3d_angle_z = eact;
3009     }
3011     if (!persp3d_VP_is_finite(persp, Proj::Z)) {
3012         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3013     } else {
3014         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3015     }
3017     /* VP Z state */
3018     {
3019         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3020                                                       // Translators: VP is short for 'vanishing point'
3021                                                       _("State of VP in Z direction"),
3022                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3023                                                       "toggle_vp_z",
3024                                                       Inkscape::ICON_SIZE_DECORATION );
3025         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3026         g_object_set_data( holder, "box3d_vp_z_state_action", act );
3027         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3028         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3029         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3030     }
3032     sigc::connection *connection = new sigc::connection(
3033         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3034        );
3035     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3036     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3039 //########################
3040 //##       Spiral       ##
3041 //########################
3043 static void
3044 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
3046     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3048     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3049         prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
3050     }
3052     // quit if run by the attr_changed listener
3053     if (g_object_get_data( tbl, "freeze" )) {
3054         return;
3055     }
3057     // in turn, prevent listener from responding
3058     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3060     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3062     bool modmade = false;
3063     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3064          items != NULL;
3065          items = items->next)
3066     {
3067         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3068             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3069             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3070             SP_OBJECT((SPItem *) items->data)->updateRepr();
3071             modmade = true;
3072         }
3073     }
3075     g_free(namespaced_name);
3077     if (modmade) {
3078         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3079                                    _("Change spiral"));
3080     }
3082     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3085 static void
3086 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3088     sp_spl_tb_value_changed(adj, tbl, "revolution");
3091 static void
3092 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3094     sp_spl_tb_value_changed(adj, tbl, "expansion");
3097 static void
3098 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3100     sp_spl_tb_value_changed(adj, tbl, "t0");
3103 static void
3104 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3106     GtkWidget *tbl = GTK_WIDGET(obj);
3108     GtkAdjustment *adj;
3110     // fixme: make settable
3111     gdouble rev = 5;
3112     gdouble exp = 1.0;
3113     gdouble t0 = 0.0;
3115     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3116     gtk_adjustment_set_value(adj, rev);
3117     gtk_adjustment_value_changed(adj);
3119     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3120     gtk_adjustment_set_value(adj, exp);
3121     gtk_adjustment_value_changed(adj);
3123     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3124     gtk_adjustment_set_value(adj, t0);
3125     gtk_adjustment_value_changed(adj);
3127     spinbutton_defocus(GTK_OBJECT(tbl));
3131 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3132                                          gchar const */*old_value*/, gchar const */*new_value*/,
3133                                          bool /*is_interactive*/, gpointer data)
3135     GtkWidget *tbl = GTK_WIDGET(data);
3137     // quit if run by the _changed callbacks
3138     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3139         return;
3140     }
3142     // in turn, prevent callbacks from responding
3143     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3145     GtkAdjustment *adj;
3146     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3147     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3149     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3150     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3152     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3153     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3155     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3159 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3160     NULL, /* child_added */
3161     NULL, /* child_removed */
3162     spiral_tb_event_attr_changed,
3163     NULL, /* content_changed */
3164     NULL  /* order_changed */
3165 };
3167 static void
3168 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3170     int n_selected = 0;
3171     Inkscape::XML::Node *repr = NULL;
3173     purge_repr_listener( tbl, tbl );
3175     for (GSList const *items = selection->itemList();
3176          items != NULL;
3177          items = items->next)
3178     {
3179         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3180             n_selected++;
3181             repr = SP_OBJECT_REPR((SPItem *) items->data);
3182         }
3183     }
3185     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3187     if (n_selected == 0) {
3188         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3189     } else if (n_selected == 1) {
3190         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3192         if (repr) {
3193             g_object_set_data( tbl, "repr", repr );
3194             Inkscape::GC::anchor(repr);
3195             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3196             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3197         }
3198     } else {
3199         // FIXME: implement averaging of all parameters for multiple selected
3200         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3201         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3202     }
3206 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3208     EgeAdjustmentAction* eact = 0;
3209     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3211     {
3212         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3213         ege_output_action_set_use_markup( act, TRUE );
3214         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3215         g_object_set_data( holder, "mode_action", act );
3216     }
3218     /* Revolution */
3219     {
3220         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3221         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3222         eact = create_adjustment_action( "SpiralRevolutionAction",
3223                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3224                                          "tools.shapes.spiral", "revolution", 3.0,
3225                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3226                                          0.01, 1024.0, 0.1, 1.0,
3227                                          labels, values, G_N_ELEMENTS(labels),
3228                                          sp_spl_tb_revolution_value_changed, 1, 2);
3229         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3230     }
3232     /* Expansion */
3233     {
3234         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3235         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3236         eact = create_adjustment_action( "SpiralExpansionAction",
3237                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3238                                          "tools.shapes.spiral", "expansion", 1.0,
3239                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3240                                          0.0, 1000.0, 0.01, 1.0,
3241                                          labels, values, G_N_ELEMENTS(labels),
3242                                          sp_spl_tb_expansion_value_changed);
3243         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3244     }
3246     /* T0 */
3247     {
3248         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3249         gdouble values[] = {0, 0.5, 0.9};
3250         eact = create_adjustment_action( "SpiralT0Action",
3251                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3252                                          "tools.shapes.spiral", "t0", 0.0,
3253                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3254                                          0.0, 0.999, 0.01, 1.0,
3255                                          labels, values, G_N_ELEMENTS(labels),
3256                                          sp_spl_tb_t0_value_changed);
3257         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3258     }
3260     /* Reset */
3261     {
3262         InkAction* inky = ink_action_new( "SpiralResetAction",
3263                                           _("Defaults"),
3264                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3265                                           GTK_STOCK_CLEAR,
3266                                           secondarySize );
3267         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3268         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3269     }
3272     sigc::connection *connection = new sigc::connection(
3273         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3274         );
3275     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3276     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3279 //########################
3280 //##     Pen/Pencil     ##
3281 //########################
3283 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3284 static char const *
3285 freehand_tool_name(GObject *dataKludge)
3287     SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3288     return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3289              ? "tools.freehand.pen"
3290              : "tools.freehand.pencil" );
3293 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3295     gint mode = ege_select_one_action_get_active(act);
3297     prefs_set_int_attribute(freehand_tool_name(tbl), "freehand-mode", mode);
3299     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3301     // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3302     // preparatory work here
3303     if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3304         SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3305         sp_pen_context_set_polyline_mode(pc);
3306     }
3309 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3311     /* Freehand mode toggle buttons */
3312     {
3313         guint freehandMode = prefs_get_int_attribute(tool_is_pencil ? "tools.freehand.pencil" : "tools.freehand.pen", "freehand-mode", 0);
3314         Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3316         {
3317             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3319             GtkTreeIter iter;
3320             gtk_list_store_append( model, &iter );
3321             gtk_list_store_set( model, &iter,
3322                                 0, _("Bezier"),
3323                                 1, _("Create regular Bezier path"),
3324                                 2, "bezier_mode",
3325                                 -1 );
3327             gtk_list_store_append( model, &iter );
3328             gtk_list_store_set( model, &iter,
3329                                 0, _("Spiro"),
3330                                 1, _("Create Spiro path"),
3331                                 2, "spiro_splines_mode",
3332                                 -1 );
3334             if (!tool_is_pencil) {
3335                 gtk_list_store_append( model, &iter );
3336                 gtk_list_store_set( model, &iter,
3337                                     0, _("Zigzag"),
3338                                     1, _("Create a sequence of straight line segments"),
3339                                     2, "polylines_mode",
3340                                     -1 );
3342                 gtk_list_store_append( model, &iter );
3343                 gtk_list_store_set( model, &iter,
3344                                     0, _("Paraxial"),
3345                                     1, _("Create a sequence of paraxial line segments"),
3346                                     2, "paraxial_lines_mode",
3347                                     -1 );
3348             }
3350             EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3351                                                                 "FreehandModeActionPencil" :
3352                                                                 "FreehandModeActionPen",
3353                                                                 (_("Mode:")), ("Mode"), NULL, GTK_TREE_MODEL(model) );
3354             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3356             ege_select_one_action_set_appearance( act, "full" );
3357             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3358             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3359             ege_select_one_action_set_icon_column( act, 2 );
3360             ege_select_one_action_set_icon_size( act, secondarySize );
3361             ege_select_one_action_set_tooltip_column( act, 1  );
3363             ege_select_one_action_set_active( act, freehandMode);
3364             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3365         }
3366     }
3369 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3370     gint shape = ege_select_one_action_get_active( act );
3371     prefs_set_int_attribute(freehand_tool_name(dataKludge), "shape", shape);
3374 /**
3375  * \brief Generate the list of freehand advanced shape option entries.
3376  */
3377 GList * freehand_shape_dropdown_items_list() {
3378     GList *glist = NULL;
3380     glist = g_list_append (glist, _("None"));
3381     glist = g_list_append (glist, _("Triangle in"));
3382     glist = g_list_append (glist, _("Triangle out"));
3383     glist = g_list_append (glist, _("Ellipse"));
3384     glist = g_list_append (glist, _("From clipboard"));
3386     return glist;
3389 static void
3390 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3391     /*advanced shape options */
3392     {
3393         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3395         GList* items = 0;
3396         gint count = 0;
3397         for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3398         {
3399             GtkTreeIter iter;
3400             gtk_list_store_append( model, &iter );
3401             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3402             count++;
3403         }
3404         g_list_free( items );
3405         items = 0;
3406         EgeSelectOneAction* act1 = ege_select_one_action_new(
3407             tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3408             _("Shape:"), ("Shape"), NULL, GTK_TREE_MODEL(model));
3409         g_object_set( act1, "short_label", _("Shape:"), NULL );
3410         ege_select_one_action_set_appearance( act1, "compact" );
3411         ege_select_one_action_set_active( act1, prefs_get_int_attribute(tool_is_pencil ? "tools.freehand.pencil" : "tools.freehand.pen", "shape", 0) );
3412         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3413         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3414         g_object_set_data( holder, "shape_action", act1 );
3415     }
3418 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3420     sp_add_freehand_mode_toggle(mainActions, holder, false);
3421     freehand_add_advanced_shape_options(mainActions, holder, false);
3425 static void
3426 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3428     GtkWidget *tbl = GTK_WIDGET(obj);
3430     GtkAdjustment *adj;
3432     // fixme: make settable
3433     gdouble tolerance = 4;
3435     adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3436     gtk_adjustment_set_value(adj, tolerance);
3437     gtk_adjustment_value_changed(adj);
3439     spinbutton_defocus(GTK_OBJECT(tbl));
3442 static void
3443 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3446     // quit if run by the attr_changed listener
3447     if (g_object_get_data( tbl, "freeze" )) {
3448         return;
3449     }
3450     // in turn, prevent listener from responding
3451     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3452     prefs_set_double_attribute("tools.freehand.pencil",
3453                                "tolerance", adj->value);
3454     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3460 static void
3461 sp_pencil_tb_tolerance_value_changed_external(Inkscape::XML::Node */*repr*/,
3462                                               const gchar */*key*/,
3463                                               const gchar */*oldval*/,
3464                                               const gchar */*newval*/,
3465                                               bool /*is_interactive*/,
3466                                               void * data)
3468     GObject* tbl = G_OBJECT(data);
3469     if (g_object_get_data( tbl, "freeze" )) {
3470         return;
3471     }
3473     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3475     GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl,
3476                                                             "tolerance");
3478     double v = prefs_get_double_attribute("tools.freehand.pencil",
3479                                             "tolerance", adj->value);
3480     gtk_adjustment_set_value(adj, v);
3481     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3485 static Inkscape::XML::NodeEventVector pencil_node_events =
3487     NULL,
3488     NULL,
3489     sp_pencil_tb_tolerance_value_changed_external,
3490     NULL,
3491     NULL,
3492 };
3495 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3497     sp_add_freehand_mode_toggle(mainActions, holder, true);
3499     EgeAdjustmentAction* eact = 0;
3501     /* Tolerance */
3502     {
3503         gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
3504         gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
3505         eact = create_adjustment_action( "PencilToleranceAction",
3506                                          _("Smoothing:"), _("Smoothing: "),
3507                  _("How much smoothing (simplifying) is applied to the line"),
3508                                          "tools.freehand.pencil", "tolerance",
3509                                          3.0,
3510                                          GTK_WIDGET(desktop->canvas), NULL,
3511                                          holder, TRUE, "altx-pencil",
3512                                          1, 100.0, 0.5, 0,
3513                                          labels, values, G_N_ELEMENTS(labels),
3514                                          sp_pencil_tb_tolerance_value_changed,
3515                                          1, 2);
3516         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3517         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3519         Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE,
3520                                                       "tools.freehand.pencil");
3521         repr->addListener(&pencil_node_events, G_OBJECT(holder));
3522         g_object_set_data(G_OBJECT(holder), "repr", repr);
3524     }
3526     /* advanced shape options */
3527     freehand_add_advanced_shape_options(mainActions, holder, true);
3529     /* Reset */
3530     {
3531         InkAction* inky = ink_action_new( "PencilResetAction",
3532                                           _("Defaults"),
3533                                           _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3534                                           GTK_STOCK_CLEAR,
3535                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3536         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
3537         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3538     }
3540     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3545 //########################
3546 //##       Tweak        ##
3547 //########################
3549 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3551     prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3554 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3556     prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3559 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3561     prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3564 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3566     int mode = ege_select_one_action_get_active( act );
3567     prefs_set_int_attribute("tools.tweak", "mode", mode);
3569     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3570     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3571     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3572     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3573     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3574     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3575     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3576         if (doh) gtk_action_set_sensitive (doh, TRUE);
3577         if (dos) gtk_action_set_sensitive (dos, TRUE);
3578         if (dol) gtk_action_set_sensitive (dol, TRUE);
3579         if (doo) gtk_action_set_sensitive (doo, TRUE);
3580         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3581         if (fid) gtk_action_set_sensitive (fid, FALSE);
3582     } else {
3583         if (doh) gtk_action_set_sensitive (doh, FALSE);
3584         if (dos) gtk_action_set_sensitive (dos, FALSE);
3585         if (dol) gtk_action_set_sensitive (dol, FALSE);
3586         if (doo) gtk_action_set_sensitive (doo, FALSE);
3587         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3588         if (fid) gtk_action_set_sensitive (fid, TRUE);
3589     }
3592 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3594     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3597 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3598     bool show = gtk_toggle_action_get_active( act );
3599     prefs_set_int_attribute ("tools.tweak", "doh",  show ? 1 : 0);
3601 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3602     bool show = gtk_toggle_action_get_active( act );
3603     prefs_set_int_attribute ("tools.tweak", "dos",  show ? 1 : 0);
3605 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3606     bool show = gtk_toggle_action_get_active( act );
3607     prefs_set_int_attribute ("tools.tweak", "dol",  show ? 1 : 0);
3609 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3610     bool show = gtk_toggle_action_get_active( act );
3611     prefs_set_int_attribute ("tools.tweak", "doo",  show ? 1 : 0);
3614 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3616     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3618     {
3619         /* Width */
3620         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3621         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3622         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3623                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3624                                                               "tools.tweak", "width", 15,
3625                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3626                                                               1, 100, 1.0, 0.0,
3627                                                               labels, values, G_N_ELEMENTS(labels),
3628                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
3629         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3630         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3631         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3632     }
3635     {
3636         /* Force */
3637         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3638         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3639         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3640                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
3641                                                               "tools.tweak", "force", 20,
3642                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3643                                                               1, 100, 1.0, 0.0,
3644                                                               labels, values, G_N_ELEMENTS(labels),
3645                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
3646         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3647         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3648         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3649     }
3651     /* Mode */
3652     {
3653         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3655         GtkTreeIter iter;
3656         gtk_list_store_append( model, &iter );
3657         gtk_list_store_set( model, &iter,
3658                             0, _("Push mode"),
3659                             1, _("Push parts of paths in any direction"),
3660                             2, "tweak_push_mode",
3661                             -1 );
3663         gtk_list_store_append( model, &iter );
3664         gtk_list_store_set( model, &iter,
3665                             0, _("Shrink mode"),
3666                             1, _("Shrink (inset) parts of paths"),
3667                             2, "tweak_shrink_mode",
3668                             -1 );
3670         gtk_list_store_append( model, &iter );
3671         gtk_list_store_set( model, &iter,
3672                             0, _("Grow mode"),
3673                             1, _("Grow (outset) parts of paths"),
3674                             2, "tweak_grow_mode",
3675                             -1 );
3677         gtk_list_store_append( model, &iter );
3678         gtk_list_store_set( model, &iter,
3679                             0, _("Attract mode"),
3680                             1, _("Attract parts of paths towards cursor"),
3681                             2, "tweak_attract_mode",
3682                             -1 );
3684         gtk_list_store_append( model, &iter );
3685         gtk_list_store_set( model, &iter,
3686                             0, _("Repel mode"),
3687                             1, _("Repel parts of paths from cursor"),
3688                             2, "tweak_repel_mode",
3689                             -1 );
3691         gtk_list_store_append( model, &iter );
3692         gtk_list_store_set( model, &iter,
3693                             0, _("Roughen mode"),
3694                             1, _("Roughen parts of paths"),
3695                             2, "tweak_roughen_mode",
3696                             -1 );
3698         gtk_list_store_append( model, &iter );
3699         gtk_list_store_set( model, &iter,
3700                             0, _("Color paint mode"),
3701                             1, _("Paint the tool's color upon selected objects"),
3702                             2, "tweak_colorpaint_mode",
3703                             -1 );
3705         gtk_list_store_append( model, &iter );
3706         gtk_list_store_set( model, &iter,
3707                             0, _("Color jitter mode"),
3708                             1, _("Jitter the colors of selected objects"),
3709                             2, "tweak_colorjitter_mode",
3710                             -1 );
3712         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3713         g_object_set( act, "short_label", _("Mode:"), NULL );
3714         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3715         g_object_set_data( holder, "mode_action", act );
3717         ege_select_one_action_set_appearance( act, "full" );
3718         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3719         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3720         ege_select_one_action_set_icon_column( act, 2 );
3721         ege_select_one_action_set_icon_size( act, secondarySize );
3722         ege_select_one_action_set_tooltip_column( act, 1  );
3724         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3725         ege_select_one_action_set_active( act, mode );
3726         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3728         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3729     }
3731     guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3733     {
3734         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3735         ege_output_action_set_use_markup( act, TRUE );
3736         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3737         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3738             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3739         g_object_set_data( holder, "tweak_channels_label", act);
3740     }
3742     {
3743         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3744                                                       _("Hue"),
3745                                                       _("In color mode, act on objects' hue"),
3746                                                       NULL,
3747                                                       Inkscape::ICON_SIZE_DECORATION );
3748         //TRANSLATORS:  "H" here stands for hue
3749         g_object_set( act, "short_label", _("H"), NULL );
3750         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3751         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3752         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3753         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3754             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3755         g_object_set_data( holder, "tweak_doh", act);
3756     }
3757     {
3758         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3759                                                       _("Saturation"),
3760                                                       _("In color mode, act on objects' saturation"),
3761                                                       NULL,
3762                                                       Inkscape::ICON_SIZE_DECORATION );
3763         //TRANSLATORS: "S" here stands for Saturation
3764         g_object_set( act, "short_label", _("S"), NULL );
3765         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3766         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3767         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3768         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3769             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3770         g_object_set_data( holder, "tweak_dos", act );
3771     }
3772     {
3773         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3774                                                       _("Lightness"),
3775                                                       _("In color mode, act on objects' lightness"),
3776                                                       NULL,
3777                                                       Inkscape::ICON_SIZE_DECORATION );
3778         //TRANSLATORS: "L" here stands for Lightness
3779         g_object_set( act, "short_label", _("L"), NULL );
3780         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3781         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3782         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3783         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3784             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3785         g_object_set_data( holder, "tweak_dol", act );
3786     }
3787     {
3788         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3789                                                       _("Opacity"),
3790                                                       _("In color mode, act on objects' opacity"),
3791                                                       NULL,
3792                                                       Inkscape::ICON_SIZE_DECORATION );
3793         //TRANSLATORS: "O" here stands for Opacity
3794         g_object_set( act, "short_label", _("O"), NULL );
3795         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3796         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3797         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3798         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3799             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3800         g_object_set_data( holder, "tweak_doo", act );
3801     }
3803     {   /* Fidelity */
3804         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3805         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3806         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3807                                                               _("Fidelity"), _("Fidelity:"),
3808                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3809                                                               "tools.tweak", "fidelity", 50,
3810                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3811                                                               1, 100, 1.0, 10.0,
3812                                                               labels, values, G_N_ELEMENTS(labels),
3813                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
3814         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3815         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3816         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3817             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3818         g_object_set_data( holder, "tweak_fidelity", eact );
3819     }
3822     /* Use Pressure button */
3823     {
3824         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3825                                                       _("Pressure"),
3826                                                       _("Use the pressure of the input device to alter the force of tweak action"),
3827                                                       "use_pressure",
3828                                                       Inkscape::ICON_SIZE_DECORATION );
3829         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3830         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3831         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3832     }
3837 //########################
3838 //##     Calligraphy    ##
3839 //########################
3840 static void update_presets_list (GObject *tbl)
3842     if (g_object_get_data(tbl, "presets_blocked"))
3843         return;
3845     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3846     if (!sel) {
3847         ege_select_one_action_set_active(sel, 0);
3848         return;
3849     }
3851     int total_prefs = pref_path_number_of_children("tools.calligraphic.preset");
3853     for (int i = 1; i <= total_prefs; i++) {
3854         gchar *preset_path = get_pref_nth_child("tools.calligraphic.preset", i);
3855         Inkscape::XML::Node *preset_repr = inkscape_get_repr(INKSCAPE, preset_path);
3857         bool match = true;
3859         for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
3860               iter; 
3861               ++iter ) {
3862             const gchar *attr_name = g_quark_to_string(iter->key);
3863             if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
3864                 continue;
3865             void *widget = g_object_get_data(tbl, attr_name);
3866             if (widget) {
3867                 if (GTK_IS_ADJUSTMENT(widget)) {
3868                     double v = prefs_get_double_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
3869                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
3870                     //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
3871                     if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
3872                         match = false;
3873                         break;
3874                     }
3875                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
3876                     int v = prefs_get_int_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
3877                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
3878                     //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
3879                     if (gtk_toggle_action_get_active(toggle) != v) {
3880                         match = false;
3881                         break;
3882                     }
3883                 } 
3884             } 
3885         }
3887         if (match) {
3888             // newly added item is at the same index as the 
3889             // save command, so we need to change twice for it to take effect
3890             ege_select_one_action_set_active(sel, 0);
3891             ege_select_one_action_set_active(sel, i);
3892             return;
3893         }
3894     }
3896     // no match found
3897     ege_select_one_action_set_active(sel, 0);
3900 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3902     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value * 0.01 );
3903     update_presets_list(tbl);
3906 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3908     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value * 0.01 );
3909     update_presets_list(tbl);
3912 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3914     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3915     update_presets_list(tbl);
3918 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3920     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3921     update_presets_list(tbl);
3924 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3926     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value * 0.01 );
3927     update_presets_list(tbl);
3930 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3932     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value * 0.01);
3933     update_presets_list(tbl);
3936 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3938     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value * 0.01 );
3939     update_presets_list(tbl);
3942 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3944     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3945     update_presets_list(tbl);
3948 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
3950     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3951     update_presets_list(tbl);
3954 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
3956     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3957     update_presets_list(tbl);
3960 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
3962     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
3963     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3964     update_presets_list(tbl);
3965     if (calligraphy_angle )
3966         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3970 static gchar const *const widget_names[] = {
3971     "width",
3972     "mass",
3973     "wiggle",
3974     "angle",
3975     "thinning",
3976     "tremor",
3977     "flatness",
3978     "cap_rounding",
3979     "usepressure",
3980     "tracebackground",
3981     "usetilt"
3982 };
3985 static void sp_dcc_build_presets_list(GObject *tbl) 
3987     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
3989     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3990     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
3991     gtk_list_store_clear (model);
3993     {
3994         GtkTreeIter iter;
3995         gtk_list_store_append( model, &iter );
3996         gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
3997     }
3999     //TODO: switch back to prefs API
4000     Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
4001     Inkscape::XML::Node *child_repr = sp_repr_children(repr);
4002     int ii=1;
4003     while (child_repr) {
4004         GtkTreeIter iter;
4005         char *preset_name = (char *) child_repr->attribute("name");
4006         gtk_list_store_append( model, &iter );
4007         gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );
4008         child_repr = sp_repr_next(child_repr);
4009     }
4011     {
4012         GtkTreeIter iter;
4013         gtk_list_store_append( model, &iter );
4014         gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4015         g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4016     }
4018     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4020     update_presets_list (tbl);
4023 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4025     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4026     if (! desktop) return;
4028     if (g_object_get_data(tbl, "presets_blocked")) 
4029         return;
4031     Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
4032     if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) {
4033         // dialog cancelled
4034         update_presets_list (tbl);
4035         return;
4036     }
4037     Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
4039     if (!profile_name.c_str() || *profile_name.c_str() == 0) {
4040         // empty name entered
4041         update_presets_list (tbl);
4042         return;
4043     }
4045     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4047     int new_index = -1;
4048     gchar *pref_path = NULL;
4049     int total_prefs = pref_path_number_of_children("tools.calligraphic.preset");
4051     for (int i = 1; i <= total_prefs; i++) {
4052         gchar *path = get_pref_nth_child("tools.calligraphic.preset", i);
4053         const gchar *name = prefs_get_string_attribute(path, "name");
4054         if (name && !strcmp(name, profile_name.c_str())) {
4055             // we already have preset with this name, replace its values
4056             new_index = i;
4057             pref_path = g_strdup(path);
4058             break;
4059         }
4060     }
4062     if (new_index == -1) {
4063         // no preset with this name, create 
4064         new_index = total_prefs + 1;
4065         gchar *profile_id = g_strdup_printf("dcc%d", new_index);
4066         pref_path = create_pref("tools.calligraphic.preset", profile_id);
4067         g_free(profile_id);
4068     }
4070     for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4071         gchar const *const widget_name = widget_names[i];
4072         void *widget = g_object_get_data(tbl, widget_name);
4073         if (widget) {
4074             if (GTK_IS_ADJUSTMENT(widget)) {
4075                 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4076                 double v = gtk_adjustment_get_value(adj);
4077                 prefs_set_double_attribute(pref_path, widget_name, v);
4078                 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4079             } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4080                 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4081                 int v = gtk_toggle_action_get_active(toggle);
4082                 prefs_set_int_attribute(pref_path, widget_name, v);
4083                 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4084             } else {
4085                g_warning("Unknown widget type for preset: %s\n", widget_name);
4086             }
4087         } else {
4088             g_warning("Bad key when writing preset: %s\n", widget_name);
4089         }
4090     }
4091     prefs_set_string_attribute(pref_path, "name", profile_name.c_str());
4093     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4095     sp_dcc_build_presets_list (tbl);
4097     g_free(pref_path);
4101 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4103     gint preset_index = ege_select_one_action_get_active( act );
4104     gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4106     if (preset_index == save_presets_index) {
4107         // this is the Save command
4108         sp_dcc_save_profile(NULL, tbl);
4109         return;
4110     }
4112     if (g_object_get_data(tbl, "presets_blocked")) 
4113         return;
4115     gchar *const preset_path = get_pref_nth_child("tools.calligraphic.preset", preset_index);
4117     if (preset_path) {
4118         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
4120         Inkscape::XML::Node *preset_repr = inkscape_get_repr(INKSCAPE, preset_path);
4122         for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
4123               iter; 
4124               ++iter ) {
4125             const gchar *attr_name = g_quark_to_string(iter->key);
4126             if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
4127                 continue;
4128             void *widget = g_object_get_data(tbl, attr_name);
4129             if (widget) {
4130                 if (GTK_IS_ADJUSTMENT(widget)) {
4131                     double v = prefs_get_double_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4132                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4133                     gtk_adjustment_set_value(adj, v);
4134                     //std::cout << "set adj " << attr_name << " to " << v << "\n";
4135                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4136                     int v = prefs_get_int_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4137                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4138                     gtk_toggle_action_set_active(toggle, v);
4139                     //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4140                 } else {
4141                     g_warning("Unknown widget type for preset: %s\n", attr_name);
4142                 }
4143             } else {
4144                 g_warning("Bad key found in a preset record: %s\n", attr_name);
4145             }
4146         }
4147         g_free(preset_path);
4148         g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4149     }
4154 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4156     {
4157         g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4159         EgeAdjustmentAction* calligraphy_angle = 0;
4161         {
4162         /* Width */
4163         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4164         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4165         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4166                                                               _("Pen Width"), _("Width:"),
4167                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
4168                                                               "tools.calligraphic", "width", 15,
4169                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4170                                                               1, 100, 1.0, 0.0,
4171                                                               labels, values, G_N_ELEMENTS(labels),
4172                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
4173         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4174         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4175         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4176         }
4178         {
4179         /* Thinning */
4180             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4181             gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4182         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4183                                                               _("Stroke Thinning"), _("Thinning:"),
4184                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4185                                                               "tools.calligraphic", "thinning", 10,
4186                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4187                                                               -100, 100, 1, 0.1,
4188                                                               labels, values, G_N_ELEMENTS(labels),
4189                                                               sp_ddc_velthin_value_changed, 0.01, 0, 100);
4190         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4191         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4192         }
4194         {
4195         /* Angle */
4196         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4197         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4198         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4199                                                               _("Pen Angle"), _("Angle:"),
4200                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4201                                                               "tools.calligraphic", "angle", 30,
4202                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4203                                                               -90.0, 90.0, 1.0, 10.0,
4204                                                               labels, values, G_N_ELEMENTS(labels),
4205                                                               sp_ddc_angle_value_changed, 1, 0 );
4206         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4207         g_object_set_data( holder, "angle_action", eact );
4208         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4209         calligraphy_angle = eact;
4210         }
4212         {
4213         /* Fixation */
4214             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4215         gdouble values[] = {0, 20, 40, 60, 90, 100};
4216         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4217                                                               _("Fixation"), _("Fixation:"),
4218                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
4219                                                               "tools.calligraphic", "flatness", 90,
4220                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4221                                                               0.0, 100, 1.0, 10.0,
4222                                                               labels, values, G_N_ELEMENTS(labels),
4223                                                               sp_ddc_flatness_value_changed, 0.01, 0, 100 );
4224         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4225         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4226         }
4228         {
4229         /* Cap Rounding */
4230             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4231         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4232         // TRANSLATORS: "cap" means "end" (both start and finish) here
4233         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4234                                                               _("Cap rounding"), _("Caps:"),
4235                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4236                                                               "tools.calligraphic", "cap_rounding", 0.0,
4237                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4238                                                               0.0, 5.0, 0.01, 0.1,
4239                                                               labels, values, G_N_ELEMENTS(labels),
4240                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4241         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4242         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4243         }
4245         {
4246         /* Tremor */
4247             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4248         gdouble values[] = {0, 10, 20, 40, 60, 100};
4249         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4250                                                               _("Stroke Tremor"), _("Tremor:"),
4251                                                               _("Increase to make strokes rugged and trembling"),
4252                                                               "tools.calligraphic", "tremor", 0.0,
4253                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4254                                                               0.0, 100, 1, 0.0,
4255                                                               labels, values, G_N_ELEMENTS(labels),
4256                                                               sp_ddc_tremor_value_changed, 0.01, 0, 100 );
4258         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4259         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4260         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4261         }
4263         {
4264         /* Wiggle */
4265         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4266         gdouble values[] = {0, 20, 40, 60, 100};
4267         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4268                                                               _("Pen Wiggle"), _("Wiggle:"),
4269                                                               _("Increase to make the pen waver and wiggle"),
4270                                                               "tools.calligraphic", "wiggle", 0.0,
4271                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4272                                                               0.0, 100, 1, 0.0,
4273                                                               labels, values, G_N_ELEMENTS(labels),
4274                                                               sp_ddc_wiggle_value_changed, 0.01, 0, 100 );
4275         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4276         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4277         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4278         }
4280         {
4281         /* Mass */
4282             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4283         gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4284         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4285                                                               _("Pen Mass"), _("Mass:"),
4286                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
4287                                                               "tools.calligraphic", "mass", 2.0,
4288                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4289                                                               0.0, 100, 1, 0.0,
4290                                                               labels, values, G_N_ELEMENTS(labels),
4291                                                               sp_ddc_mass_value_changed, 0.01, 0, 100 );
4292         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4293         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4294         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4295         }
4298         /* Trace Background button */
4299         {
4300             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4301                                                           _("Trace Background"),
4302                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4303                                                           "trace_background",
4304                                                           Inkscape::ICON_SIZE_DECORATION );
4305             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4306             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4307             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
4308             g_object_set_data( holder, "tracebackground", act );
4309         }
4311         /* Use Pressure button */
4312         {
4313             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4314                                                           _("Pressure"),
4315                                                           _("Use the pressure of the input device to alter the width of the pen"),
4316                                                           "use_pressure",
4317                                                           Inkscape::ICON_SIZE_DECORATION );
4318             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4319             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4320             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
4321             g_object_set_data( holder, "usepressure", act );
4322         }
4324         /* Use Tilt button */
4325         {
4326             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4327                                                           _("Tilt"),
4328                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
4329                                                           "use_tilt",
4330                                                           Inkscape::ICON_SIZE_DECORATION );
4331             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4332             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4333             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4334             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4335             g_object_set_data( holder, "usetilt", act );
4336         }
4338         /*calligraphic profile */
4339         {
4340             GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
4341             EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
4342             ege_select_one_action_set_appearance (act1, "compact");
4343             g_object_set_data (holder, "profile_selector", act1 );
4345             g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
4347             sp_dcc_build_presets_list (holder);
4349             g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
4350             gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
4351         }
4352     }
4356 //########################
4357 //##    Circle / Arc    ##
4358 //########################
4360 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4362     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4363     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4365     if (v1 == 0 && v2 == 0) {
4366         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4367             gtk_action_set_sensitive( ocb, FALSE );
4368             gtk_action_set_sensitive( make_whole, FALSE );
4369         }
4370     } else {
4371         gtk_action_set_sensitive( ocb, TRUE );
4372         gtk_action_set_sensitive( make_whole, TRUE );
4373     }
4376 static void
4377 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4379     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4381     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4382         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
4383     }
4385     // quit if run by the attr_changed listener
4386     if (g_object_get_data( tbl, "freeze" )) {
4387         return;
4388     }
4390     // in turn, prevent listener from responding
4391     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4393     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4395     bool modmade = false;
4396     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4397          items != NULL;
4398          items = items->next)
4399     {
4400         SPItem *item = SP_ITEM(items->data);
4402         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4404             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4405             SPArc *arc = SP_ARC(item);
4407             if (!strcmp(value_name, "start"))
4408                 ge->start = (adj->value * M_PI)/ 180;
4409             else
4410                 ge->end = (adj->value * M_PI)/ 180;
4412             sp_genericellipse_normalize(ge);
4413             ((SPObject *)arc)->updateRepr();
4414             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4416             modmade = true;
4417         }
4418     }
4420     g_free(namespaced_name);
4422     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4424     sp_arctb_sensitivize( tbl, adj->value, other->value );
4426     if (modmade) {
4427         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4428                                    _("Arc: Change start/end"));
4429     }
4431     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4435 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
4437     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
4440 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4442     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
4446 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4448     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4449     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4450         if ( ege_select_one_action_get_active( act ) != 0 ) {
4451             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
4452         } else {
4453             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
4454         }
4455     }
4457     // quit if run by the attr_changed listener
4458     if (g_object_get_data( tbl, "freeze" )) {
4459         return;
4460     }
4462     // in turn, prevent listener from responding
4463     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4465     bool modmade = false;
4467     if ( ege_select_one_action_get_active(act) != 0 ) {
4468         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4469              items != NULL;
4470              items = items->next)
4471         {
4472             if (SP_IS_ARC((SPItem *) items->data)) {
4473                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4474                 repr->setAttribute("sodipodi:open", "true");
4475                 SP_OBJECT((SPItem *) items->data)->updateRepr();
4476                 modmade = true;
4477             }
4478         }
4479     } else {
4480         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4481              items != NULL;
4482              items = items->next)
4483         {
4484             if (SP_IS_ARC((SPItem *) items->data))    {
4485                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4486                 repr->setAttribute("sodipodi:open", NULL);
4487                 SP_OBJECT((SPItem *) items->data)->updateRepr();
4488                 modmade = true;
4489             }
4490         }
4491     }
4493     if (modmade) {
4494         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4495                                    _("Arc: Change open/closed"));
4496     }
4498     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4501 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4503     GtkAdjustment *adj;
4504     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
4505     gtk_adjustment_set_value(adj, 0.0);
4506     gtk_adjustment_value_changed(adj);
4508     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
4509     gtk_adjustment_set_value(adj, 0.0);
4510     gtk_adjustment_value_changed(adj);
4512     spinbutton_defocus( GTK_OBJECT(obj) );
4515 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
4516                                       gchar const */*old_value*/, gchar const */*new_value*/,
4517                                       bool /*is_interactive*/, gpointer data)
4519     GObject *tbl = G_OBJECT(data);
4521     // quit if run by the _changed callbacks
4522     if (g_object_get_data( tbl, "freeze" )) {
4523         return;
4524     }
4526     // in turn, prevent callbacks from responding
4527     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4529     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4530     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4532     GtkAdjustment *adj1,*adj2;
4533     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4534     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4535     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4536     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4538     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4540     char const *openstr = NULL;
4541     openstr = repr->attribute("sodipodi:open");
4542     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4544     if (openstr) {
4545         ege_select_one_action_set_active( ocb, 1 );
4546     } else {
4547         ege_select_one_action_set_active( ocb, 0 );
4548     }
4550     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4553 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4554     NULL, /* child_added */
4555     NULL, /* child_removed */
4556     arc_tb_event_attr_changed,
4557     NULL, /* content_changed */
4558     NULL  /* order_changed */
4559 };
4562 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4564     int n_selected = 0;
4565     Inkscape::XML::Node *repr = NULL;
4567     purge_repr_listener( tbl, tbl );
4569     for (GSList const *items = selection->itemList();
4570          items != NULL;
4571          items = items->next)
4572     {
4573         if (SP_IS_ARC((SPItem *) items->data)) {
4574             n_selected++;
4575             repr = SP_OBJECT_REPR((SPItem *) items->data);
4576         }
4577     }
4579     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4581     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4582     if (n_selected == 0) {
4583         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4584     } else if (n_selected == 1) {
4585         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4586         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4588         if (repr) {
4589             g_object_set_data( tbl, "repr", repr );
4590             Inkscape::GC::anchor(repr);
4591             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4592             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4593         }
4594     } else {
4595         // FIXME: implement averaging of all parameters for multiple selected
4596         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4597         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4598         sp_arctb_sensitivize( tbl, 1, 0 );
4599     }
4603 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4605     EgeAdjustmentAction* eact = 0;
4606     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
4609     {
4610         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4611         ege_output_action_set_use_markup( act, TRUE );
4612         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4613         g_object_set_data( holder, "mode_action", act );
4614     }
4616     /* Start */
4617     {
4618         eact = create_adjustment_action( "ArcStartAction",
4619                                          _("Start"), _("Start:"),
4620                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
4621                                          "tools.shapes.arc", "start", 0.0,
4622                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4623                                          -360.0, 360.0, 1.0, 10.0,
4624                                          0, 0, 0,
4625                                          sp_arctb_start_value_changed);
4626         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4627     }
4629     /* End */
4630     {
4631         eact = create_adjustment_action( "ArcEndAction",
4632                                          _("End"), _("End:"),
4633                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
4634                                          "tools.shapes.arc", "end", 0.0,
4635                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4636                                          -360.0, 360.0, 1.0, 10.0,
4637                                          0, 0, 0,
4638                                          sp_arctb_end_value_changed);
4639         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4640     }
4642     /* Segments / Pie checkbox */
4643     {
4644         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4646         GtkTreeIter iter;
4647         gtk_list_store_append( model, &iter );
4648         gtk_list_store_set( model, &iter,
4649                             0, _("Closed arc"),
4650                             1, _("Switch to segment (closed shape with two radii)"),
4651                             2, "circle_closed_arc",
4652                             -1 );
4654         gtk_list_store_append( model, &iter );
4655         gtk_list_store_set( model, &iter,
4656                             0, _("Open Arc"),
4657                             1, _("Switch to arc (unclosed shape)"),
4658                             2, "circle_open_arc",
4659                             -1 );
4661         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4662         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4663         g_object_set_data( holder, "open_action", act );
4665         ege_select_one_action_set_appearance( act, "full" );
4666         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4667         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4668         ege_select_one_action_set_icon_column( act, 2 );
4669         ege_select_one_action_set_icon_size( act, secondarySize );
4670         ege_select_one_action_set_tooltip_column( act, 1  );
4672         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4673         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4674         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4675         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4676     }
4678     /* Make Whole */
4679     {
4680         InkAction* inky = ink_action_new( "ArcResetAction",
4681                                           _("Make whole"),
4682                                           _("Make the shape a whole ellipse, not arc or segment"),
4683                                           "reset_circle",
4684                                           secondarySize );
4685         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4686         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4687         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4688         g_object_set_data( holder, "make_whole", inky );
4689     }
4691     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4692     // sensitivize make whole and open checkbox
4693     {
4694         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4695         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4696         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4697     }
4700     sigc::connection *connection = new sigc::connection(
4701         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4702         );
4703     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4704     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4710 // toggle button callbacks and updaters
4712 //########################
4713 //##      Dropper       ##
4714 //########################
4716 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4717     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4718     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4719     if ( set_action ) {
4720         if ( gtk_toggle_action_get_active( act ) ) {
4721             gtk_action_set_sensitive( set_action, TRUE );
4722         } else {
4723             gtk_action_set_sensitive( set_action, FALSE );
4724         }
4725     }
4727     spinbutton_defocus(GTK_OBJECT(tbl));
4730 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4731     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4732     spinbutton_defocus(GTK_OBJECT(tbl));
4736 /**
4737  * Dropper auxiliary toolbar construction and setup.
4738  *
4739  * TODO: Would like to add swatch of current color.
4740  * TODO: Add queue of last 5 or so colors selected with new swatches so that
4741  *       can drag and drop places. Will provide a nice mixing palette.
4742  */
4743 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4745     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4747     {
4748         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4749         ege_output_action_set_use_markup( act, TRUE );
4750         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4751     }
4753     {
4754         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4755                                                       _("Pick opacity"),
4756                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4757                                                       NULL,
4758                                                       Inkscape::ICON_SIZE_DECORATION );
4759         g_object_set( act, "short_label", _("Pick"), NULL );
4760         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4761         g_object_set_data( holder, "pick_action", act );
4762         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4763         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4764     }
4766     {
4767         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4768                                                       _("Assign opacity"),
4769                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4770                                                       NULL,
4771                                                       Inkscape::ICON_SIZE_DECORATION );
4772         g_object_set( act, "short_label", _("Assign"), NULL );
4773         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4774         g_object_set_data( holder, "set_action", act );
4775         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4776         // make sure it's disabled if we're not picking alpha
4777         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4778         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4779     }
4783 //########################
4784 //##      LPETool       ##
4785 //########################
4787 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
4789 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
4791     using namespace Inkscape::LivePathEffect;
4793     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
4795     // TODO: how can we set *all* actions inactive (such that no sutool is activated?)
4796     gint lpeToolMode = ege_select_one_action_get_active(act);
4797     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4798         prefs_set_int_attribute( "tools.lpetool", "mode", lpeToolMode );
4799     }
4800     EffectType type = lpesubtools[lpeToolMode];
4801     SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
4803     // only take action if run by the attr_changed listener
4804     if (!g_object_get_data( tbl, "freeze" )) {
4805         // in turn, prevent listener from responding
4806         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4808         // this is now done in sp_lpetool_context_root_handler()
4809         /**
4810         // activate the LPE corresponding to the chosen subtool
4811         if (type != INVALID_LPE) {
4812             //lc->tool_state = LPETOOL_STATE_PEN;
4813             sp_pen_context_wait_for_LPE_mouse_clicks(pc, type, Effect::acceptsNumClicks(type));
4814         }
4815         // TODO: how can we take LPEs into account that don't expect any 'pre-clicks'?
4816         **/
4818         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4819     }
4822 static void
4823 sp_lpetool_test_value_changed(GtkAdjustment *adj, GObject *tbl)
4825     g_print ("sp_lpetool_test_value_changed()\n");
4826     using namespace Inkscape::LivePathEffect;
4828     // quit if run by the attr_changed listener
4829     if (g_object_get_data( tbl, "freeze" )) {
4830         return;
4831     }
4833     // in turn, prevent listener from responding
4834     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
4836     LPEAngleBisector *lpeab = static_cast<LPEAngleBisector *>(g_object_get_data(tbl, "currentlpe"));
4837     if (!lpeab) {
4838         g_print ("no LPE!\n");
4839     } else {
4840         g_print ("LPE found. Adjusting left length\n");
4841         SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
4842         lpeab->length_left.param_set_value(gtk_adjustment_get_value(adj));
4843         sp_lpe_item_update_patheffect(lpeitem, true, true);
4844     }
4846     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4849 void
4850 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
4852     using namespace Inkscape::LivePathEffect;
4853     g_print ("sp_lpetool_toolbox_sel_changed()");
4854     {
4855         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "lpetool_test_action" ) );
4856         SPItem *item = selection->singleItem();
4857         if (item && SP_IS_LPE_ITEM(item)) {
4858             g_print (" - item found\n");
4859             SPLPEItem *lpeitem = SP_LPE_ITEM(item);
4860             Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
4861             if (lpe && lpe->effectType() == ANGLE_BISECTOR) {
4862                 LPEAngleBisector *lpeab = dynamic_cast<LPEAngleBisector*>(lpe);
4863                 g_object_set_data(tbl, "currentlpe", lpeab);
4864                 g_object_set_data(tbl, "currentlpeitem", lpeitem);
4865                 gtk_action_set_sensitive(w, TRUE);
4866             } else {
4867                 g_object_set_data(tbl, "currentlpe", NULL);
4868                 g_object_set_data(tbl, "currentlpeitem", NULL);
4869                 gtk_action_set_sensitive(w, FALSE);
4870             }
4871         } else {
4872             g_print (" - unsetting item\n");
4873             g_object_set_data(tbl, "currentlpe", NULL);
4874             g_object_set_data(tbl, "currentlpeitem", NULL);
4875             gtk_action_set_sensitive(w, FALSE);
4876         }
4877     }
4878     g_print ("\n");
4881 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4883     /** Automatically create a list of LPEs that get added to the toolbar **/
4884     {
4885         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4887         GtkTreeIter iter;
4889         Inkscape::LivePathEffect::EffectType type;
4890         for (int i = 0; i < num_subtools; ++i) {
4891             type =  lpesubtools[i];
4892             gtk_list_store_append( model, &iter );
4893             gtk_list_store_set( model, &iter,
4894                                 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
4895                                 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
4896                                 2, Inkscape::LivePathEffect::LPETypeConverter.get_key(type).c_str(),
4897                                 -1 );
4898         }
4900         EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4901         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4902         g_object_set_data( holder, "lpetool_mode_action", act );
4904         ege_select_one_action_set_appearance( act, "full" );
4905         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4906         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4907         ege_select_one_action_set_icon_column( act, 2 );
4908         ege_select_one_action_set_tooltip_column( act, 1  );
4910         gint lpeToolMode = prefs_get_int_attribute("tools.lpetool", "mode", 0);
4911         ege_select_one_action_set_active( act, lpeToolMode );
4912         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
4913     }
4915     /* Test action */
4916     /**
4917     {
4918         EgeAdjustmentAction* eact = 0;
4919         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
4920         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
4921         eact = create_adjustment_action( "TestLPEAction",
4922                                          _("Test value"), _("Test value:"), _("Test for interactive control widgets ..."),
4923                                          "tools.lpetool", "testvalue", 0,
4924                                          GTK_WIDGET(desktop->canvas), NULL us, holder, TRUE, "altx-lpetool",
4925                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
4926                                          labels, values, G_N_ELEMENTS(labels),
4927                                          sp_lpetool_test_value_changed );
4928         //tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
4929         g_object_set_data( holder, "lpetool_test_action", eact );
4930         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
4931         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4932     }
4933     **/
4935     //watch selection
4936     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
4938     sigc::connection *c_selection_changed =
4939         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
4940                               (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
4941     pool->add_connection ("selection-changed", c_selection_changed);
4944 //########################
4945 //##       Eraser       ##
4946 //########################
4948 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4950     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4951     gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
4952     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4953         prefs_set_int_attribute( "tools.eraser", "mode", eraserMode );
4954     }
4956     // only take action if run by the attr_changed listener
4957     if (!g_object_get_data( tbl, "freeze" )) {
4958         // in turn, prevent listener from responding
4959         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4961         if ( eraserMode != 0 ) {
4962         } else {
4963         }
4964         // TODO finish implementation
4966         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4967     }
4970 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4972     {
4973         /* Width */
4974         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4975         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4976         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
4977                                                               _("Pen Width"), _("Width:"),
4978                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
4979                                                               "tools.eraser", "width", 15,
4980                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
4981                                                               1, 100, 1.0, 0.0,
4982                                                               labels, values, G_N_ELEMENTS(labels),
4983                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
4984         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4985         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4986         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4987     }
4989     {
4990         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4992         GtkTreeIter iter;
4993         gtk_list_store_append( model, &iter );
4994         gtk_list_store_set( model, &iter,
4995                             0, _("Delete"),
4996                             1, _("Delete objects touched by the eraser"),
4997                             2, "delete_object",
4998                             -1 );
5000         gtk_list_store_append( model, &iter );
5001         gtk_list_store_set( model, &iter,
5002                             0, _("Cut"),
5003                             1, _("Cut out from objects"),
5004                             2, "difference",
5005                             -1 );
5007         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5008         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5009         g_object_set_data( holder, "eraser_mode_action", act );
5011         ege_select_one_action_set_appearance( act, "full" );
5012         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5013         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5014         ege_select_one_action_set_icon_column( act, 2 );
5015         ege_select_one_action_set_tooltip_column( act, 1  );
5017         gint eraserMode = (prefs_get_int_attribute("tools.eraser", "mode", 0) != 0) ? 1 : 0;
5018         ege_select_one_action_set_active( act, eraserMode );
5019         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
5020     }
5024 //########################
5025 //##    Text Toolbox    ##
5026 //########################
5027 /*
5028 static void
5029 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
5031     //Call back for letter sizing spinbutton
5034 static void
5035 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
5037     //Call back for line height spinbutton
5040 static void
5041 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5043     //Call back for horizontal kerning spinbutton
5046 static void
5047 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5049     //Call back for vertical kerning spinbutton
5052 static void
5053 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
5055     //Call back for letter rotation spinbutton
5056 }*/
5058 namespace {
5060 bool popdown_visible = false;
5061 bool popdown_hasfocus = false;
5063 void
5064 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
5066     SPStyle *query =
5067         sp_style_new (SP_ACTIVE_DOCUMENT);
5069 //    int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5071     int result_family =
5072         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5074     int result_style =
5075         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5077     int result_numbers =
5078         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5080     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5082     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5083     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
5084         // there are no texts in selection, read from prefs
5085  
5086         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
5087         if (repr) {
5088             sp_style_read_from_repr (query, repr);
5089             if (g_object_get_data(tbl, "text_style_from_prefs")) {
5090                 // do not reset the toolbar style from prefs if we already did it last time
5091                 sp_style_unref(query);
5092                 return;
5093             }
5094             g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
5095         } else {
5096             sp_style_unref(query);
5097             return;
5098         }
5099     } else {
5100         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
5101     }
5103     if (query->text)
5104     {
5105         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
5106             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5107             gtk_entry_set_text (GTK_ENTRY (entry), "");
5109         } else if (query->text->font_specification.value || query->text->font_family.value) {
5111             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5113             // Get the font that corresponds
5114             Glib::ustring familyName;
5116             font_instance * font = font_factory::Default()->FaceFromStyle(query);
5117             if (font) {
5118                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
5119                 font->Unref();
5120                 font = NULL;
5121             }
5123             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
5125             Gtk::TreePath path;
5126             try {
5127                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
5128             } catch (...) {
5129                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
5130                 sp_style_unref(query);
5131                 return;
5132             }
5134             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5135             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5137             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
5139             gtk_tree_selection_select_path (tselection, path.gobj());
5140             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5142             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
5143         }
5145         //Size
5146         {
5147             GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
5148             gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
5149             g_object_set_data(tbl, "size-block", gpointer(1));
5150             gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
5151             g_object_set_data(tbl, "size-block", gpointer(0));
5152             g_free(str);
5153         }
5155         //Anchor
5156         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
5157         {
5158             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
5159             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5160             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5161             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5162         }
5163         else
5164         {
5165             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
5166             {
5167                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
5168                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5169                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5170                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5171             }
5172             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
5173             {
5174                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
5175                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5176                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5177                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5178             }
5179             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
5180             {
5181                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
5182                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5183                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5184                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5185             }
5186         }
5188         //Style
5189         {
5190             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
5192             gboolean active = gtk_toggle_button_get_active (button);
5193             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
5195             if (active != check)
5196             {
5197                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5198                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5199                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5200             }
5201         }
5203         {
5204             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
5206             gboolean active = gtk_toggle_button_get_active (button);
5207             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
5209             if (active != check)
5210             {
5211                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5212                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5213                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5214             }
5215         }
5217         //Orientation
5218         //locking both buttons, changing one affect all group (both)
5219         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
5220         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5222         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
5223         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
5225         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
5226         {
5227             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5228         }
5229         else
5230         {
5231             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
5232         }
5233         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5234         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
5235     }
5237     sp_style_unref(query);
5240 void
5241 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
5243     sp_text_toolbox_selection_changed (selection, tbl);
5246 void
5247 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
5249     sp_text_toolbox_selection_changed (NULL, tbl);
5252 void
5253 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
5254                                 GObject             *tbl)
5256     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
5257     GtkTreeModel *model = 0;
5258     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5259     GtkTreeIter   iter;
5260     char         *family = 0;
5262     gdk_pointer_ungrab (GDK_CURRENT_TIME);
5263     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5265     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
5266         return;
5267     }
5269     gtk_tree_model_get (model, &iter, 0, &family, -1);
5271     if (g_object_get_data (G_OBJECT (selection), "block"))
5272     {
5273         gtk_entry_set_text (GTK_ENTRY (entry), family);
5274         return;
5275     }
5277     gtk_entry_set_text (GTK_ENTRY (entry), family);
5279     SPStyle *query =
5280         sp_style_new (SP_ACTIVE_DOCUMENT);
5282     int result_fontspec =
5283         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5285     //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5287     SPCSSAttr *css = sp_repr_css_attr_new ();
5290     // First try to get the font spec from the stored value
5291     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
5293     if (fontSpec.empty()) {
5294         // Construct a new font specification if it does not yet exist
5295         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5296         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5297         fontFromStyle->Unref();
5298     }
5300     if (!fontSpec.empty()) {
5301         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
5302         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
5303             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
5304             if (font) {
5305                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5307                 // Set all the these just in case they were altered when finding the best
5308                 // match for the new family and old style...
5310                 gchar c[256];
5312                 font->Family(c, 256);
5313                 sp_repr_css_set_property (css, "font-family", c);
5315                 font->Attribute( "weight", c, 256);
5316                 sp_repr_css_set_property (css, "font-weight", c);
5318                 font->Attribute("style", c, 256);
5319                 sp_repr_css_set_property (css, "font-style", c);
5321                 font->Attribute("stretch", c, 256);
5322                 sp_repr_css_set_property (css, "font-stretch", c);
5324                 font->Attribute("variant", c, 256);
5325                 sp_repr_css_set_property (css, "font-variant", c);
5327                 font->Unref();
5328             }
5329         }
5330     }
5332     // If querying returned nothing, set the default style of the tool (for new texts)
5333     if (result_fontspec == QUERY_STYLE_NOTHING)
5334     {
5335         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5336         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
5337     }
5338     else
5339     {
5340         sp_desktop_set_style (desktop, css, true, true);
5341     }
5343     sp_style_unref(query);
5345     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5346                                    _("Text: Change font family"));
5347     sp_repr_css_attr_unref (css);
5348     g_free(family);
5349     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5351     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5354 /* This is where execution comes when the contents of the font family box have been completed
5355    by the press of the return key */
5356 void
5357 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
5358                                        GObject      *tbl)
5360     const char *family = gtk_entry_get_text (entry);   // Fetch the requested font family
5362 // Try to match that to a known font. If not, then leave current font alone and remain focused on text box
5363     try {
5364         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
5365         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5366         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5367         gtk_tree_selection_select_path (selection, path.gobj());
5368         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5369         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5370     } catch (...) {
5371         if (family && strlen (family))
5372         {
5373             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5374         }
5375     }
5378 void
5379 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
5380                                    gpointer          data)
5382     if (g_object_get_data (G_OBJECT (button), "block")) return;
5383     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
5384     int prop = GPOINTER_TO_INT(data);
5386     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5387     SPCSSAttr *css = sp_repr_css_attr_new ();
5389     switch (prop)
5390     {
5391         case 0:
5392         {
5393             sp_repr_css_set_property (css, "text-anchor", "start");
5394             sp_repr_css_set_property (css, "text-align", "start");
5395             break;
5396         }
5397         case 1:
5398         {
5399             sp_repr_css_set_property (css, "text-anchor", "middle");
5400             sp_repr_css_set_property (css, "text-align", "center");
5401             break;
5402         }
5404         case 2:
5405         {
5406             sp_repr_css_set_property (css, "text-anchor", "end");
5407             sp_repr_css_set_property (css, "text-align", "end");
5408             break;
5409         }
5411         case 3:
5412         {
5413             sp_repr_css_set_property (css, "text-anchor", "start");
5414             sp_repr_css_set_property (css, "text-align", "justify");
5415             break;
5416         }
5417     }
5419     SPStyle *query =
5420         sp_style_new (SP_ACTIVE_DOCUMENT);
5421     int result_numbers =
5422         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5424     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5425     if (result_numbers == QUERY_STYLE_NOTHING)
5426     {
5427         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5428     }
5430     sp_style_unref(query);
5432     sp_desktop_set_style (desktop, css, true, true);
5433     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5434                                    _("Text: Change alignment"));
5435     sp_repr_css_attr_unref (css);
5437     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5440 void
5441 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
5442                                gpointer          data)
5444     if (g_object_get_data (G_OBJECT (button), "block")) return;
5446     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5447     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5448     int          prop       = GPOINTER_TO_INT(data);
5449     bool         active     = gtk_toggle_button_get_active (button);
5451     SPStyle *query =
5452         sp_style_new (SP_ACTIVE_DOCUMENT);
5454     int result_fontspec =
5455         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5457     //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5458     //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5459     //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5461     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
5462     Glib::ustring newFontSpec = "";
5464     if (fontSpec.empty()) {
5465         // Construct a new font specification if it does not yet exist
5466         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5467         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5468         fontFromStyle->Unref();
5469     }
5471     switch (prop)
5472     {
5473         case 0:
5474         {
5475             if (!fontSpec.empty()) {
5476                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
5477             }
5478             if (fontSpec != newFontSpec) {
5479                 // Don't even set the bold if the font didn't exist on the system
5480                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
5481             }
5482             break;
5483         }
5485         case 1:
5486         {
5487             if (!fontSpec.empty()) {
5488                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
5489             }
5490             if (fontSpec != newFontSpec) {
5491                 // Don't even set the italic if the font didn't exist on the system
5492                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
5493             }
5494             break;
5495         }
5496     }
5498     if (!newFontSpec.empty()) {
5499         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5500     }
5502     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5503     if (result_fontspec == QUERY_STYLE_NOTHING)
5504     {
5505         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5506     }
5508     sp_style_unref(query);
5510     sp_desktop_set_style (desktop, css, true, true);
5511     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5512                                    _("Text: Change font style"));
5513     sp_repr_css_attr_unref (css);
5515     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5518 void
5519 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
5520                                      gpointer         data)
5522     if (g_object_get_data (G_OBJECT (button), "block")) {
5523         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5524         return;
5525     }
5527     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5528     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5529     int          prop       = GPOINTER_TO_INT(data);
5531     switch (prop)
5532     {
5533         case 0:
5534         {
5535             sp_repr_css_set_property (css, "writing-mode", "lr");
5536             break;
5537         }
5539         case 1:
5540         {
5541             sp_repr_css_set_property (css, "writing-mode", "tb");
5542             break;
5543         }
5544     }
5546     SPStyle *query =
5547         sp_style_new (SP_ACTIVE_DOCUMENT);
5548     int result_numbers =
5549         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5551     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5552     if (result_numbers == QUERY_STYLE_NOTHING)
5553     {
5554         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5555     }
5557     sp_desktop_set_style (desktop, css, true, true);
5558     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5559                                    _("Text: Change orientation"));
5560     sp_repr_css_attr_unref (css);
5562     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5565 gboolean
5566 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5568     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5569     if (!desktop) return FALSE;
5571     switch (get_group0_keyval (event)) {
5572         case GDK_Escape: // defocus
5573             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5574             sp_text_toolbox_selection_changed (NULL, tbl); // update
5575             return TRUE; // I consumed the event
5576             break;
5577     }
5578     return FALSE;
5581 gboolean
5582 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5584     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5585     if (!desktop) return FALSE;
5587     switch (get_group0_keyval (event)) {
5588         case GDK_KP_Enter:
5589         case GDK_Return:
5590         case GDK_Escape: // defocus
5591             gtk_widget_hide (w);
5592             popdown_visible = false;
5593             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5594             return TRUE; // I consumed the event
5595             break;
5596         case GDK_w:
5597         case GDK_W:
5598             if (event->state & GDK_CONTROL_MASK) {
5599                 gtk_widget_hide (w);
5600                 popdown_visible = false;
5601                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5602                 return TRUE; // I consumed the event
5603             }
5604             break;
5605     }
5606     return FALSE;
5610 void
5611 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
5612                                GObject     *tbl)
5614     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5616     if (g_object_get_data (tbl, "size-block")) return;
5618     // If this is not from selecting a size in the list (in which case get_active will give the
5619     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5620     // process this event. This fixes GTK's stupid insistence on sending an activate change every
5621     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5622     if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5623         return;
5625     gdouble value = -1;
5626     {
5627         gchar *endptr;
5628         gchar *const text = gtk_combo_box_get_active_text(cbox);
5629         if (text) {
5630             value = g_strtod(text, &endptr);
5631             if (endptr == text) {  // Conversion failed, non-numeric input.
5632                 value = -1;
5633             }
5634             g_free(text);
5635         }
5636     }
5637     if (value <= 0) {
5638         return; // could not parse value
5639     }
5641     SPCSSAttr *css = sp_repr_css_attr_new ();
5642     Inkscape::CSSOStringStream osfs;
5643     osfs << value;
5644     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5646     SPStyle *query =
5647         sp_style_new (SP_ACTIVE_DOCUMENT);
5648     int result_numbers =
5649         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5651     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5652     if (result_numbers == QUERY_STYLE_NOTHING)
5653     {
5654         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5655     }
5657     sp_style_unref(query);
5659     sp_desktop_set_style (desktop, css, true, true);
5660     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5661                                    _("Text: Change font size"));
5662     sp_repr_css_attr_unref (css);
5664     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5667 gboolean
5668 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5670     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5671     if (!desktop) return FALSE;
5673     if (!g_object_get_data (tbl, "esc-pressed")) {
5674         g_object_set_data (tbl, "enter-pressed", gpointer(1));
5675         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5676         sp_text_toolbox_size_changed (cbox, tbl);
5677         g_object_set_data (tbl, "enter-pressed", gpointer(0));
5678     }
5679     return FALSE; // I consumed the event
5683 gboolean
5684 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5686     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5687     if (!desktop) return FALSE;
5689     switch (get_group0_keyval (event)) {
5690         case GDK_Escape: // defocus
5691             g_object_set_data (tbl, "esc-pressed", gpointer(1));
5692             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5693             g_object_set_data (tbl, "esc-pressed", gpointer(0));
5694             return TRUE; // I consumed the event
5695             break;
5696         case GDK_Return: // defocus
5697         case GDK_KP_Enter:
5698             g_object_set_data (tbl, "enter-pressed", gpointer(1));
5699             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5700             sp_text_toolbox_size_changed (cbox, tbl);
5701             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5702             g_object_set_data (tbl, "enter-pressed", gpointer(0));
5703             return TRUE; // I consumed the event
5704             break;
5705     }
5706     return FALSE;
5709 void
5710 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
5711                                          GObject            *tbl)
5713     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
5714     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5715     int x, y;
5717     if (!popdown_visible)
5718     {
5719         gdk_window_get_origin (widget->window, &x, &y);
5720         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
5721         gtk_widget_show_all (popdown);
5722         //sp_transientize (popdown);
5724         gdk_pointer_grab (widget->window, TRUE,
5725                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
5726                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
5727                                         GDK_POINTER_MOTION_MASK),
5728                           NULL, NULL, GDK_CURRENT_TIME);
5730         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
5732         popdown_visible = true;
5733     }
5734     else
5735     {
5736         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5737         gdk_pointer_ungrab (GDK_CURRENT_TIME);
5738         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5739         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5740         gtk_widget_hide (popdown);
5741         popdown_visible = false;
5742     }
5745 gboolean
5746 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
5747                                  GdkEventFocus    */*event*/,
5748                                  GObject          */*tbl*/)
5750     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
5751     return FALSE;
5754 gboolean
5755 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
5756                                    GdkEventFocus    */*event*/,
5757                                    GObject          */*tbl*/)
5759     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5761     if (popdown_hasfocus) {
5762         gtk_widget_hide (popdown);
5763         popdown_hasfocus = false;
5764         popdown_visible = false;
5765         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5766         return TRUE;
5767     }
5768     return FALSE;
5771 gboolean
5772 sp_text_toolbox_popdown_focus_in (GtkWidget        */*popdown*/,
5773                                    GdkEventFocus    */*event*/,
5774                                    GObject          */*tbl*/)
5776     popdown_hasfocus = true;
5777     return TRUE;
5781 void
5782 cell_data_func  (GtkTreeViewColumn */*column*/,
5783                  GtkCellRenderer   *cell,
5784                  GtkTreeModel      *tree_model,
5785                  GtkTreeIter       *iter,
5786                  gpointer           /*data*/)
5788     gchar *family;
5789     gtk_tree_model_get(tree_model, iter, 0, &family, -1);
5790     gchar *const family_escaped = g_markup_escape_text(family, -1);
5792     static char const *const sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
5793     gchar *const sample_escaped = g_markup_escape_text(sample, -1);
5795     std::stringstream markup;
5796     markup << family_escaped << "  <span foreground='darkgray' font_family='"
5797            << family_escaped << "'>" << sample_escaped << "</span>";
5798     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
5800     g_free(family);
5801     g_free(family_escaped);
5802     g_free(sample_escaped);
5805 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
5806     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
5807     if (completion) {
5808         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
5809         g_object_unref (completion);
5810     }
5813 GtkWidget*
5814 sp_text_toolbox_new (SPDesktop *desktop)
5816     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
5817     GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
5819     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
5820     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
5822     GtkTooltips *tt = gtk_tooltips_new();
5823     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
5825     ////////////Family
5826     //Window
5827     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5828     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
5830     //Entry
5831     GtkWidget           *entry = gtk_entry_new ();
5832     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
5833     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
5834     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
5835     gtk_entry_completion_set_text_column (completion, 0);
5836     gtk_entry_completion_set_minimum_key_length (completion, 1);
5837     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
5838     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
5839     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
5840     gtk_toolbar_append_widget( tbl, entry, "", "" );
5841     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
5843     //Button
5844     GtkWidget   *button = gtk_button_new ();
5845     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
5846     gtk_toolbar_append_widget( tbl, button, "", "");
5848     //Popdown
5849     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
5850     GtkWidget           *treeview = gtk_tree_view_new ();
5852     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
5853     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
5854     gtk_tree_view_column_pack_start (column, cell, FALSE);
5855     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
5856     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5857     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5859     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5860     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5861     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5863     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5865     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5866     gtk_container_add (GTK_CONTAINER (sw), treeview);
5868     gtk_container_add (GTK_CONTAINER (window), sw);
5869     gtk_widget_set_size_request (window, 300, 450);
5871     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5872     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5873     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5875     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5877     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5878     g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5879     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5881     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5882     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5884     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5885     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5886     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5887     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5888     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5890     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
5891     GtkWidget *box = gtk_event_box_new ();
5892     gtk_container_add (GTK_CONTAINER (box), image);
5893     gtk_toolbar_append_widget( tbl, box, "", "");
5894     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5895     GtkTooltips *tooltips = gtk_tooltips_new ();
5896     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5897     gtk_widget_hide (GTK_WIDGET (box));
5898     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
5900     ////////////Size
5901     gchar const *const sizes[] = {
5902         "4", "6", "8", "9", "10", "11", "12", "13", "14",
5903         "16", "18", "20", "22", "24", "28",
5904         "32", "36", "40", "48", "56", "64", "72", "144"
5905     };
5907     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
5908     for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
5909         gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
5910     }
5911     gtk_widget_set_size_request (cbox, 80, -1);
5912     gtk_toolbar_append_widget( tbl, cbox, "", "");
5913     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
5914     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
5915     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
5916     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
5918     ////////////Text anchor
5919     GtkWidget *group   = gtk_radio_button_new (NULL);
5920     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
5921     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
5923     // left
5924     GtkWidget *rbutton = group;
5925     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5926     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
5927     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5929     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5930     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
5931     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
5932     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
5934     // center
5935     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5936     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5937     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
5938     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5940     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5941     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
5942     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5943     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5945     // right
5946     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5947     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5948     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
5949     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5951     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5952     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
5953     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5954     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5956     // fill
5957     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5958     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5959     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
5960     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5962     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5963     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
5964     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
5965     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
5967     gtk_toolbar_append_widget( tbl, row, "", "");
5969     //spacer
5970     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5972     ////////////Text style
5973     row = gtk_hbox_new (FALSE, 4);
5975     // bold
5976     rbutton = gtk_toggle_button_new ();
5977     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5978     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
5979     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5980     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
5982     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5983     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
5984     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
5986     // italic
5987     rbutton = gtk_toggle_button_new ();
5988     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5989     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
5990     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5991     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
5993     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5994     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
5995     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
5997     gtk_toolbar_append_widget( tbl, row, "", "");
5999     //spacer
6000     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6002     ////////////Text orientation
6003     group   = gtk_radio_button_new (NULL);
6004     row     = gtk_hbox_new (FALSE, 4);
6005     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
6007     // horizontal
6008     rbutton = group;
6009     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6010     gtk_container_add           (GTK_CONTAINER (rbutton),
6011                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
6012     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6013     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
6015     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6016     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
6017     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
6019     // vertical
6020     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6021     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6022     gtk_container_add           (GTK_CONTAINER (rbutton),
6023                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
6024     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6025     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
6027     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6028     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
6029     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
6030     gtk_toolbar_append_widget( tbl, row, "", "" );
6033     //watch selection
6034     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
6036     sigc::connection *c_selection_changed =
6037         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
6038                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
6039     pool->add_connection ("selection-changed", c_selection_changed);
6041     sigc::connection *c_selection_modified =
6042         new sigc::connection (sp_desktop_selection (desktop)->connectModified
6043                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
6044     pool->add_connection ("selection-modified", c_selection_modified);
6046     sigc::connection *c_subselection_changed =
6047         new sigc::connection (desktop->connectToolSubselectionChanged
6048                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
6049     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
6051     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
6054     gtk_widget_show_all( GTK_WIDGET(tbl) );
6056     return GTK_WIDGET(tbl);
6057 } // end of sp_text_toolbox_new()
6059 }//<unnamed> namespace
6062 //#########################
6063 //##      Connector      ##
6064 //#########################
6066 static void sp_connector_path_set_avoid(void)
6068     cc_selection_set_avoid(true);
6072 static void sp_connector_path_set_ignore(void)
6074     cc_selection_set_avoid(false);
6079 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
6081     // quit if run by the _changed callbacks
6082     if (g_object_get_data( tbl, "freeze" )) {
6083         return;
6084     }
6086     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
6087     SPDocument *doc = sp_desktop_document(desktop);
6089     if (!sp_document_get_undo_sensitive(doc))
6090     {
6091         return;
6092     }
6094     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6096     if ( repr->attribute("inkscape:connector-spacing") ) {
6097         gdouble priorValue = gtk_adjustment_get_value(adj);
6098         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
6099         if ( priorValue == gtk_adjustment_get_value(adj) ) {
6100             return;
6101         }
6102     } else if ( adj->value == defaultConnSpacing ) {
6103         return;
6104     }
6106     // in turn, prevent callbacks from responding
6107     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6109     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
6110     SP_OBJECT(desktop->namedview)->updateRepr();
6112     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
6113     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
6114         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
6115         NR::Matrix m = NR::identity();
6116         avoid_item_move(&m, item);
6117     }
6119     if (items) {
6120         g_slist_free(items);
6121     }
6123     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
6124             _("Change connector spacing"));
6126     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6128     spinbutton_defocus(GTK_OBJECT(tbl));
6131 static void sp_connector_graph_layout(void)
6133     if (!SP_ACTIVE_DESKTOP) return;
6135     // hack for clones, see comment in align-and-distribute.cpp
6136     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
6137     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
6139     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
6141     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
6143     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
6146 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6148     if ( gtk_toggle_action_get_active( act ) ) {
6149         prefs_set_string_attribute("tools.connector", "directedlayout",
6150                 "true");
6151     } else {
6152         prefs_set_string_attribute("tools.connector", "directedlayout",
6153                 "false");
6154     }
6157 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6159     if ( gtk_toggle_action_get_active( act ) ) {
6160         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
6161                 "true");
6162     } else {
6163         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
6164                 "false");
6165     }
6169 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
6171     prefs_set_double_attribute("tools.connector", "length", adj->value);
6172     spinbutton_defocus(GTK_OBJECT(tbl));
6175 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
6176                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
6177                                             bool /*is_interactive*/, gpointer data)
6179     GtkWidget *tbl = GTK_WIDGET(data);
6181     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6182         return;
6183     }
6184     if (strcmp(name, "inkscape:connector-spacing") != 0) {
6185         return;
6186     }
6188     GtkAdjustment *adj = (GtkAdjustment*)
6189             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
6190     gdouble spacing = defaultConnSpacing;
6191     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
6193     gtk_adjustment_set_value(adj, spacing);
6197 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
6198     NULL, /* child_added */
6199     NULL, /* child_removed */
6200     connector_tb_event_attr_changed,
6201     NULL, /* content_changed */
6202     NULL  /* order_changed */
6203 };
6206 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
6208     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
6210     {
6211         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
6212                                           _("Avoid"),
6213                                           _("Make connectors avoid selected objects"),
6214                                           "connector_avoid",
6215                                           secondarySize );
6216         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
6217         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6218     }
6220     {
6221         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
6222                                           _("Ignore"),
6223                                           _("Make connectors ignore selected objects"),
6224                                           "connector_ignore",
6225                                           secondarySize );
6226         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
6227         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6228     }
6230     EgeAdjustmentAction* eact = 0;
6232     // Spacing spinbox
6233     eact = create_adjustment_action( "ConnectorSpacingAction",
6234                                      _("Connector Spacing"), _("Spacing:"),
6235                                      _("The amount of space left around objects by auto-routing connectors"),
6236                                      "tools.connector", "spacing", defaultConnSpacing,
6237                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
6238                                      0, 100, 1.0, 10.0,
6239                                      0, 0, 0,
6240                                      connector_spacing_changed, 1, 0 );
6241     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6243     // Graph (connector network) layout
6244     {
6245         InkAction* inky = ink_action_new( "ConnectorGraphAction",
6246                                           _("Graph"),
6247                                           _("Nicely arrange selected connector network"),
6248                                           "graph_layout",
6249                                           secondarySize );
6250         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
6251         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6252     }
6254     // Default connector length spinbox
6255     eact = create_adjustment_action( "ConnectorLengthAction",
6256                                      _("Connector Length"), _("Length:"),
6257                                      _("Ideal length for connectors when layout is applied"),
6258                                      "tools.connector", "length", 100,
6259                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
6260                                      10, 1000, 10.0, 100.0,
6261                                      0, 0, 0,
6262                                      connector_length_changed, 1, 0 );
6263     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6266     // Directed edges toggle button
6267     {
6268         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
6269                                                       _("Downwards"),
6270                                                       _("Make connectors with end-markers (arrows) point downwards"),
6271                                                       "directed_graph",
6272                                                       Inkscape::ICON_SIZE_DECORATION );
6273         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6275         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
6276         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6277                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6279         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
6280     }
6282     // Avoid overlaps toggle button
6283     {
6284         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
6285                                                       _("Remove overlaps"),
6286                                                       _("Do not allow overlapping shapes"),
6287                                                       "remove_overlaps",
6288                                                       Inkscape::ICON_SIZE_DECORATION );
6289         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6291         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
6292         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6293                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6295         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
6296     }
6298     // Code to watch for changes to the connector-spacing attribute in
6299     // the XML.
6300     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6301     g_assert(repr != NULL);
6303     purge_repr_listener( holder, holder );
6305     if (repr) {
6306         g_object_set_data( holder, "repr", repr );
6307         Inkscape::GC::anchor(repr);
6308         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
6309         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
6310     }
6311 } // end of sp_connector_toolbox_prep()
6314 //#########################
6315 //##     Paintbucket     ##
6316 //#########################
6318 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
6320     gint channels = ege_select_one_action_get_active( act );
6321     flood_channels_set_channels( channels );
6324 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
6326     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
6329 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
6331     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
6334 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
6336     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
6337     SPUnit const *unit = tracker->getActiveUnit();
6339     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
6341     prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
6344 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
6346     // FIXME: make defaults settable via Inkscape Options
6347     struct KeyValue {
6348         char const *key;
6349         double value;
6350     } const key_values[] = {
6351         {"threshold", 15},
6352         {"offset", 0.0}
6353     };
6355     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
6356         KeyValue const &kv = key_values[i];
6357         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
6358         if ( adj ) {
6359             gtk_adjustment_set_value(adj, kv.value);
6360         }
6361     }
6363     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
6364     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
6365     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
6366     ege_select_one_action_set_active( autogap_action, 0 );
6369 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6371     EgeAdjustmentAction* eact = 0;
6373     {
6374         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6376         GList* items = 0;
6377         gint count = 0;
6378         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
6379         {
6380             GtkTreeIter iter;
6381             gtk_list_store_append( model, &iter );
6382             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6383             count++;
6384         }
6385         g_list_free( items );
6386         items = 0;
6387         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
6388         g_object_set( act1, "short_label", _("Fill by:"), NULL );
6389         ege_select_one_action_set_appearance( act1, "compact" );
6390         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
6391         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
6392         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
6393         g_object_set_data( holder, "channels_action", act1 );
6394     }
6396     // Spacing spinbox
6397     {
6398         eact = create_adjustment_action(
6399             "ThresholdAction",
6400             _("Fill Threshold"), _("Threshold:"),
6401             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
6402             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
6403             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
6404             0, 0, 0,
6405             paintbucket_threshold_changed, 1, 0 );
6407         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6408         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6409     }
6411     // Create the units menu.
6412     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
6413     const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
6414     if (stored_unit)
6415         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
6416     g_object_set_data( holder, "tracker", tracker );
6417     {
6418         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
6419         gtk_action_group_add_action( mainActions, act );
6420     }
6422     // Offset spinbox
6423     {
6424         eact = create_adjustment_action(
6425             "OffsetAction",
6426             _("Grow/shrink by"), _("Grow/shrink by:"),
6427             _("The amount to grow (positive) or shrink (negative) the created fill path"),
6428             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
6429             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
6430             0, 0, 0,
6431             paintbucket_offset_changed, 1, 2);
6432         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
6434         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6435     }
6437     /* Auto Gap */
6438     {
6439         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6441         GList* items = 0;
6442         gint count = 0;
6443         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
6444         {
6445             GtkTreeIter iter;
6446             gtk_list_store_append( model, &iter );
6447             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6448             count++;
6449         }
6450         g_list_free( items );
6451         items = 0;
6452         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
6453         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
6454         ege_select_one_action_set_appearance( act2, "compact" );
6455         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
6456         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
6457         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
6458         g_object_set_data( holder, "autogap_action", act2 );
6459     }
6461     /* Reset */
6462     {
6463         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
6464                                           _("Defaults"),
6465                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
6466                                           GTK_STOCK_CLEAR );
6467         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
6468         gtk_action_group_add_action( mainActions, act );
6469         gtk_action_set_sensitive( act, TRUE );
6470     }
6474 /*
6475   Local Variables:
6476   mode:c++
6477   c-file-style:"stroustrup"
6478   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
6479   indent-tabs-mode:nil
6480   fill-column:99
6481   End:
6482 */
6483 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :