Code

9731436f049c0435bcf434015c09c50d19ceacb2
[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     { "SPEraserContext",   "eraser_tool",    SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
172     { "SPLPEToolContext",  "lpetool_tool",   SP_VERB_CONTEXT_LPETOOL, SP_VERB_CONTEXT_LPETOOL_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()\n");
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             SPLPEItem *lpeitem = SP_LPE_ITEM(item);
4859             Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
4860             if (lpe && lpe->effectType() == ANGLE_BISECTOR) {
4861                 LPEAngleBisector *lpeab = dynamic_cast<LPEAngleBisector*>(lpe);
4862                 g_object_set_data(tbl, "currentlpe", lpeab);
4863                 g_object_set_data(tbl, "currentlpeitem", lpeitem);
4864                 gtk_action_set_sensitive(w, TRUE);
4865             } else {
4866                 g_object_set_data(tbl, "currentlpe", NULL);
4867                 g_object_set_data(tbl, "currentlpeitem", NULL);
4868                 gtk_action_set_sensitive(w, FALSE);
4869             }
4870         } else {
4871             g_object_set_data(tbl, "currentlpe", NULL);
4872             g_object_set_data(tbl, "currentlpeitem", NULL);
4873             gtk_action_set_sensitive(w, FALSE);
4874         }
4875     }
4878 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4880     /** Automatically create a list of LPEs that get added to the toolbar **/
4881     {
4882         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4884         GtkTreeIter iter;
4886         Inkscape::LivePathEffect::EffectType type;
4887         for (int i = 0; i < num_subtools; ++i) {
4888             type =  lpesubtools[i];
4889             gtk_list_store_append( model, &iter );
4890             gtk_list_store_set( model, &iter,
4891                                 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
4892                                 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
4893                                 2, Inkscape::LivePathEffect::LPETypeConverter.get_key(type).c_str(),
4894                                 -1 );
4895         }
4897         EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4898         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4899         g_object_set_data( holder, "lpetool_mode_action", act );
4901         ege_select_one_action_set_appearance( act, "full" );
4902         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4903         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4904         ege_select_one_action_set_icon_column( act, 2 );
4905         ege_select_one_action_set_tooltip_column( act, 1  );
4907         gint lpeToolMode = prefs_get_int_attribute("tools.lpetool", "mode", 0);
4908         ege_select_one_action_set_active( act, lpeToolMode );
4909         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
4910     }
4912     /* Test action */
4913     /**
4914     {
4915         EgeAdjustmentAction* eact = 0;
4916         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
4917         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
4918         eact = create_adjustment_action( "TestLPEAction",
4919                                          _("Test value"), _("Test value:"), _("Test for interactive control widgets ..."),
4920                                          "tools.lpetool", "testvalue", 0,
4921                                          GTK_WIDGET(desktop->canvas), NULL us, holder, TRUE, "altx-lpetool",
4922                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
4923                                          labels, values, G_N_ELEMENTS(labels),
4924                                          sp_lpetool_test_value_changed );
4925         //tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
4926         g_object_set_data( holder, "lpetool_test_action", eact );
4927         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
4928         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4929     }
4930     **/
4932     //watch selection
4933     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
4935     sigc::connection *c_selection_changed =
4936         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
4937                               (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
4938     pool->add_connection ("selection-changed", c_selection_changed);
4941 //########################
4942 //##       Eraser       ##
4943 //########################
4945 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4947     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4948     gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
4949     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4950         prefs_set_int_attribute( "tools.eraser", "mode", eraserMode );
4951     }
4953     // only take action if run by the attr_changed listener
4954     if (!g_object_get_data( tbl, "freeze" )) {
4955         // in turn, prevent listener from responding
4956         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4958         if ( eraserMode != 0 ) {
4959         } else {
4960         }
4961         // TODO finish implementation
4963         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4964     }
4967 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4969     {
4970         /* Width */
4971         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4972         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4973         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
4974                                                               _("Pen Width"), _("Width:"),
4975                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
4976                                                               "tools.eraser", "width", 15,
4977                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
4978                                                               1, 100, 1.0, 0.0,
4979                                                               labels, values, G_N_ELEMENTS(labels),
4980                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
4981         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4982         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4983         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4984     }
4986     {
4987         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4989         GtkTreeIter iter;
4990         gtk_list_store_append( model, &iter );
4991         gtk_list_store_set( model, &iter,
4992                             0, _("Delete"),
4993                             1, _("Delete objects touched by the eraser"),
4994                             2, "delete_object",
4995                             -1 );
4997         gtk_list_store_append( model, &iter );
4998         gtk_list_store_set( model, &iter,
4999                             0, _("Cut"),
5000                             1, _("Cut out from objects"),
5001                             2, "difference",
5002                             -1 );
5004         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5005         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5006         g_object_set_data( holder, "eraser_mode_action", act );
5008         ege_select_one_action_set_appearance( act, "full" );
5009         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5010         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5011         ege_select_one_action_set_icon_column( act, 2 );
5012         ege_select_one_action_set_tooltip_column( act, 1  );
5014         gint eraserMode = (prefs_get_int_attribute("tools.eraser", "mode", 0) != 0) ? 1 : 0;
5015         ege_select_one_action_set_active( act, eraserMode );
5016         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
5017     }
5021 //########################
5022 //##    Text Toolbox    ##
5023 //########################
5024 /*
5025 static void
5026 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
5028     //Call back for letter sizing spinbutton
5031 static void
5032 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
5034     //Call back for line height spinbutton
5037 static void
5038 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5040     //Call back for horizontal kerning spinbutton
5043 static void
5044 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5046     //Call back for vertical kerning spinbutton
5049 static void
5050 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
5052     //Call back for letter rotation spinbutton
5053 }*/
5055 namespace {
5057 bool popdown_visible = false;
5058 bool popdown_hasfocus = false;
5060 void
5061 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
5063     SPStyle *query =
5064         sp_style_new (SP_ACTIVE_DOCUMENT);
5066 //    int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5068     int result_family =
5069         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5071     int result_style =
5072         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5074     int result_numbers =
5075         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5077     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5079     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5080     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
5081         // there are no texts in selection, read from prefs
5082  
5083         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
5084         if (repr) {
5085             sp_style_read_from_repr (query, repr);
5086             if (g_object_get_data(tbl, "text_style_from_prefs")) {
5087                 // do not reset the toolbar style from prefs if we already did it last time
5088                 sp_style_unref(query);
5089                 return;
5090             }
5091             g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
5092         } else {
5093             sp_style_unref(query);
5094             return;
5095         }
5096     } else {
5097         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
5098     }
5100     if (query->text)
5101     {
5102         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
5103             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5104             gtk_entry_set_text (GTK_ENTRY (entry), "");
5106         } else if (query->text->font_specification.value || query->text->font_family.value) {
5108             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5110             // Get the font that corresponds
5111             Glib::ustring familyName;
5113             font_instance * font = font_factory::Default()->FaceFromStyle(query);
5114             if (font) {
5115                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
5116                 font->Unref();
5117                 font = NULL;
5118             }
5120             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
5122             Gtk::TreePath path;
5123             try {
5124                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
5125             } catch (...) {
5126                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
5127                 sp_style_unref(query);
5128                 return;
5129             }
5131             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5132             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5134             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
5136             gtk_tree_selection_select_path (tselection, path.gobj());
5137             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5139             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
5140         }
5142         //Size
5143         {
5144             GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
5145             gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
5146             g_object_set_data(tbl, "size-block", gpointer(1));
5147             gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
5148             g_object_set_data(tbl, "size-block", gpointer(0));
5149             g_free(str);
5150         }
5152         //Anchor
5153         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
5154         {
5155             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
5156             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5157             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5158             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5159         }
5160         else
5161         {
5162             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
5163             {
5164                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
5165                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5166                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5167                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5168             }
5169             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
5170             {
5171                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
5172                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5173                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5174                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5175             }
5176             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
5177             {
5178                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
5179                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5180                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5181                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5182             }
5183         }
5185         //Style
5186         {
5187             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
5189             gboolean active = gtk_toggle_button_get_active (button);
5190             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
5192             if (active != check)
5193             {
5194                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5195                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5196                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5197             }
5198         }
5200         {
5201             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
5203             gboolean active = gtk_toggle_button_get_active (button);
5204             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
5206             if (active != check)
5207             {
5208                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5209                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5210                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5211             }
5212         }
5214         //Orientation
5215         //locking both buttons, changing one affect all group (both)
5216         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
5217         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5219         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
5220         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
5222         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
5223         {
5224             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5225         }
5226         else
5227         {
5228             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
5229         }
5230         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5231         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
5232     }
5234     sp_style_unref(query);
5237 void
5238 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
5240     sp_text_toolbox_selection_changed (selection, tbl);
5243 void
5244 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
5246     sp_text_toolbox_selection_changed (NULL, tbl);
5249 void
5250 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
5251                                 GObject             *tbl)
5253     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
5254     GtkTreeModel *model = 0;
5255     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5256     GtkTreeIter   iter;
5257     char         *family = 0;
5259     gdk_pointer_ungrab (GDK_CURRENT_TIME);
5260     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5262     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
5263         return;
5264     }
5266     gtk_tree_model_get (model, &iter, 0, &family, -1);
5268     if (g_object_get_data (G_OBJECT (selection), "block"))
5269     {
5270         gtk_entry_set_text (GTK_ENTRY (entry), family);
5271         return;
5272     }
5274     gtk_entry_set_text (GTK_ENTRY (entry), family);
5276     SPStyle *query =
5277         sp_style_new (SP_ACTIVE_DOCUMENT);
5279     int result_fontspec =
5280         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5282     //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5284     SPCSSAttr *css = sp_repr_css_attr_new ();
5287     // First try to get the font spec from the stored value
5288     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
5290     if (fontSpec.empty()) {
5291         // Construct a new font specification if it does not yet exist
5292         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5293         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5294         fontFromStyle->Unref();
5295     }
5297     if (!fontSpec.empty()) {
5298         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
5299         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
5300             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
5301             if (font) {
5302                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5304                 // Set all the these just in case they were altered when finding the best
5305                 // match for the new family and old style...
5307                 gchar c[256];
5309                 font->Family(c, 256);
5310                 sp_repr_css_set_property (css, "font-family", c);
5312                 font->Attribute( "weight", c, 256);
5313                 sp_repr_css_set_property (css, "font-weight", c);
5315                 font->Attribute("style", c, 256);
5316                 sp_repr_css_set_property (css, "font-style", c);
5318                 font->Attribute("stretch", c, 256);
5319                 sp_repr_css_set_property (css, "font-stretch", c);
5321                 font->Attribute("variant", c, 256);
5322                 sp_repr_css_set_property (css, "font-variant", c);
5324                 font->Unref();
5325             }
5326         }
5327     }
5329     // If querying returned nothing, set the default style of the tool (for new texts)
5330     if (result_fontspec == QUERY_STYLE_NOTHING)
5331     {
5332         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5333         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
5334     }
5335     else
5336     {
5337         sp_desktop_set_style (desktop, css, true, true);
5338     }
5340     sp_style_unref(query);
5342     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5343                                    _("Text: Change font family"));
5344     sp_repr_css_attr_unref (css);
5345     g_free(family);
5346     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5348     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5351 /* This is where execution comes when the contents of the font family box have been completed
5352    by the press of the return key */
5353 void
5354 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
5355                                        GObject      *tbl)
5357     const char *family = gtk_entry_get_text (entry);   // Fetch the requested font family
5359 // Try to match that to a known font. If not, then leave current font alone and remain focused on text box
5360     try {
5361         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
5362         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5363         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5364         gtk_tree_selection_select_path (selection, path.gobj());
5365         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5366         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5367     } catch (...) {
5368         if (family && strlen (family))
5369         {
5370             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5371         }
5372     }
5375 void
5376 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
5377                                    gpointer          data)
5379     if (g_object_get_data (G_OBJECT (button), "block")) return;
5380     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
5381     int prop = GPOINTER_TO_INT(data);
5383     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5384     SPCSSAttr *css = sp_repr_css_attr_new ();
5386     switch (prop)
5387     {
5388         case 0:
5389         {
5390             sp_repr_css_set_property (css, "text-anchor", "start");
5391             sp_repr_css_set_property (css, "text-align", "start");
5392             break;
5393         }
5394         case 1:
5395         {
5396             sp_repr_css_set_property (css, "text-anchor", "middle");
5397             sp_repr_css_set_property (css, "text-align", "center");
5398             break;
5399         }
5401         case 2:
5402         {
5403             sp_repr_css_set_property (css, "text-anchor", "end");
5404             sp_repr_css_set_property (css, "text-align", "end");
5405             break;
5406         }
5408         case 3:
5409         {
5410             sp_repr_css_set_property (css, "text-anchor", "start");
5411             sp_repr_css_set_property (css, "text-align", "justify");
5412             break;
5413         }
5414     }
5416     SPStyle *query =
5417         sp_style_new (SP_ACTIVE_DOCUMENT);
5418     int result_numbers =
5419         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5421     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5422     if (result_numbers == QUERY_STYLE_NOTHING)
5423     {
5424         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5425     }
5427     sp_style_unref(query);
5429     sp_desktop_set_style (desktop, css, true, true);
5430     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5431                                    _("Text: Change alignment"));
5432     sp_repr_css_attr_unref (css);
5434     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5437 void
5438 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
5439                                gpointer          data)
5441     if (g_object_get_data (G_OBJECT (button), "block")) return;
5443     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5444     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5445     int          prop       = GPOINTER_TO_INT(data);
5446     bool         active     = gtk_toggle_button_get_active (button);
5448     SPStyle *query =
5449         sp_style_new (SP_ACTIVE_DOCUMENT);
5451     int result_fontspec =
5452         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5454     //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5455     //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5456     //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5458     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
5459     Glib::ustring newFontSpec = "";
5461     if (fontSpec.empty()) {
5462         // Construct a new font specification if it does not yet exist
5463         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5464         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5465         fontFromStyle->Unref();
5466     }
5468     switch (prop)
5469     {
5470         case 0:
5471         {
5472             if (!fontSpec.empty()) {
5473                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
5474             }
5475             if (fontSpec != newFontSpec) {
5476                 // Don't even set the bold if the font didn't exist on the system
5477                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
5478             }
5479             break;
5480         }
5482         case 1:
5483         {
5484             if (!fontSpec.empty()) {
5485                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
5486             }
5487             if (fontSpec != newFontSpec) {
5488                 // Don't even set the italic if the font didn't exist on the system
5489                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
5490             }
5491             break;
5492         }
5493     }
5495     if (!newFontSpec.empty()) {
5496         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5497     }
5499     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5500     if (result_fontspec == QUERY_STYLE_NOTHING)
5501     {
5502         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5503     }
5505     sp_style_unref(query);
5507     sp_desktop_set_style (desktop, css, true, true);
5508     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5509                                    _("Text: Change font style"));
5510     sp_repr_css_attr_unref (css);
5512     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5515 void
5516 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
5517                                      gpointer         data)
5519     if (g_object_get_data (G_OBJECT (button), "block")) {
5520         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5521         return;
5522     }
5524     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5525     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5526     int          prop       = GPOINTER_TO_INT(data);
5528     switch (prop)
5529     {
5530         case 0:
5531         {
5532             sp_repr_css_set_property (css, "writing-mode", "lr");
5533             break;
5534         }
5536         case 1:
5537         {
5538             sp_repr_css_set_property (css, "writing-mode", "tb");
5539             break;
5540         }
5541     }
5543     SPStyle *query =
5544         sp_style_new (SP_ACTIVE_DOCUMENT);
5545     int result_numbers =
5546         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5548     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5549     if (result_numbers == QUERY_STYLE_NOTHING)
5550     {
5551         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5552     }
5554     sp_desktop_set_style (desktop, css, true, true);
5555     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5556                                    _("Text: Change orientation"));
5557     sp_repr_css_attr_unref (css);
5559     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5562 gboolean
5563 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5565     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5566     if (!desktop) return FALSE;
5568     switch (get_group0_keyval (event)) {
5569         case GDK_Escape: // defocus
5570             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5571             sp_text_toolbox_selection_changed (NULL, tbl); // update
5572             return TRUE; // I consumed the event
5573             break;
5574     }
5575     return FALSE;
5578 gboolean
5579 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5581     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5582     if (!desktop) return FALSE;
5584     switch (get_group0_keyval (event)) {
5585         case GDK_KP_Enter:
5586         case GDK_Return:
5587         case GDK_Escape: // defocus
5588             gtk_widget_hide (w);
5589             popdown_visible = false;
5590             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5591             return TRUE; // I consumed the event
5592             break;
5593         case GDK_w:
5594         case GDK_W:
5595             if (event->state & GDK_CONTROL_MASK) {
5596                 gtk_widget_hide (w);
5597                 popdown_visible = false;
5598                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5599                 return TRUE; // I consumed the event
5600             }
5601             break;
5602     }
5603     return FALSE;
5607 void
5608 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
5609                                GObject     *tbl)
5611     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5613     if (g_object_get_data (tbl, "size-block")) return;
5615     // If this is not from selecting a size in the list (in which case get_active will give the
5616     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5617     // process this event. This fixes GTK's stupid insistence on sending an activate change every
5618     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5619     if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5620         return;
5622     gdouble value = -1;
5623     {
5624         gchar *endptr;
5625         gchar *const text = gtk_combo_box_get_active_text(cbox);
5626         if (text) {
5627             value = g_strtod(text, &endptr);
5628             if (endptr == text) {  // Conversion failed, non-numeric input.
5629                 value = -1;
5630             }
5631             g_free(text);
5632         }
5633     }
5634     if (value <= 0) {
5635         return; // could not parse value
5636     }
5638     SPCSSAttr *css = sp_repr_css_attr_new ();
5639     Inkscape::CSSOStringStream osfs;
5640     osfs << value;
5641     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5643     SPStyle *query =
5644         sp_style_new (SP_ACTIVE_DOCUMENT);
5645     int result_numbers =
5646         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5648     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5649     if (result_numbers == QUERY_STYLE_NOTHING)
5650     {
5651         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5652     }
5654     sp_style_unref(query);
5656     sp_desktop_set_style (desktop, css, true, true);
5657     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5658                                    _("Text: Change font size"));
5659     sp_repr_css_attr_unref (css);
5661     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5664 gboolean
5665 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5667     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5668     if (!desktop) return FALSE;
5670     if (!g_object_get_data (tbl, "esc-pressed")) {
5671         g_object_set_data (tbl, "enter-pressed", gpointer(1));
5672         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5673         sp_text_toolbox_size_changed (cbox, tbl);
5674         g_object_set_data (tbl, "enter-pressed", gpointer(0));
5675     }
5676     return FALSE; // I consumed the event
5680 gboolean
5681 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5683     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5684     if (!desktop) return FALSE;
5686     switch (get_group0_keyval (event)) {
5687         case GDK_Escape: // defocus
5688             g_object_set_data (tbl, "esc-pressed", gpointer(1));
5689             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5690             g_object_set_data (tbl, "esc-pressed", gpointer(0));
5691             return TRUE; // I consumed the event
5692             break;
5693         case GDK_Return: // defocus
5694         case GDK_KP_Enter:
5695             g_object_set_data (tbl, "enter-pressed", gpointer(1));
5696             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5697             sp_text_toolbox_size_changed (cbox, tbl);
5698             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5699             g_object_set_data (tbl, "enter-pressed", gpointer(0));
5700             return TRUE; // I consumed the event
5701             break;
5702     }
5703     return FALSE;
5706 void
5707 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
5708                                          GObject            *tbl)
5710     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
5711     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5712     int x, y;
5714     if (!popdown_visible)
5715     {
5716         gdk_window_get_origin (widget->window, &x, &y);
5717         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
5718         gtk_widget_show_all (popdown);
5719         //sp_transientize (popdown);
5721         gdk_pointer_grab (widget->window, TRUE,
5722                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
5723                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
5724                                         GDK_POINTER_MOTION_MASK),
5725                           NULL, NULL, GDK_CURRENT_TIME);
5727         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
5729         popdown_visible = true;
5730     }
5731     else
5732     {
5733         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5734         gdk_pointer_ungrab (GDK_CURRENT_TIME);
5735         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5736         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5737         gtk_widget_hide (popdown);
5738         popdown_visible = false;
5739     }
5742 gboolean
5743 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
5744                                  GdkEventFocus    */*event*/,
5745                                  GObject          */*tbl*/)
5747     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
5748     return FALSE;
5751 gboolean
5752 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
5753                                    GdkEventFocus    */*event*/,
5754                                    GObject          */*tbl*/)
5756     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5758     if (popdown_hasfocus) {
5759         gtk_widget_hide (popdown);
5760         popdown_hasfocus = false;
5761         popdown_visible = false;
5762         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5763         return TRUE;
5764     }
5765     return FALSE;
5768 gboolean
5769 sp_text_toolbox_popdown_focus_in (GtkWidget        */*popdown*/,
5770                                    GdkEventFocus    */*event*/,
5771                                    GObject          */*tbl*/)
5773     popdown_hasfocus = true;
5774     return TRUE;
5778 void
5779 cell_data_func  (GtkTreeViewColumn */*column*/,
5780                  GtkCellRenderer   *cell,
5781                  GtkTreeModel      *tree_model,
5782                  GtkTreeIter       *iter,
5783                  gpointer           /*data*/)
5785     gchar *family;
5786     gtk_tree_model_get(tree_model, iter, 0, &family, -1);
5787     gchar *const family_escaped = g_markup_escape_text(family, -1);
5789     static char const *const sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
5790     gchar *const sample_escaped = g_markup_escape_text(sample, -1);
5792     std::stringstream markup;
5793     markup << family_escaped << "  <span foreground='darkgray' font_family='"
5794            << family_escaped << "'>" << sample_escaped << "</span>";
5795     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
5797     g_free(family);
5798     g_free(family_escaped);
5799     g_free(sample_escaped);
5802 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
5803     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
5804     if (completion) {
5805         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
5806         g_object_unref (completion);
5807     }
5810 GtkWidget*
5811 sp_text_toolbox_new (SPDesktop *desktop)
5813     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
5814     GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
5816     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
5817     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
5819     GtkTooltips *tt = gtk_tooltips_new();
5820     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
5822     ////////////Family
5823     //Window
5824     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5825     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
5827     //Entry
5828     GtkWidget           *entry = gtk_entry_new ();
5829     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
5830     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
5831     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
5832     gtk_entry_completion_set_text_column (completion, 0);
5833     gtk_entry_completion_set_minimum_key_length (completion, 1);
5834     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
5835     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
5836     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
5837     gtk_toolbar_append_widget( tbl, entry, "", "" );
5838     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
5840     //Button
5841     GtkWidget   *button = gtk_button_new ();
5842     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
5843     gtk_toolbar_append_widget( tbl, button, "", "");
5845     //Popdown
5846     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
5847     GtkWidget           *treeview = gtk_tree_view_new ();
5849     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
5850     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
5851     gtk_tree_view_column_pack_start (column, cell, FALSE);
5852     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
5853     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5854     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5856     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5857     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5858     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5860     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5862     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5863     gtk_container_add (GTK_CONTAINER (sw), treeview);
5865     gtk_container_add (GTK_CONTAINER (window), sw);
5866     gtk_widget_set_size_request (window, 300, 450);
5868     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5869     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5870     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5872     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5874     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5875     g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5876     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5878     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5879     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5881     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5882     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5883     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5884     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5885     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5887     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
5888     GtkWidget *box = gtk_event_box_new ();
5889     gtk_container_add (GTK_CONTAINER (box), image);
5890     gtk_toolbar_append_widget( tbl, box, "", "");
5891     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5892     GtkTooltips *tooltips = gtk_tooltips_new ();
5893     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5894     gtk_widget_hide (GTK_WIDGET (box));
5895     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
5897     ////////////Size
5898     gchar const *const sizes[] = {
5899         "4", "6", "8", "9", "10", "11", "12", "13", "14",
5900         "16", "18", "20", "22", "24", "28",
5901         "32", "36", "40", "48", "56", "64", "72", "144"
5902     };
5904     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
5905     for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
5906         gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
5907     }
5908     gtk_widget_set_size_request (cbox, 80, -1);
5909     gtk_toolbar_append_widget( tbl, cbox, "", "");
5910     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
5911     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
5912     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
5913     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
5915     ////////////Text anchor
5916     GtkWidget *group   = gtk_radio_button_new (NULL);
5917     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
5918     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
5920     // left
5921     GtkWidget *rbutton = group;
5922     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5923     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
5924     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5926     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5927     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
5928     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
5929     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
5931     // center
5932     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5933     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5934     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
5935     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5937     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5938     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
5939     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5940     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5942     // right
5943     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5944     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5945     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
5946     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5948     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5949     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
5950     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5951     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5953     // fill
5954     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5955     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5956     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
5957     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5959     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5960     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
5961     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
5962     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
5964     gtk_toolbar_append_widget( tbl, row, "", "");
5966     //spacer
5967     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5969     ////////////Text style
5970     row = gtk_hbox_new (FALSE, 4);
5972     // bold
5973     rbutton = gtk_toggle_button_new ();
5974     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5975     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
5976     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5977     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
5979     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5980     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
5981     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
5983     // italic
5984     rbutton = gtk_toggle_button_new ();
5985     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5986     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
5987     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5988     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
5990     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5991     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
5992     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
5994     gtk_toolbar_append_widget( tbl, row, "", "");
5996     //spacer
5997     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5999     ////////////Text orientation
6000     group   = gtk_radio_button_new (NULL);
6001     row     = gtk_hbox_new (FALSE, 4);
6002     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
6004     // horizontal
6005     rbutton = group;
6006     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6007     gtk_container_add           (GTK_CONTAINER (rbutton),
6008                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
6009     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6010     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
6012     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6013     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
6014     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
6016     // vertical
6017     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6018     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6019     gtk_container_add           (GTK_CONTAINER (rbutton),
6020                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
6021     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6022     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
6024     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6025     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
6026     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
6027     gtk_toolbar_append_widget( tbl, row, "", "" );
6030     //watch selection
6031     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
6033     sigc::connection *c_selection_changed =
6034         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
6035                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
6036     pool->add_connection ("selection-changed", c_selection_changed);
6038     sigc::connection *c_selection_modified =
6039         new sigc::connection (sp_desktop_selection (desktop)->connectModified
6040                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
6041     pool->add_connection ("selection-modified", c_selection_modified);
6043     sigc::connection *c_subselection_changed =
6044         new sigc::connection (desktop->connectToolSubselectionChanged
6045                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
6046     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
6048     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
6051     gtk_widget_show_all( GTK_WIDGET(tbl) );
6053     return GTK_WIDGET(tbl);
6054 } // end of sp_text_toolbox_new()
6056 }//<unnamed> namespace
6059 //#########################
6060 //##      Connector      ##
6061 //#########################
6063 static void sp_connector_path_set_avoid(void)
6065     cc_selection_set_avoid(true);
6069 static void sp_connector_path_set_ignore(void)
6071     cc_selection_set_avoid(false);
6076 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
6078     // quit if run by the _changed callbacks
6079     if (g_object_get_data( tbl, "freeze" )) {
6080         return;
6081     }
6083     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
6084     SPDocument *doc = sp_desktop_document(desktop);
6086     if (!sp_document_get_undo_sensitive(doc))
6087     {
6088         return;
6089     }
6091     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6093     if ( repr->attribute("inkscape:connector-spacing") ) {
6094         gdouble priorValue = gtk_adjustment_get_value(adj);
6095         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
6096         if ( priorValue == gtk_adjustment_get_value(adj) ) {
6097             return;
6098         }
6099     } else if ( adj->value == defaultConnSpacing ) {
6100         return;
6101     }
6103     // in turn, prevent callbacks from responding
6104     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6106     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
6107     SP_OBJECT(desktop->namedview)->updateRepr();
6109     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
6110     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
6111         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
6112         NR::Matrix m = NR::identity();
6113         avoid_item_move(&m, item);
6114     }
6116     if (items) {
6117         g_slist_free(items);
6118     }
6120     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
6121             _("Change connector spacing"));
6123     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6125     spinbutton_defocus(GTK_OBJECT(tbl));
6128 static void sp_connector_graph_layout(void)
6130     if (!SP_ACTIVE_DESKTOP) return;
6132     // hack for clones, see comment in align-and-distribute.cpp
6133     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
6134     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
6136     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
6138     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
6140     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
6143 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6145     if ( gtk_toggle_action_get_active( act ) ) {
6146         prefs_set_string_attribute("tools.connector", "directedlayout",
6147                 "true");
6148     } else {
6149         prefs_set_string_attribute("tools.connector", "directedlayout",
6150                 "false");
6151     }
6154 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6156     if ( gtk_toggle_action_get_active( act ) ) {
6157         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
6158                 "true");
6159     } else {
6160         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
6161                 "false");
6162     }
6166 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
6168     prefs_set_double_attribute("tools.connector", "length", adj->value);
6169     spinbutton_defocus(GTK_OBJECT(tbl));
6172 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
6173                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
6174                                             bool /*is_interactive*/, gpointer data)
6176     GtkWidget *tbl = GTK_WIDGET(data);
6178     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6179         return;
6180     }
6181     if (strcmp(name, "inkscape:connector-spacing") != 0) {
6182         return;
6183     }
6185     GtkAdjustment *adj = (GtkAdjustment*)
6186             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
6187     gdouble spacing = defaultConnSpacing;
6188     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
6190     gtk_adjustment_set_value(adj, spacing);
6194 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
6195     NULL, /* child_added */
6196     NULL, /* child_removed */
6197     connector_tb_event_attr_changed,
6198     NULL, /* content_changed */
6199     NULL  /* order_changed */
6200 };
6203 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
6205     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
6207     {
6208         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
6209                                           _("Avoid"),
6210                                           _("Make connectors avoid selected objects"),
6211                                           "connector_avoid",
6212                                           secondarySize );
6213         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
6214         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6215     }
6217     {
6218         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
6219                                           _("Ignore"),
6220                                           _("Make connectors ignore selected objects"),
6221                                           "connector_ignore",
6222                                           secondarySize );
6223         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
6224         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6225     }
6227     EgeAdjustmentAction* eact = 0;
6229     // Spacing spinbox
6230     eact = create_adjustment_action( "ConnectorSpacingAction",
6231                                      _("Connector Spacing"), _("Spacing:"),
6232                                      _("The amount of space left around objects by auto-routing connectors"),
6233                                      "tools.connector", "spacing", defaultConnSpacing,
6234                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
6235                                      0, 100, 1.0, 10.0,
6236                                      0, 0, 0,
6237                                      connector_spacing_changed, 1, 0 );
6238     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6240     // Graph (connector network) layout
6241     {
6242         InkAction* inky = ink_action_new( "ConnectorGraphAction",
6243                                           _("Graph"),
6244                                           _("Nicely arrange selected connector network"),
6245                                           "graph_layout",
6246                                           secondarySize );
6247         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
6248         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6249     }
6251     // Default connector length spinbox
6252     eact = create_adjustment_action( "ConnectorLengthAction",
6253                                      _("Connector Length"), _("Length:"),
6254                                      _("Ideal length for connectors when layout is applied"),
6255                                      "tools.connector", "length", 100,
6256                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
6257                                      10, 1000, 10.0, 100.0,
6258                                      0, 0, 0,
6259                                      connector_length_changed, 1, 0 );
6260     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6263     // Directed edges toggle button
6264     {
6265         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
6266                                                       _("Downwards"),
6267                                                       _("Make connectors with end-markers (arrows) point downwards"),
6268                                                       "directed_graph",
6269                                                       Inkscape::ICON_SIZE_DECORATION );
6270         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6272         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
6273         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6274                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6276         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
6277     }
6279     // Avoid overlaps toggle button
6280     {
6281         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
6282                                                       _("Remove overlaps"),
6283                                                       _("Do not allow overlapping shapes"),
6284                                                       "remove_overlaps",
6285                                                       Inkscape::ICON_SIZE_DECORATION );
6286         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6288         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
6289         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6290                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6292         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
6293     }
6295     // Code to watch for changes to the connector-spacing attribute in
6296     // the XML.
6297     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6298     g_assert(repr != NULL);
6300     purge_repr_listener( holder, holder );
6302     if (repr) {
6303         g_object_set_data( holder, "repr", repr );
6304         Inkscape::GC::anchor(repr);
6305         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
6306         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
6307     }
6308 } // end of sp_connector_toolbox_prep()
6311 //#########################
6312 //##     Paintbucket     ##
6313 //#########################
6315 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
6317     gint channels = ege_select_one_action_get_active( act );
6318     flood_channels_set_channels( channels );
6321 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
6323     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
6326 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
6328     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
6331 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
6333     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
6334     SPUnit const *unit = tracker->getActiveUnit();
6336     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
6338     prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
6341 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
6343     // FIXME: make defaults settable via Inkscape Options
6344     struct KeyValue {
6345         char const *key;
6346         double value;
6347     } const key_values[] = {
6348         {"threshold", 15},
6349         {"offset", 0.0}
6350     };
6352     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
6353         KeyValue const &kv = key_values[i];
6354         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
6355         if ( adj ) {
6356             gtk_adjustment_set_value(adj, kv.value);
6357         }
6358     }
6360     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
6361     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
6362     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
6363     ege_select_one_action_set_active( autogap_action, 0 );
6366 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6368     EgeAdjustmentAction* eact = 0;
6370     {
6371         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6373         GList* items = 0;
6374         gint count = 0;
6375         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
6376         {
6377             GtkTreeIter iter;
6378             gtk_list_store_append( model, &iter );
6379             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6380             count++;
6381         }
6382         g_list_free( items );
6383         items = 0;
6384         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
6385         g_object_set( act1, "short_label", _("Fill by:"), NULL );
6386         ege_select_one_action_set_appearance( act1, "compact" );
6387         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
6388         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
6389         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
6390         g_object_set_data( holder, "channels_action", act1 );
6391     }
6393     // Spacing spinbox
6394     {
6395         eact = create_adjustment_action(
6396             "ThresholdAction",
6397             _("Fill Threshold"), _("Threshold:"),
6398             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
6399             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
6400             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
6401             0, 0, 0,
6402             paintbucket_threshold_changed, 1, 0 );
6404         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6405         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6406     }
6408     // Create the units menu.
6409     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
6410     const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
6411     if (stored_unit)
6412         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
6413     g_object_set_data( holder, "tracker", tracker );
6414     {
6415         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
6416         gtk_action_group_add_action( mainActions, act );
6417     }
6419     // Offset spinbox
6420     {
6421         eact = create_adjustment_action(
6422             "OffsetAction",
6423             _("Grow/shrink by"), _("Grow/shrink by:"),
6424             _("The amount to grow (positive) or shrink (negative) the created fill path"),
6425             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
6426             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
6427             0, 0, 0,
6428             paintbucket_offset_changed, 1, 2);
6429         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
6431         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6432     }
6434     /* Auto Gap */
6435     {
6436         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6438         GList* items = 0;
6439         gint count = 0;
6440         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
6441         {
6442             GtkTreeIter iter;
6443             gtk_list_store_append( model, &iter );
6444             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6445             count++;
6446         }
6447         g_list_free( items );
6448         items = 0;
6449         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
6450         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
6451         ege_select_one_action_set_appearance( act2, "compact" );
6452         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
6453         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
6454         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
6455         g_object_set_data( holder, "autogap_action", act2 );
6456     }
6458     /* Reset */
6459     {
6460         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
6461                                           _("Defaults"),
6462                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
6463                                           GTK_STOCK_CLEAR );
6464         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
6465         gtk_action_group_add_action( mainActions, act );
6466         gtk_action_set_sensitive( act, TRUE );
6467     }
6471 /*
6472   Local Variables:
6473   mode:c++
6474   c-file-style:"stroustrup"
6475   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
6476   indent-tabs-mode:nil
6477   fill-column:99
6478   End:
6479 */
6480 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :