Code

fix warning
[inkscape.git] / src / widgets / toolbox.cpp
1 /** \file
2  * Controls bars for some of Inkscape's tools
3  * (for some tools, they are in their own files)
4  */
6 /*
7 *
8 * Authors:
9 *   MenTaLguY <mental@rydia.net>
10 *   Lauris Kaplinski <lauris@kaplinski.com>
11 *   bulia byak <buliabyak@users.sf.net>
12 *   Frank Felfe <innerspace@iname.com>
13 *   John Cliff <simarilius@yahoo.com>
14 *   David Turner <novalis@gnu.org>
15 *   Josh Andler <scislac@scislac.com>
16 *   Jon A. Cruz <jon@joncruz.org>
17 *
18 * Copyright (C) 2004 David Turner
19 * Copyright (C) 2003 MenTaLguY
20 * Copyright (C) 1999-2006 authors
21 * Copyright (C) 2001-2002 Ximian, Inc.
22 *
23 * Released under GNU GPL, read the file 'COPYING' for more information
24 */
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
30 #include <cstring>
31 #include <string>
33 #include <gtkmm.h>
34 #include <gtk/gtk.h>
35 #include <iostream>
36 #include <sstream>
38 #include "widgets/button.h"
39 #include "widgets/widget-sizes.h"
40 #include "widgets/spw-utilities.h"
41 #include "widgets/spinbutton-events.h"
42 #include "dialogs/text-edit.h"
43 #include "dialogs/dialog-events.h"
45 #include "ui/widget/style-swatch.h"
47 #include "prefs-utils.h"
48 #include "verbs.h"
49 #include "sp-namedview.h"
50 #include "desktop.h"
51 #include "desktop-handles.h"
52 #include "xml/repr.h"
53 #include "xml/node-event-vector.h"
54 #include "xml/attribute-record.h"
55 #include <glibmm/i18n.h>
56 #include "helper/unit-menu.h"
57 #include "helper/units.h"
58 #include "live_effects/effect.h"
60 #include "inkscape.h"
61 #include "conn-avoid-ref.h"
64 #include "select-toolbar.h"
65 #include "gradient-toolbar.h"
67 #include "connector-context.h"
68 #include "node-context.h"
69 #include "draw-context.h"
70 #include "shape-editor.h"
71 #include "tweak-context.h"
72 #include "sp-rect.h"
73 #include "box3d.h"
74 #include "box3d-context.h"
75 #include "sp-star.h"
76 #include "sp-spiral.h"
77 #include "sp-ellipse.h"
78 #include "sp-text.h"
79 #include "sp-flowtext.h"
80 #include "sp-clippath.h"
81 #include "sp-mask.h"
82 #include "style.h"
83 #include "selection.h"
84 #include "selection-chemistry.h"
85 #include "document-private.h"
86 #include "desktop-style.h"
87 #include "../libnrtype/font-lister.h"
88 #include "../libnrtype/font-instance.h"
89 #include "../connection-pool.h"
90 #include "../prefs-utils.h"
91 #include "../inkscape-stock.h"
92 #include "icon.h"
93 #include "graphlayout/graphlayout.h"
94 #include "interface.h"
95 #include "shortcuts.h"
97 #include "mod360.h"
99 #include "toolbox.h"
101 #include "flood-context.h"
103 #include "ink-action.h"
104 #include "ege-adjustment-action.h"
105 #include "ege-output-action.h"
106 #include "ege-select-one-action.h"
107 #include "helper/unit-tracker.h"
109 #include "svg/css-ostringstream.h"
111 #include "widgets/calligraphic-profile-rename.h"
113 using Inkscape::UnitTracker;
115 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
116 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
118 static void       sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
119 static void       sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
120 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
121 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
122 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
123 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 static void       box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
126 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
127 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
128 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
129 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
130 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
131 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
132 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
133 static void       sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
135 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
138 Inkscape::IconSize prefToSize( gchar const *path, gchar const *attr, int base ) {
139     static Inkscape::IconSize sizeChoices[] = {
140         Inkscape::ICON_SIZE_LARGE_TOOLBAR,
141         Inkscape::ICON_SIZE_SMALL_TOOLBAR,
142         Inkscape::ICON_SIZE_MENU
143     };
144     int index = prefs_get_int_attribute_limited( path, attr, base, 0, G_N_ELEMENTS(sizeChoices) );
145     return sizeChoices[index];
148 static struct {
149     gchar const *type_name;
150     gchar const *data_name;
151     sp_verb_t verb;
152     sp_verb_t doubleclick_verb;
153 } const tools[] = {
154     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
155     { "SPNodeContext",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
156     { "SPTweakContext",    "tweak_tool",     SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
157     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
158     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
159     { "Box3DContext",      "3dbox_tool",     SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
160     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
161     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
162     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
163     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
164     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
165     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
166     { "SPEraserContext",   "eraser_tool",    SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
167     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
168     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
169     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
170     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
171     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
172     { NULL, NULL, 0, 0 }
173 };
175 static struct {
176     gchar const *type_name;
177     gchar const *data_name;
178     GtkWidget *(*create_func)(SPDesktop *desktop);
179     void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
180     gchar const *ui_name;
181     gint swatch_verb_id;
182     gchar const *swatch_tool;
183     gchar const *swatch_tip;
184 } const aux_toolboxes[] = {
185     { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep,            "SelectToolbar",
186       SP_VERB_INVALID, 0, 0},
187     { "SPNodeContext",   "node_toolbox",   0, sp_node_toolbox_prep,              "NodeToolbar",
188       SP_VERB_INVALID, 0, 0},
189     { "SPTweakContext",   "tweak_toolbox",   0, sp_tweak_toolbox_prep,              "TweakToolbar",
190       SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", N_("Color/opacity used for color tweaking")},
191     { "SPZoomContext",   "zoom_toolbox",   0, sp_zoom_toolbox_prep,              "ZoomToolbar",
192       SP_VERB_INVALID, 0, 0},
193     { "SPStarContext",   "star_toolbox",   0, sp_star_toolbox_prep,              "StarToolbar",
194       SP_VERB_CONTEXT_STAR_PREFS,   "tools.shapes.star",     N_("Style of new stars")},
195     { "SPRectContext",   "rect_toolbox",   0, sp_rect_toolbox_prep,              "RectToolbar",
196       SP_VERB_CONTEXT_RECT_PREFS,   "tools.shapes.rect",     N_("Style of new rectangles")},
197     { "Box3DContext",  "3dbox_toolbox",  0, box3d_toolbox_prep,             "3DBoxToolbar",
198       SP_VERB_CONTEXT_3DBOX_PREFS,  "tools.shapes.3dbox",    N_("Style of new 3D boxes")},
199     { "SPArcContext",    "arc_toolbox",    0, sp_arc_toolbox_prep,               "ArcToolbar",
200       SP_VERB_CONTEXT_ARC_PREFS,    "tools.shapes.arc",      N_("Style of new ellipses")},
201     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
202       SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral",   N_("Style of new spirals")},
203     { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep,            "PencilToolbar",
204       SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", N_("Style of new paths created by Pencil")},
205     { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep,                     "PenToolbar",
206       SP_VERB_CONTEXT_PEN_PREFS,    "tools.freehand.pen",    N_("Style of new paths created by Pen")},
207     { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
208       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", N_("Style of new calligraphic strokes")},
209     { "SPEraserContext", "eraser_toolbox", 0, sp_eraser_toolbox_prep,"EraserToolbar",
210       SP_VERB_CONTEXT_ERASER_PREFS, "tools.eraser", _("TBD")},
211     { "SPTextContext",   "text_toolbox",   sp_text_toolbox_new, 0,               0,
212       SP_VERB_INVALID, 0, 0},
213     { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep,         "DropperToolbar",
214       SP_VERB_INVALID, 0, 0},
215     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0,       0,
216       SP_VERB_INVALID, 0, 0},
217     { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep,   "ConnectorToolbar",
218       SP_VERB_INVALID, 0, 0},
219     { "SPFloodContext",  "paintbucket_toolbox",  0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
220       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", N_("Style of Paint Bucket fill objects")},
221     { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
222 };
224 #define TOOLBAR_SLIDER_HINT "full"
226 static gchar const * ui_descr =
227         "<ui>"
228         "  <toolbar name='SelectToolbar'>"
229         "    <toolitem action='EditSelectAll' />"
230         "    <toolitem action='EditSelectAllInAllLayers' />"
231         "    <toolitem action='EditDeselect' />"
232         "    <separator />"
233         "    <toolitem action='ObjectRotate90CCW' />"
234         "    <toolitem action='ObjectRotate90' />"
235         "    <toolitem action='ObjectFlipHorizontally' />"
236         "    <toolitem action='ObjectFlipVertically' />"
237         "    <separator />"
238         "    <toolitem action='SelectionToBack' />"
239         "    <toolitem action='SelectionLower' />"
240         "    <toolitem action='SelectionRaise' />"
241         "    <toolitem action='SelectionToFront' />"
242         "    <separator />"
243         "    <toolitem action='XAction' />"
244         "    <toolitem action='YAction' />"
245         "    <toolitem action='WidthAction' />"
246         "    <toolitem action='LockAction' />"
247         "    <toolitem action='HeightAction' />"
248         "    <toolitem action='UnitsAction' />"
249         "    <separator />"
250         "    <toolitem action='transform_affect_label' />"
251         "    <toolitem action='transform_stroke' />"
252         "    <toolitem action='transform_corners' />"
253         "    <toolitem action='transform_gradient' />"
254         "    <toolitem action='transform_pattern' />"
255         "  </toolbar>"
257         "  <toolbar name='NodeToolbar'>"
258         "    <toolitem action='NodeInsertAction' />"
259         "    <toolitem action='NodeDeleteAction' />"
260         "    <separator />"
261         "    <toolitem action='NodeJoinAction' />"
262         "    <toolitem action='NodeBreakAction' />"
263         "    <separator />"
264         "    <toolitem action='NodeJoinSegmentAction' />"
265         "    <toolitem action='NodeDeleteSegmentAction' />"
266         "    <separator />"
267         "    <toolitem action='NodeCuspAction' />"
268         "    <toolitem action='NodeSmoothAction' />"
269         "    <toolitem action='NodeSymmetricAction' />"
270         "    <separator />"
271         "    <toolitem action='NodeLineAction' />"
272         "    <toolitem action='NodeCurveAction' />"
273         "    <separator />"
274         "    <toolitem action='ObjectToPath' />"
275         "    <toolitem action='StrokeToPath' />"
276         "    <separator />"
277         "    <toolitem action='NodeXAction' />"
278         "    <toolitem action='NodeYAction' />"
279         "    <toolitem action='NodeUnitsAction' />"
280         "    <separator />"
281         "    <toolitem action='ObjectEditClipPathAction' />"
282         "    <toolitem action='ObjectEditMaskPathAction' />"
283         "    <toolitem action='EditNextLPEParameterAction' />"
284         "    <separator />"
285         "    <toolitem action='NodesShowHandlesAction' />"
286         "    <toolitem action='NodesShowHelperpath' />"
287         "  </toolbar>"
289         "  <toolbar name='TweakToolbar'>"
290         "    <toolitem action='TweakWidthAction' />"
291         "    <separator />"
292         "    <toolitem action='TweakForceAction' />"
293         "    <toolitem action='TweakPressureAction' />"
294         "    <separator />"
295         "    <toolitem action='TweakModeAction' />"
296         "    <separator />"
297         "    <toolitem action='TweakFidelityAction' />"
298         "    <separator />"
299         "    <toolitem action='TweakChannelsLabel' />"
300         "    <toolitem action='TweakDoH' />"
301         "    <toolitem action='TweakDoS' />"
302         "    <toolitem action='TweakDoL' />"
303         "    <toolitem action='TweakDoO' />"
304         "  </toolbar>"
306         "  <toolbar name='ZoomToolbar'>"
307         "    <toolitem action='ZoomIn' />"
308         "    <toolitem action='ZoomOut' />"
309         "    <separator />"
310         "    <toolitem action='Zoom1:0' />"
311         "    <toolitem action='Zoom1:2' />"
312         "    <toolitem action='Zoom2:1' />"
313         "    <separator />"
314         "    <toolitem action='ZoomSelection' />"
315         "    <toolitem action='ZoomDrawing' />"
316         "    <toolitem action='ZoomPage' />"
317         "    <toolitem action='ZoomPageWidth' />"
318         "    <separator />"
319         "    <toolitem action='ZoomPrev' />"
320         "    <toolitem action='ZoomNext' />"
321         "  </toolbar>"
323         "  <toolbar name='StarToolbar'>"
324         "    <separator />"
325         "    <toolitem action='StarStateAction' />"
326         "    <separator />"
327         "    <toolitem action='FlatAction' />"
328         "    <separator />"
329         "    <toolitem action='MagnitudeAction' />"
330         "    <toolitem action='SpokeAction' />"
331         "    <toolitem action='RoundednessAction' />"
332         "    <toolitem action='RandomizationAction' />"
333         "    <separator />"
334         "    <toolitem action='StarResetAction' />"
335         "  </toolbar>"
337         "  <toolbar name='RectToolbar'>"
338         "    <toolitem action='RectStateAction' />"
339         "    <toolitem action='RectWidthAction' />"
340         "    <toolitem action='RectHeightAction' />"
341         "    <toolitem action='RadiusXAction' />"
342         "    <toolitem action='RadiusYAction' />"
343         "    <toolitem action='RectUnitsAction' />"
344         "    <separator />"
345         "    <toolitem action='RectResetAction' />"
346         "  </toolbar>"
348         "  <toolbar name='3DBoxToolbar'>"
349         "    <toolitem action='3DBoxAngleXAction' />"
350         "    <toolitem action='3DBoxVPXStateAction' />"
351         "    <separator />"
352         "    <toolitem action='3DBoxAngleYAction' />"
353         "    <toolitem action='3DBoxVPYStateAction' />"
354         "    <separator />"
355         "    <toolitem action='3DBoxAngleZAction' />"
356         "    <toolitem action='3DBoxVPZStateAction' />"
357         "  </toolbar>"
359         "  <toolbar name='SpiralToolbar'>"
360         "    <toolitem action='SpiralStateAction' />"
361         "    <toolitem action='SpiralRevolutionAction' />"
362         "    <toolitem action='SpiralExpansionAction' />"
363         "    <toolitem action='SpiralT0Action' />"
364         "    <separator />"
365         "    <toolitem action='SpiralResetAction' />"
366         "  </toolbar>"
368         "  <toolbar name='PenToolbar'>"
369         "    <toolitem action='FreehandModeActionPenTemp' />"
370         "    <toolitem action='FreehandModeActionPen' />"
371         "    <separator />"
372         "    <toolitem action='SetPenShapeAction'/>"
373         "  </toolbar>"
375         "  <toolbar name='PencilToolbar'>"
376         "    <toolitem action='FreehandModeActionPencilTemp' />"
377         "    <toolitem action='FreehandModeActionPencil' />"
378         "    <separator />"
379         "    <toolitem action='PencilToleranceAction' />"
380         "    <separator />"
381         "    <toolitem action='PencilResetAction' />"
382         "    <separator />"
383         "    <toolitem action='SetPencilShapeAction'/>"
384         "  </toolbar>"
386         "  <toolbar name='CalligraphyToolbar'>"
387         "    <separator />"
388         "    <toolitem action='SetProfileAction'/>"
389         "    <toolitem action='SaveDeleteProfileAction'/>"
390         "    <separator />"
391         "    <toolitem action='CalligraphyWidthAction' />"
392         "    <toolitem action='PressureAction' />"
393         "    <toolitem action='TraceAction' />"
394         "    <toolitem action='ThinningAction' />"
395         "    <separator />"
396         "    <toolitem action='AngleAction' />"
397         "    <toolitem action='TiltAction' />"
398         "    <toolitem action='FixationAction' />"
399         "    <separator />"
400         "    <toolitem action='CapRoundingAction' />"
401         "    <separator />"
402         "    <toolitem action='TremorAction' />"
403         "    <toolitem action='WiggleAction' />"
404         "    <toolitem action='MassAction' />"
405         "    <separator />"
406         "  </toolbar>"
408         "  <toolbar name='ArcToolbar'>"
409         "    <toolitem action='ArcStateAction' />"
410         "    <separator />"
411         "    <toolitem action='ArcStartAction' />"
412         "    <toolitem action='ArcEndAction' />"
413         "    <separator />"
414         "    <toolitem action='ArcOpenAction' />"
415         "    <separator />"
416         "    <toolitem action='ArcResetAction' />"
417         "    <separator />"
418         "  </toolbar>"
420         "  <toolbar name='PaintbucketToolbar'>"
421         "    <toolitem action='ChannelsAction' />"
422         "    <separator />"
423         "    <toolitem action='ThresholdAction' />"
424         "    <separator />"
425         "    <toolitem action='OffsetAction' />"
426         "    <toolitem action='PaintbucketUnitsAction' />"
427         "    <separator />"
428         "    <toolitem action='AutoGapAction' />"
429         "    <separator />"
430         "    <toolitem action='PaintbucketResetAction' />"
431         "  </toolbar>"
433         "  <toolbar name='EraserToolbar'>"
434         "    <toolitem action='EraserWidthAction' />"
435         "    <separator />"
436         "    <toolitem action='EraserModeAction' />"
437         "  </toolbar>"
439         "  <toolbar name='DropperToolbar'>"
440         "    <toolitem action='DropperOpacityAction' />"
441         "    <toolitem action='DropperPickAlphaAction' />"
442         "    <toolitem action='DropperSetAlphaAction' />"
443         "  </toolbar>"
445         "  <toolbar name='ConnectorToolbar'>"
446         "    <toolitem action='ConnectorAvoidAction' />"
447         "    <toolitem action='ConnectorIgnoreAction' />"
448         "    <toolitem action='ConnectorSpacingAction' />"
449         "    <toolitem action='ConnectorGraphAction' />"
450         "    <toolitem action='ConnectorLengthAction' />"
451         "    <toolitem action='ConnectorDirectedAction' />"
452         "    <toolitem action='ConnectorOverlapAction' />"
453         "  </toolbar>"
455         "</ui>"
458 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
460 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
462 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
463 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
465 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
466 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
468 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
469 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
472 GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick( GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
473                                                               Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
474                                                               Inkscape::UI::View::View *view, GtkTooltips *tt);
476 class VerbAction : public Gtk::Action {
477 public:
478     static Glib::RefPtr<VerbAction> create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
480     virtual ~VerbAction();
481     virtual void set_active(bool active = true);
483 protected:
484     virtual Gtk::Widget* create_menu_item_vfunc();
485     virtual Gtk::Widget* create_tool_item_vfunc();
487     virtual void connect_proxy_vfunc(Gtk::Widget* proxy);
488     virtual void disconnect_proxy_vfunc(Gtk::Widget* proxy);
490     virtual void on_activate();
492 private:
493     Inkscape::Verb* verb;
494     Inkscape::Verb* verb2;
495     Inkscape::UI::View::View *view;
496     GtkTooltips *tooltips;
497     bool active;
499     VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
500 };
503 Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips)
505     Glib::RefPtr<VerbAction> result;
506     SPAction *action = verb->get_action(view);
507     if ( action ) {
508         //SPAction* action2 = verb2 ? verb2->get_action(view) : 0;
509         result = Glib::RefPtr<VerbAction>(new VerbAction(verb, verb2, view, tooltips));
510     }
512     return result;
515 VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips) :
516     Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(verb->get_image()), Glib::ustring(_(verb->get_name())), Glib::ustring(_(verb->get_tip()))),
517     verb(verb),
518     verb2(verb2),
519     view(view),
520     tooltips(tooltips),
521     active(false)
525 VerbAction::~VerbAction()
529 Gtk::Widget* VerbAction::create_menu_item_vfunc()
531 // First call in to get the icon rendered if present in SVG
532     Gtk::Widget *widget = sp_icon_get_icon( property_stock_id().get_value().get_string(), Inkscape::ICON_SIZE_MENU );
533     delete widget;
534     widget = 0;
536     Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
537 //     g_message("create_menu_item_vfunc() = %p  for '%s'", widg, verb->get_id());
538     return widg;
541 Gtk::Widget* VerbAction::create_tool_item_vfunc()
543 //     Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
544     Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
545     GtkWidget* toolbox = 0;
546     GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
547                                                                           SP_BUTTON_TYPE_TOGGLE,
548                                                                           verb,
549                                                                           verb2,
550                                                                           view,
551                                                                           tooltips );
552     if ( active ) {
553         sp_button_toggle_set_down( SP_BUTTON(button), active);
554     }
555     gtk_widget_show_all( button );
556     Gtk::Widget* wrapped = Glib::wrap(button);
557     Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
558     holder->add(*wrapped);
560 //     g_message("create_tool_item_vfunc() = %p  for '%s'", holder, verb->get_id());
561     return holder;
564 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
566 //     g_message("connect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
567     Gtk::Action::connect_proxy_vfunc(proxy);
570 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
572 //     g_message("disconnect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
573     Gtk::Action::disconnect_proxy_vfunc(proxy);
576 void VerbAction::set_active(bool active)
578     this->active = active;
579     Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
580     for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
581         Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
582         if (ti) {
583             // *should* have one child that is the SPButton
584             Gtk::Widget* child = ti->get_child();
585             if ( child && SP_IS_BUTTON(child->gobj()) ) {
586                 SPButton* button = SP_BUTTON(child->gobj());
587                 sp_button_toggle_set_down( button, active );
588             }
589         }
590     }
593 void VerbAction::on_activate()
595     if ( verb ) {
596         SPAction *action = verb->get_action(view);
597         if ( action ) {
598             sp_action_perform(action, 0);
599         }
600     }
603 /* Global text entry widgets necessary for update */
604 /* GtkWidget *dropper_rgb_entry,
605           *dropper_opacity_entry ; */
606 // should be made a private member once this is converted to class
608 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
609     connection->disconnect();
610     delete connection;
613 static void purge_repr_listener( GObject* obj, GObject* tbl )
615     (void)obj;
616     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
617     if (oldrepr) { // remove old listener
618         sp_repr_remove_listener_by_data(oldrepr, tbl);
619         Inkscape::GC::release(oldrepr);
620         oldrepr = 0;
621         g_object_set_data( tbl, "repr", NULL );
622     }
625 GtkWidget *
626 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
627                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
628                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
630     SPAction *action = verb->get_action(view);
631     if (!action) return NULL;
633     SPAction *doubleclick_action;
634     if (doubleclick_verb)
635         doubleclick_action = doubleclick_verb->get_action(view);
636     else
637         doubleclick_action = NULL;
639     /* fixme: Handle sensitive/unsensitive */
640     /* fixme: Implement sp_button_new_from_action */
641     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
642     gtk_widget_show(b);
645     unsigned int shortcut = sp_shortcut_get_primary(verb);
646     if (shortcut) {
647         gchar key[256];
648         sp_ui_shortcut_string(shortcut, key);
649         gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
650         if ( t ) {
651             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
652         }
653         g_free(tip);
654     } else {
655         if ( t ) {
656             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
657         }
658     }
660     return b;
664 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
666     SPAction* targetAction = SP_ACTION(user_data);
667     if ( targetAction ) {
668         sp_action_perform( targetAction, NULL );
669     }
672 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
674     if ( data ) {
675         GtkAction* act = GTK_ACTION(data);
676         gtk_action_set_sensitive( act, sensitive );
677     }
680 static SPActionEventVector action_event_vector = {
681     {NULL},
682     NULL,
683     NULL,
684     sp_action_action_set_sensitive,
685     NULL,
686     NULL
687 };
689 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
691     GtkAction* act = 0;
693     SPAction* targetAction = verb->get_action(view);
694     InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size  );
695     act = GTK_ACTION(inky);
696     gtk_action_set_sensitive( act, targetAction->sensitive );
698     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
700     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
701     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
703     return act;
706 Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
708     Inkscape::UI::View::View *view = desktop;
709     gint verbsToUse[] = {
710         // disabled until we have icons for them:
711         //find
712         //SP_VERB_EDIT_TILE,
713         //SP_VERB_EDIT_UNTILE,
714         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
715         SP_VERB_DIALOG_DISPLAY,
716         SP_VERB_DIALOG_FILL_STROKE,
717         SP_VERB_DIALOG_NAMEDVIEW,
718         SP_VERB_DIALOG_TEXT,
719         SP_VERB_DIALOG_XML_EDITOR,
720         SP_VERB_EDIT_CLONE,
721         SP_VERB_EDIT_COPY,
722         SP_VERB_EDIT_CUT,
723         SP_VERB_EDIT_DUPLICATE,
724         SP_VERB_EDIT_PASTE,
725         SP_VERB_EDIT_REDO,
726         SP_VERB_EDIT_UNDO,
727         SP_VERB_EDIT_UNLINK_CLONE,
728         SP_VERB_FILE_EXPORT,
729         SP_VERB_FILE_IMPORT,
730         SP_VERB_FILE_NEW,
731         SP_VERB_FILE_OPEN,
732         SP_VERB_FILE_PRINT,
733         SP_VERB_FILE_SAVE,
734         SP_VERB_OBJECT_TO_CURVE,
735         SP_VERB_SELECTION_GROUP,
736         SP_VERB_SELECTION_OUTLINE,
737         SP_VERB_SELECTION_UNGROUP,
738         SP_VERB_ZOOM_1_1,
739         SP_VERB_ZOOM_1_2,
740         SP_VERB_ZOOM_2_1,
741         SP_VERB_ZOOM_DRAWING,
742         SP_VERB_ZOOM_IN,
743         SP_VERB_ZOOM_NEXT,
744         SP_VERB_ZOOM_OUT,
745         SP_VERB_ZOOM_PAGE,
746         SP_VERB_ZOOM_PAGE_WIDTH,
747         SP_VERB_ZOOM_PREV,
748         SP_VERB_ZOOM_SELECTION,
749     };
751     Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
753     static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
754     Glib::RefPtr<Gtk::ActionGroup> mainActions;
755     if ( groups.find(desktop) != groups.end() ) {
756         mainActions = groups[desktop];
757     }
759     if ( !mainActions ) {
760         mainActions = Gtk::ActionGroup::create("main");
761         groups[desktop] = mainActions;
762     }
764     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
765         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
766         if ( verb ) {
767             if (!mainActions->get_action(verb->get_id())) {
768                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
769                 mainActions->add(Glib::wrap(act));
770             }
771         }
772     }
774     if ( !mainActions->get_action("ToolZoom") ) {
775         GtkTooltips *tt = gtk_tooltips_new();
776         for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
777             Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
778             if ( va ) {
779                 mainActions->add(va);
780                 if ( i == 0 ) {
781                     va->set_active(true);
782                 }
783             }
784         }
785     }
788     return mainActions;
792 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
794     gtk_widget_set_size_request( widget,
795                                  widget->allocation.width,
796                                  widget->allocation.height );
799 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
801     gtk_widget_set_size_request( widget, -1, -1 );
806 GtkWidget *
807 sp_tool_toolbox_new()
809     GtkTooltips *tt = gtk_tooltips_new();
810     GtkWidget* tb = gtk_toolbar_new();
811     gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
812     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
814     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
815     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
817     gtk_widget_set_sensitive(tb, FALSE);
819     GtkWidget *hb = gtk_handle_box_new();
820     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
821     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
822     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
824     gtk_container_add(GTK_CONTAINER(hb), tb);
825     gtk_widget_show(GTK_WIDGET(tb));
827     sigc::connection* conn = new sigc::connection;
828     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
830     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
831     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
833     return hb;
836 GtkWidget *
837 sp_aux_toolbox_new()
839     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
841     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
843     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
845     gtk_widget_set_sensitive(tb, FALSE);
847     GtkWidget *hb = gtk_handle_box_new();
848     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
849     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
850     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
852     gtk_container_add(GTK_CONTAINER(hb), tb);
853     gtk_widget_show(GTK_WIDGET(tb));
855     sigc::connection* conn = new sigc::connection;
856     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
858     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
859     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
861     return hb;
864 //####################################
865 //# Commands Bar
866 //####################################
868 GtkWidget *
869 sp_commands_toolbox_new()
871     GtkWidget *tb = gtk_toolbar_new();
873     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
874     gtk_widget_set_sensitive(tb, FALSE);
876     GtkWidget *hb = gtk_handle_box_new();
877     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
878     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
879     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
881     gtk_container_add(GTK_CONTAINER(hb), tb);
882     gtk_widget_show(GTK_WIDGET(tb));
884     sigc::connection* conn = new sigc::connection;
885     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
887     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
888     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
890     return hb;
894 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
895                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
896                                                        gchar const *path, gchar const *data, gdouble def,
897                                                        GtkWidget *focusTarget,
898                                                        GtkWidget *us,
899                                                        GObject *dataKludge,
900                                                        gboolean altx, gchar const *altx_mark,
901                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
902                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
903                                                        void (*callback)(GtkAdjustment *, GObject *),
904                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
906     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
907                                                              lower, upper, step, page, page ) );
908     if (us) {
909         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
910     }
912     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
914     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
915     if ( shortLabel ) {
916         g_object_set( act, "short_label", shortLabel, NULL );
917     }
919     if ( (descrCount > 0) && descrLabels && descrValues ) {
920         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
921     }
923     if ( focusTarget ) {
924         ege_adjustment_action_set_focuswidget( act, focusTarget );
925     }
927     if ( altx && altx_mark ) {
928         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
929     }
931     if ( dataKludge ) {
932         g_object_set_data( dataKludge, data, adj );
933     }
935     // Using a cast just to make sure we pass in the right kind of function pointer
936     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
938     return act;
942 //####################################
943 //# node editing callbacks
944 //####################################
946 /**
947  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
948  */
949 static ShapeEditor *get_current_shape_editor()
951     if (!SP_ACTIVE_DESKTOP) {
952         return NULL;
953     }
955     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
957     if (!SP_IS_NODE_CONTEXT(event_context)) {
958         return NULL;
959     }
961     return SP_NODE_CONTEXT(event_context)->shape_editor;
965 void
966 sp_node_path_edit_add(void)
968     ShapeEditor *shape_editor = get_current_shape_editor();
969     if (shape_editor) shape_editor->add_node();
972 void
973 sp_node_path_edit_delete(void)
975     ShapeEditor *shape_editor = get_current_shape_editor();
976     if (shape_editor) shape_editor->delete_nodes_preserving_shape();
979 void
980 sp_node_path_edit_delete_segment(void)
982     ShapeEditor *shape_editor = get_current_shape_editor();
983     if (shape_editor) shape_editor->delete_segment();
986 void
987 sp_node_path_edit_break(void)
989     ShapeEditor *shape_editor = get_current_shape_editor();
990     if (shape_editor) shape_editor->break_at_nodes();
993 void
994 sp_node_path_edit_join(void)
996     ShapeEditor *shape_editor = get_current_shape_editor();
997     if (shape_editor) shape_editor->join_nodes();
1000 void
1001 sp_node_path_edit_join_segment(void)
1003     ShapeEditor *shape_editor = get_current_shape_editor();
1004     if (shape_editor) shape_editor->join_segments();
1007 void
1008 sp_node_path_edit_toline(void)
1010     ShapeEditor *shape_editor = get_current_shape_editor();
1011     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1014 void
1015 sp_node_path_edit_tocurve(void)
1017     ShapeEditor *shape_editor = get_current_shape_editor();
1018     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1021 void
1022 sp_node_path_edit_cusp(void)
1024     ShapeEditor *shape_editor = get_current_shape_editor();
1025     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1028 void
1029 sp_node_path_edit_smooth(void)
1031     ShapeEditor *shape_editor = get_current_shape_editor();
1032     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1035 void
1036 sp_node_path_edit_symmetrical(void)
1038     ShapeEditor *shape_editor = get_current_shape_editor();
1039     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1042 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1043     bool show = gtk_toggle_action_get_active( act );
1044     prefs_set_int_attribute ("tools.nodes", "show_handles",  show ? 1 : 0);
1045     ShapeEditor *shape_editor = get_current_shape_editor();
1046     if (shape_editor) shape_editor->show_handles(show);
1049 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1050     bool show = gtk_toggle_action_get_active( act );
1051     prefs_set_int_attribute ("tools.nodes", "show_helperpath",  show ? 1 : 0);
1052     ShapeEditor *shape_editor = get_current_shape_editor();
1053     if (shape_editor) shape_editor->show_helperpath(show);
1056 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1057     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1060 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1061     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1064 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1065     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1068 /* is called when the node selection is modified */
1069 static void
1070 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1072     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1073     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1074     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1075     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1077     // quit if run by the attr_changed listener
1078     if (g_object_get_data( tbl, "freeze" )) {
1079         return;
1080     }
1082     // in turn, prevent listener from responding
1083     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1085     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1086     SPUnit const *unit = tracker->getActiveUnit();
1088     ShapeEditor *shape_editor = get_current_shape_editor();
1089     if (shape_editor && shape_editor->has_nodepath()) {
1090         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1091         int n_selected = 0;
1092         if (nodepath) {
1093             n_selected = nodepath->numSelected();
1094         }
1096         if (n_selected == 0) {
1097             gtk_action_set_sensitive(xact, FALSE);
1098             gtk_action_set_sensitive(yact, FALSE);
1099         } else {
1100             gtk_action_set_sensitive(xact, TRUE);
1101             gtk_action_set_sensitive(yact, TRUE);
1102             NR::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1103             NR::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1105             if (n_selected == 1) {
1106                 NR::Point sel_node = nodepath->singleSelectedCoords();
1107                 if (oldx != sel_node[NR::X] || oldy != sel_node[NR::Y]) {
1108                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[NR::X], *unit));
1109                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[NR::Y], *unit));
1110                 }
1111             } else {
1112                 NR::Maybe<NR::Coord> x = sp_node_selected_common_coord(nodepath, NR::X);
1113                 NR::Maybe<NR::Coord> y = sp_node_selected_common_coord(nodepath, NR::Y);
1114                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1115                     /* Note: Currently x and y will always have a value, even if the coordinates of the
1116                        selected nodes don't coincide (in this case we use the coordinates of the center
1117                        of the bounding box). So the entries are never set to zero. */
1118                     // FIXME: Maybe we should clear the entry if several nodes are selected
1119                     //        instead of providing a kind of average value
1120                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1121                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1122                 }
1123             }
1124         }
1125     } else {
1126         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1127         gtk_action_set_sensitive(xact, FALSE);
1128         gtk_action_set_sensitive(yact, FALSE);
1129     }
1131     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1134 static void
1135 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1137     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1139     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1140     SPUnit const *unit = tracker->getActiveUnit();
1142     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1143         prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
1144     }
1146     // quit if run by the attr_changed listener
1147     if (g_object_get_data( tbl, "freeze" )) {
1148         return;
1149     }
1151     // in turn, prevent listener from responding
1152     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1154     ShapeEditor *shape_editor = get_current_shape_editor();
1155     if (shape_editor && shape_editor->has_nodepath()) {
1156         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1157         if (!strcmp(value_name, "x")) {
1158             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::X);
1159         }
1160         if (!strcmp(value_name, "y")) {
1161             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::Y);
1162         }
1163     }
1165     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1168 static void
1169 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1171     sp_node_path_value_changed(adj, tbl, "x");
1174 static void
1175 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1177     sp_node_path_value_changed(adj, tbl, "y");
1180 void
1181 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1183     {
1184     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1185     SPItem *item = selection->singleItem();
1186     if (item && SP_IS_LPE_ITEM(item)) {
1187        if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1188            gtk_action_set_sensitive(w, TRUE);
1189        } else {
1190            gtk_action_set_sensitive(w, FALSE);
1191        }
1192     } else {
1193        gtk_action_set_sensitive(w, FALSE);
1194     }
1195     }
1197     {
1198     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1199     SPItem *item = selection->singleItem();
1200     if (item && item->clip_ref && item->clip_ref->getObject()) {
1201        gtk_action_set_sensitive(w, TRUE);
1202     } else {
1203        gtk_action_set_sensitive(w, FALSE);
1204     }
1205     }
1207     {
1208     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1209     SPItem *item = selection->singleItem();
1210     if (item && item->mask_ref && item->mask_ref->getObject()) {
1211        gtk_action_set_sensitive(w, TRUE);
1212     } else {
1213        gtk_action_set_sensitive(w, FALSE);
1214     }
1215     }
1218 void
1219 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1221     sp_node_toolbox_sel_changed (selection, tbl);
1226 //################################
1227 //##    Node Editing Toolbox    ##
1228 //################################
1230 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1232     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1233     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1234     g_object_set_data( holder, "tracker", tracker );
1236     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
1238     {
1239         InkAction* inky = ink_action_new( "NodeInsertAction",
1240                                           _("Insert node"),
1241                                           _("Insert new nodes into selected segments"),
1242                                           "node_insert",
1243                                           secondarySize );
1244         g_object_set( inky, "short_label", _("Insert"), NULL );
1245         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1246         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1247     }
1249     {
1250         InkAction* inky = ink_action_new( "NodeDeleteAction",
1251                                           _("Delete node"),
1252                                           _("Delete selected nodes"),
1253                                           "node_delete",
1254                                           secondarySize );
1255         g_object_set( inky, "short_label", _("Delete"), NULL );
1256         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1257         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1258     }
1260     {
1261         InkAction* inky = ink_action_new( "NodeJoinAction",
1262                                           _("Join endnodes"),
1263                                           _("Join selected endnodes"),
1264                                           "node_join",
1265                                           secondarySize );
1266         g_object_set( inky, "short_label", _("Join"), NULL );
1267         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1268         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1269     }
1271     {
1272         InkAction* inky = ink_action_new( "NodeBreakAction",
1273                                           _("Break nodes"),
1274                                           _("Break path at selected nodes"),
1275                                           "node_break",
1276                                           secondarySize );
1277         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1278         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1279     }
1282     {
1283         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1284                                           _("Join with segment"),
1285                                           _("Join selected endnodes with a new segment"),
1286                                           "node_join_segment",
1287                                           secondarySize );
1288         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1289         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1290     }
1292     {
1293         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1294                                           _("Delete segment"),
1295                                           _("Delete segment between two non-endpoint nodes"),
1296                                           "node_delete_segment",
1297                                           secondarySize );
1298         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1299         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1300     }
1302     {
1303         InkAction* inky = ink_action_new( "NodeCuspAction",
1304                                           _("Node Cusp"),
1305                                           _("Make selected nodes corner"),
1306                                           "node_cusp",
1307                                           secondarySize );
1308         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1309         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1310     }
1312     {
1313         InkAction* inky = ink_action_new( "NodeSmoothAction",
1314                                           _("Node Smooth"),
1315                                           _("Make selected nodes smooth"),
1316                                           "node_smooth",
1317                                           secondarySize );
1318         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1319         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1320     }
1322     {
1323         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1324                                           _("Node Symmetric"),
1325                                           _("Make selected nodes symmetric"),
1326                                           "node_symmetric",
1327                                           secondarySize );
1328         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1329         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1330     }
1332     {
1333         InkAction* inky = ink_action_new( "NodeLineAction",
1334                                           _("Node Line"),
1335                                           _("Make selected segments lines"),
1336                                           "node_line",
1337                                           secondarySize );
1338         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1339         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1340     }
1342     {
1343         InkAction* inky = ink_action_new( "NodeCurveAction",
1344                                           _("Node Curve"),
1345                                           _("Make selected segments curves"),
1346                                           "node_curve",
1347                                           secondarySize );
1348         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1349         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1350     }
1352     {
1353         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1354                                                       _("Show Handles"),
1355                                                       _("Show the Bezier handles of selected nodes"),
1356                                                       "nodes_show_handles",
1357                                                       Inkscape::ICON_SIZE_DECORATION );
1358         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1359         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1360         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1361     }
1363     {
1364         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1365                                                       _("Show Outline"),
1366                                                       _("Show the outline of the path"),
1367                                                       "nodes_show_helperpath",
1368                                                       Inkscape::ICON_SIZE_DECORATION );
1369         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1370         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1371         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_helperpath", 0 ) );
1372     }
1374     {
1375         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1376                                           _("Next path effect parameter"),
1377                                           _("Show next path effect parameter for editing"),
1378                                           "edit_next_parameter",
1379                                           Inkscape::ICON_SIZE_DECORATION );
1380         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1381         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1382         g_object_set_data( holder, "nodes_lpeedit", inky);
1383     }
1385     {
1386         InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1387                                           _("Edit clipping path"),
1388                                           _("Edit the clipping path of the object"),
1389                                           "nodeedit-clippath",
1390                                           Inkscape::ICON_SIZE_DECORATION );
1391         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1392         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1393         g_object_set_data( holder, "nodes_clippathedit", inky);
1394     }
1396     {
1397         InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1398                                           _("Edit mask path"),
1399                                           _("Edit the mask of the object"),
1400                                           "nodeedit-mask",
1401                                           Inkscape::ICON_SIZE_DECORATION );
1402         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1403         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1404         g_object_set_data( holder, "nodes_maskedit", inky);
1405     }
1407     /* X coord of selected node(s) */
1408     {
1409         EgeAdjustmentAction* eact = 0;
1410         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1411         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1412         eact = create_adjustment_action( "NodeXAction",
1413                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1414                                          "tools.nodes", "Xcoord", 0,
1415                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1416                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1417                                          labels, values, G_N_ELEMENTS(labels),
1418                                          sp_node_path_x_value_changed );
1419         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1420         g_object_set_data( holder, "nodes_x_action", eact );
1421         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1422         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1423     }
1425     /* Y coord of selected node(s) */
1426     {
1427         EgeAdjustmentAction* eact = 0;
1428         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1429         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1430         eact = create_adjustment_action( "NodeYAction",
1431                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1432                                          "tools.nodes", "Ycoord", 0,
1433                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1434                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1435                                          labels, values, G_N_ELEMENTS(labels),
1436                                          sp_node_path_y_value_changed );
1437         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1438         g_object_set_data( holder, "nodes_y_action", eact );
1439         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1440         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1441     }
1443     // add the units menu
1444     {
1445         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1446         gtk_action_group_add_action( mainActions, act );
1447     }
1450     sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1452     //watch selection
1453     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1455     sigc::connection *c_selection_changed =
1456         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1457                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1458     pool->add_connection ("selection-changed", c_selection_changed);
1460     sigc::connection *c_selection_modified =
1461         new sigc::connection (sp_desktop_selection (desktop)->connectModified
1462                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1463     pool->add_connection ("selection-modified", c_selection_modified);
1465     sigc::connection *c_subselection_changed =
1466         new sigc::connection (desktop->connectToolSubselectionChanged
1467                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1468     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1470     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1472     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1473 } // end of sp_node_toolbox_prep()
1476 //########################
1477 //##    Zoom Toolbox    ##
1478 //########################
1480 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1482     // no custom GtkAction setup needed
1483 } // end of sp_zoom_toolbox_prep()
1485 void
1486 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1488     toolbox_set_desktop(toolbox,
1489                         desktop,
1490                         setup_tool_toolbox,
1491                         update_tool_toolbox,
1492                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1493                                                                          "event_context_connection")));
1497 void
1498 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1500     toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1501                         desktop,
1502                         setup_aux_toolbox,
1503                         update_aux_toolbox,
1504                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1505                                                                          "event_context_connection")));
1508 void
1509 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1511     toolbox_set_desktop(toolbox,
1512                         desktop,
1513                         setup_commands_toolbox,
1514                         update_commands_toolbox,
1515                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1516                                                                          "event_context_connection")));
1519 static void
1520 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1522     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1523     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1525     if (old_desktop) {
1526         GList *children, *iter;
1528         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1529         for ( iter = children ; iter ; iter = iter->next ) {
1530             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1531         }
1532         g_list_free(children);
1533     }
1535     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1537     if (desktop) {
1538         gtk_widget_set_sensitive(toolbox, TRUE);
1539         setup_func(toolbox, desktop);
1540         update_func(desktop, desktop->event_context, toolbox);
1541         *conn = desktop->connectEventContextChanged
1542             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1543     } else {
1544         gtk_widget_set_sensitive(toolbox, FALSE);
1545     }
1547 } // end of toolbox_set_desktop()
1550 static void
1551 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1553     gchar const * descr =
1554         "<ui>"
1555         "  <toolbar name='ToolToolbar'>"
1556         "    <toolitem action='ToolSelector' />"
1557         "    <toolitem action='ToolNode' />"
1558         "    <toolitem action='ToolTweak' />"
1559         "    <toolitem action='ToolZoom' />"
1560         "    <toolitem action='ToolRect' />"
1561         "    <toolitem action='Tool3DBox' />"
1562         "    <toolitem action='ToolArc' />"
1563         "    <toolitem action='ToolStar' />"
1564         "    <toolitem action='ToolSpiral' />"
1565         "    <toolitem action='ToolPencil' />"
1566         "    <toolitem action='ToolPen' />"
1567         "    <toolitem action='ToolCalligraphic' />"
1568         "    <toolitem action='ToolEraser' />"
1569         "    <toolitem action='ToolPaintBucket' />"
1570         "    <toolitem action='ToolText' />"
1571         "    <toolitem action='ToolConnector' />"
1572         "    <toolitem action='ToolGradient' />"
1573         "    <toolitem action='ToolDropper' />"
1574         "  </toolbar>"
1575         "</ui>";
1576     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1577     GtkUIManager* mgr = gtk_ui_manager_new();
1578     GError* errVal = 0;
1580     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1581     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1583     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" );
1584     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1585         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1586     }
1587     Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1588     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1590     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
1591     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1593     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1595     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1596     if ( child ) {
1597         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1598     }
1600     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1601 //     Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1605 static void
1606 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1608     gchar const *const tname = ( eventcontext
1609                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1610                                  : NULL );
1611     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1613     for (int i = 0 ; tools[i].type_name ; i++ ) {
1614         Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1615         if ( act ) {
1616             bool setActive = tname && !strcmp(tname, tools[i].type_name);
1617             Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1618             if ( verbAct ) {
1619                 verbAct->set_active(setActive);
1620             }
1621         }
1622     }
1625 static void
1626 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1628     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1629     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1630     GtkUIManager* mgr = gtk_ui_manager_new();
1631     GError* errVal = 0;
1632     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1633     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1635     std::map<std::string, GtkWidget*> dataHolders;
1637     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1638         if ( aux_toolboxes[i].prep_func ) {
1639             // converted to GtkActions and UIManager
1641             GtkWidget* kludge = gtk_toolbar_new();
1642             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1643             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1644             dataHolders[aux_toolboxes[i].type_name] = kludge;
1645             aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1646         } else {
1648             GtkWidget *sub_toolbox = 0;
1649             if (aux_toolboxes[i].create_func == NULL)
1650                 sub_toolbox = sp_empty_toolbox_new(desktop);
1651             else {
1652                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1653             }
1655             gtk_size_group_add_widget( grouper, sub_toolbox );
1657             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1658             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1660         }
1661     }
1663     // Second pass to create toolbars *after* all GtkActions are created
1664     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1665         if ( aux_toolboxes[i].prep_func ) {
1666             // converted to GtkActions and UIManager
1668             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1670             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1671             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1673             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1674             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1675             g_free( tmp );
1676             tmp = 0;
1678             Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1679             if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1680                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1681             }
1682             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1685             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1687             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1688                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1689                 swatch->setDesktop( desktop );
1690                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1691                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1692                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1693                 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 );
1694             }
1696             gtk_widget_show_all( holder );
1697             sp_set_font_size_smaller( holder );
1699             gtk_size_group_add_widget( grouper, holder );
1701             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1702             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1703         }
1704     }
1706     g_object_unref( G_OBJECT(grouper) );
1709 static void
1710 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1712     gchar const *tname = ( eventcontext
1713                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1714                            : NULL );
1715     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1716         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1717         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1718             gtk_widget_show_all(sub_toolbox);
1719             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1720         } else {
1721             gtk_widget_hide(sub_toolbox);
1722         }
1723     }
1726 static void
1727 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1729     gchar const * descr =
1730         "<ui>"
1731         "  <toolbar name='CommandsToolbar'>"
1732         "    <toolitem action='FileNew' />"
1733         "    <toolitem action='FileOpen' />"
1734         "    <toolitem action='FileSave' />"
1735         "    <toolitem action='FilePrint' />"
1736         "    <separator />"
1737         "    <toolitem action='FileImport' />"
1738         "    <toolitem action='FileExport' />"
1739         "    <separator />"
1740         "    <toolitem action='EditUndo' />"
1741         "    <toolitem action='EditRedo' />"
1742         "    <separator />"
1743         "    <toolitem action='EditCopy' />"
1744         "    <toolitem action='EditCut' />"
1745         "    <toolitem action='EditPaste' />"
1746         "    <separator />"
1747         "    <toolitem action='ZoomSelection' />"
1748         "    <toolitem action='ZoomDrawing' />"
1749         "    <toolitem action='ZoomPage' />"
1750         "    <separator />"
1751         "    <toolitem action='EditDuplicate' />"
1752         "    <toolitem action='EditClone' />"
1753         "    <toolitem action='EditUnlinkClone' />"
1754         "    <separator />"
1755         "    <toolitem action='SelectionGroup' />"
1756         "    <toolitem action='SelectionUnGroup' />"
1757         "    <separator />"
1758         "    <toolitem action='DialogFillStroke' />"
1759         "    <toolitem action='DialogText' />"
1760         "    <toolitem action='DialogXMLEditor' />"
1761         "    <toolitem action='DialogAlignDistribute' />"
1762         "    <separator />"
1763         "    <toolitem action='DialogPreferences' />"
1764         "    <toolitem action='DialogDocumentProperties' />"
1765         "  </toolbar>"
1766         "</ui>";
1767     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1770     GtkUIManager* mgr = gtk_ui_manager_new();
1771     GError* errVal = 0;
1773     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1774     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1776     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1777     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1778         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1779     }
1781     Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1782     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1784     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1785     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1788     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1790     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1791     if ( child ) {
1792         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1793     }
1795     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1798 static void
1799 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1803 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1805     gtk_widget_show(toolbox_toplevel);
1806     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1808     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1809     if (!shown_toolbox) {
1810         return;
1811     }
1812     gtk_widget_show(toolbox);
1814     gtk_widget_show_all(shown_toolbox);
1817 static GtkWidget *
1818 sp_empty_toolbox_new(SPDesktop *desktop)
1820     GtkWidget *tbl = gtk_toolbar_new();
1821     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1822     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1824     gtk_widget_show_all(tbl);
1825     sp_set_font_size_smaller (tbl);
1827     return tbl;
1830 #define MODE_LABEL_WIDTH 70
1832 //########################
1833 //##       Star         ##
1834 //########################
1836 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1838     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1840     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1841         // do not remember prefs if this call is initiated by an undo change, because undoing object
1842         // creation sets bogus values to its attributes before it is deleted
1843         prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1844     }
1846     // quit if run by the attr_changed listener
1847     if (g_object_get_data( dataKludge, "freeze" )) {
1848         return;
1849     }
1851     // in turn, prevent listener from responding
1852     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1854     bool modmade = false;
1856     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1857     GSList const *items = selection->itemList();
1858     for (; items != NULL; items = items->next) {
1859         if (SP_IS_STAR((SPItem *) items->data)) {
1860             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1861             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1862             sp_repr_set_svg_double(repr, "sodipodi:arg2",
1863                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1864                                     + M_PI / (gint)adj->value));
1865             SP_OBJECT((SPItem *) items->data)->updateRepr();
1866             modmade = true;
1867         }
1868     }
1869     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1870                                    _("Star: Change number of corners"));
1872     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1875 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1877     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1879     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1880         prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1881     }
1883     // quit if run by the attr_changed listener
1884     if (g_object_get_data( dataKludge, "freeze" )) {
1885         return;
1886     }
1888     // in turn, prevent listener from responding
1889     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1891     bool modmade = false;
1892     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1893     GSList const *items = selection->itemList();
1894     for (; items != NULL; items = items->next) {
1895         if (SP_IS_STAR((SPItem *) items->data)) {
1896             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1898             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1899             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1900             if (r2 < r1) {
1901                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1902             } else {
1903                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1904             }
1906             SP_OBJECT((SPItem *) items->data)->updateRepr();
1907             modmade = true;
1908         }
1909     }
1911     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1912                                    _("Star: Change spoke ratio"));
1914     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1917 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1919     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1920     bool flat = ege_select_one_action_get_active( act ) == 0;
1922     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1923         prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1924                                     flat ? "true" : "false" );
1925     }
1927     // quit if run by the attr_changed listener
1928     if (g_object_get_data( dataKludge, "freeze" )) {
1929         return;
1930     }
1932     // in turn, prevent listener from responding
1933     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1935     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1936     GSList const *items = selection->itemList();
1937     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1938     bool modmade = false;
1940     if ( prop_action ) {
1941         gtk_action_set_sensitive( prop_action, !flat );
1942     }
1944     for (; items != NULL; items = items->next) {
1945         if (SP_IS_STAR((SPItem *) items->data)) {
1946             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1947             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1948             SP_OBJECT((SPItem *) items->data)->updateRepr();
1949             modmade = true;
1950         }
1951     }
1953     if (modmade) {
1954         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1955                          flat ? _("Make polygon") : _("Make star"));
1956     }
1958     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1961 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1963     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1965     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1966         prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1967     }
1969     // quit if run by the attr_changed listener
1970     if (g_object_get_data( dataKludge, "freeze" )) {
1971         return;
1972     }
1974     // in turn, prevent listener from responding
1975     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1977     bool modmade = false;
1979     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1980     GSList const *items = selection->itemList();
1981     for (; items != NULL; items = items->next) {
1982         if (SP_IS_STAR((SPItem *) items->data)) {
1983             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1984             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1985             SP_OBJECT(items->data)->updateRepr();
1986             modmade = true;
1987         }
1988     }
1989     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1990                                    _("Star: Change rounding"));
1992     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1995 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1997     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1999     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2000         prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
2001     }
2003     // quit if run by the attr_changed listener
2004     if (g_object_get_data( dataKludge, "freeze" )) {
2005         return;
2006     }
2008     // in turn, prevent listener from responding
2009     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2011     bool modmade = false;
2013     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2014     GSList const *items = selection->itemList();
2015     for (; items != NULL; items = items->next) {
2016         if (SP_IS_STAR((SPItem *) items->data)) {
2017             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2018             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2019             SP_OBJECT(items->data)->updateRepr();
2020             modmade = true;
2021         }
2022     }
2023     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2024                                    _("Star: Change randomization"));
2026     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2030 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2031                                        gchar const */*old_value*/, gchar const */*new_value*/,
2032                                        bool /*is_interactive*/, gpointer data)
2034     GtkWidget *tbl = GTK_WIDGET(data);
2036     // quit if run by the _changed callbacks
2037     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2038         return;
2039     }
2041     // in turn, prevent callbacks from responding
2042     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2044     GtkAdjustment *adj = 0;
2046     gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2047     bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2049     if (!strcmp(name, "inkscape:randomized")) {
2050         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2051         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2052     } else if (!strcmp(name, "inkscape:rounded")) {
2053         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2054         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2055     } else if (!strcmp(name, "inkscape:flatsided")) {
2056         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2057         char const *flatsides = repr->attribute("inkscape:flatsided");
2058         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2059         if ( flatsides && !strcmp(flatsides,"false") ) {
2060             ege_select_one_action_set_active( flat_action, 1 );
2061             gtk_action_set_sensitive( prop_action, TRUE );
2062         } else {
2063             ege_select_one_action_set_active( flat_action, 0 );
2064             gtk_action_set_sensitive( prop_action, FALSE );
2065         }
2066     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2067         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2068         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2069         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2070         if (r2 < r1) {
2071             gtk_adjustment_set_value(adj, r2/r1);
2072         } else {
2073             gtk_adjustment_set_value(adj, r1/r2);
2074         }
2075     } else if (!strcmp(name, "sodipodi:sides")) {
2076         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2077         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2078     }
2080     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2084 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2086     NULL, /* child_added */
2087     NULL, /* child_removed */
2088     star_tb_event_attr_changed,
2089     NULL, /* content_changed */
2090     NULL  /* order_changed */
2091 };
2094 /**
2095  *  \param selection Should not be NULL.
2096  */
2097 static void
2098 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2100     int n_selected = 0;
2101     Inkscape::XML::Node *repr = NULL;
2103     purge_repr_listener( tbl, tbl );
2105     for (GSList const *items = selection->itemList();
2106          items != NULL;
2107          items = items->next)
2108     {
2109         if (SP_IS_STAR((SPItem *) items->data)) {
2110             n_selected++;
2111             repr = SP_OBJECT_REPR((SPItem *) items->data);
2112         }
2113     }
2115     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2117     if (n_selected == 0) {
2118         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2119     } else if (n_selected == 1) {
2120         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2122         if (repr) {
2123             g_object_set_data( tbl, "repr", repr );
2124             Inkscape::GC::anchor(repr);
2125             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2126             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2127         }
2128     } else {
2129         // FIXME: implement averaging of all parameters for multiple selected stars
2130         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2131         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2132     }
2136 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2138     // FIXME: in this and all other _default functions, set some flag telling the value_changed
2139     // callbacks to lump all the changes for all selected objects in one undo step
2141     GtkAdjustment *adj = 0;
2143     // fixme: make settable in prefs!
2144     gint mag = 5;
2145     gdouble prop = 0.5;
2146     gboolean flat = FALSE;
2147     gdouble randomized = 0;
2148     gdouble rounded = 0;
2150     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2151     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2153     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2154     gtk_action_set_sensitive( sb2, !flat );
2156     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2157     gtk_adjustment_set_value(adj, mag);
2158     gtk_adjustment_value_changed(adj);
2160     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2161     gtk_adjustment_set_value(adj, prop);
2162     gtk_adjustment_value_changed(adj);
2164     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2165     gtk_adjustment_set_value(adj, rounded);
2166     gtk_adjustment_value_changed(adj);
2168     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2169     gtk_adjustment_set_value(adj, randomized);
2170     gtk_adjustment_value_changed(adj);
2174 void
2175 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2177     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2178     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2179     GtkWidget *l = gtk_label_new(NULL);
2180     gtk_label_set_markup(GTK_LABEL(l), title);
2181     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2182     if ( GTK_IS_TOOLBAR(tbl) ) {
2183         gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2184     } else {
2185         gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2186     }
2187     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2191 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2193     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2195     {
2196         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2197         ege_output_action_set_use_markup( act, TRUE );
2198         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2199         g_object_set_data( holder, "mode_action", act );
2200     }
2202     {
2203         EgeAdjustmentAction* eact = 0;
2204         gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2205         bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2207         /* Flatsided checkbox */
2208         {
2209             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2211             GtkTreeIter iter;
2212             gtk_list_store_append( model, &iter );
2213             gtk_list_store_set( model, &iter,
2214                                 0, _("Polygon"),
2215                                 1, _("Regular polygon (with one handle) instead of a star"),
2216                                 2, "star_flat",
2217                                 -1 );
2219             gtk_list_store_append( model, &iter );
2220             gtk_list_store_set( model, &iter,
2221                                 0, _("Star"),
2222                                 1, _("Star instead of a regular polygon (with one handle)"),
2223                                 2, "star_angled",
2224                                 -1 );
2226             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2227             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2228             g_object_set_data( holder, "flat_action", act );
2230             ege_select_one_action_set_appearance( act, "full" );
2231             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2232             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2233             ege_select_one_action_set_icon_column( act, 2 );
2234             ege_select_one_action_set_icon_size( act, secondarySize );
2235             ege_select_one_action_set_tooltip_column( act, 1  );
2237             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2238             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2239         }
2241         /* Magnitude */
2242         {
2243         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2244         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2245         eact = create_adjustment_action( "MagnitudeAction",
2246                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2247                                          "tools.shapes.star", "magnitude", 3,
2248                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2249                                          3, 1024, 1, 5,
2250                                          labels, values, G_N_ELEMENTS(labels),
2251                                          sp_stb_magnitude_value_changed,
2252                                          1.0, 0 );
2253         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2254         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2255         }
2257         /* Spoke ratio */
2258         {
2259         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2260         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2261         eact = create_adjustment_action( "SpokeAction",
2262                                          _("Spoke ratio"), _("Spoke ratio:"),
2263                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2264                                          // Base radius is the same for the closest handle.
2265                                          _("Base radius to tip radius ratio"),
2266                                          "tools.shapes.star", "proportion", 0.5,
2267                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2268                                          0.01, 1.0, 0.01, 0.1,
2269                                          labels, values, G_N_ELEMENTS(labels),
2270                                          sp_stb_proportion_value_changed );
2271         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2272         g_object_set_data( holder, "prop_action", eact );
2273         }
2275         if ( !isFlatSided ) {
2276             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2277         } else {
2278             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2279         }
2281         /* Roundedness */
2282         {
2283         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2284         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2285         eact = create_adjustment_action( "RoundednessAction",
2286                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2287                                          "tools.shapes.star", "rounded", 0.0,
2288                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2289                                          -10.0, 10.0, 0.01, 0.1,
2290                                          labels, values, G_N_ELEMENTS(labels),
2291                                          sp_stb_rounded_value_changed );
2292         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2293         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2294         }
2296         /* Randomization */
2297         {
2298         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2299         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2300         eact = create_adjustment_action( "RandomizationAction",
2301                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2302                                          "tools.shapes.star", "randomized", 0.0,
2303                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2304                                          -10.0, 10.0, 0.001, 0.01,
2305                                          labels, values, G_N_ELEMENTS(labels),
2306                                          sp_stb_randomized_value_changed, 0.1, 3 );
2307         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2308         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2309         }
2310     }
2312     {
2313         /* Reset */
2314         {
2315             GtkAction* act = gtk_action_new( "StarResetAction",
2316                                              _("Defaults"),
2317                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2318                                              GTK_STOCK_CLEAR );
2319             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2320             gtk_action_group_add_action( mainActions, act );
2321             gtk_action_set_sensitive( act, TRUE );
2322         }
2323     }
2325     sigc::connection *connection = new sigc::connection(
2326         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2327         );
2328     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2329     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2333 //########################
2334 //##       Rect         ##
2335 //########################
2337 static void sp_rtb_sensitivize( GObject *tbl )
2339     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2340     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2341     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2343     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2344         gtk_action_set_sensitive( not_rounded, FALSE );
2345     } else {
2346         gtk_action_set_sensitive( not_rounded, TRUE );
2347     }
2351 static void
2352 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2353                           void (*setter)(SPRect *, gdouble))
2355     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2357     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2358     SPUnit const *unit = tracker->getActiveUnit();
2360     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2361         prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2362     }
2364     // quit if run by the attr_changed listener
2365     if (g_object_get_data( tbl, "freeze" )) {
2366         return;
2367     }
2369     // in turn, prevent listener from responding
2370     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2372     bool modmade = false;
2373     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2374     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2375         if (SP_IS_RECT(items->data)) {
2376             if (adj->value != 0) {
2377                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2378             } else {
2379                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2380             }
2381             modmade = true;
2382         }
2383     }
2385     sp_rtb_sensitivize( tbl );
2387     if (modmade) {
2388         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2389                                    _("Change rectangle"));
2390     }
2392     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2395 static void
2396 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2398     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2401 static void
2402 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2404     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2407 static void
2408 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2410     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2413 static void
2414 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2416     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2421 static void
2422 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2424     GtkAdjustment *adj = 0;
2426     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2427     gtk_adjustment_set_value(adj, 0.0);
2428     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2429     gtk_adjustment_value_changed(adj);
2431     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2432     gtk_adjustment_set_value(adj, 0.0);
2433     gtk_adjustment_value_changed(adj);
2435     sp_rtb_sensitivize( obj );
2438 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2439                                        gchar const */*old_value*/, gchar const */*new_value*/,
2440                                        bool /*is_interactive*/, gpointer data)
2442     GObject *tbl = G_OBJECT(data);
2444     // quit if run by the _changed callbacks
2445     if (g_object_get_data( tbl, "freeze" )) {
2446         return;
2447     }
2449     // in turn, prevent callbacks from responding
2450     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2452     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2453     SPUnit const *unit = tracker->getActiveUnit();
2455     gpointer item = g_object_get_data( tbl, "item" );
2456     if (item && SP_IS_RECT(item)) {
2457         {
2458             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2459             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2460             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2461         }
2463         {
2464             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2465             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2466             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2467         }
2469         {
2470             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2471             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2472             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2473         }
2475         {
2476             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2477             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2478             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2479         }
2480     }
2482     sp_rtb_sensitivize( tbl );
2484     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2488 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2489     NULL, /* child_added */
2490     NULL, /* child_removed */
2491     rect_tb_event_attr_changed,
2492     NULL, /* content_changed */
2493     NULL  /* order_changed */
2494 };
2496 /**
2497  *  \param selection should not be NULL.
2498  */
2499 static void
2500 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2502     int n_selected = 0;
2503     Inkscape::XML::Node *repr = NULL;
2504     SPItem *item = NULL;
2506     if ( g_object_get_data( tbl, "repr" ) ) {
2507         g_object_set_data( tbl, "item", NULL );
2508     }
2509     purge_repr_listener( tbl, tbl );
2511     for (GSList const *items = selection->itemList();
2512          items != NULL;
2513          items = items->next) {
2514         if (SP_IS_RECT((SPItem *) items->data)) {
2515             n_selected++;
2516             item = (SPItem *) items->data;
2517             repr = SP_OBJECT_REPR(item);
2518         }
2519     }
2521     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2523     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2525     if (n_selected == 0) {
2526         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2528         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2529         gtk_action_set_sensitive(w, FALSE);
2530         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2531         gtk_action_set_sensitive(h, FALSE);
2533     } else if (n_selected == 1) {
2534         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2535         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2537         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2538         gtk_action_set_sensitive(w, TRUE);
2539         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2540         gtk_action_set_sensitive(h, TRUE);
2542         if (repr) {
2543             g_object_set_data( tbl, "repr", repr );
2544             g_object_set_data( tbl, "item", item );
2545             Inkscape::GC::anchor(repr);
2546             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2547             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2548         }
2549     } else {
2550         // FIXME: implement averaging of all parameters for multiple selected
2551         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2552         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2553         sp_rtb_sensitivize( tbl );
2554     }
2558 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2560     EgeAdjustmentAction* eact = 0;
2561     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2563     {
2564         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2565         ege_output_action_set_use_markup( act, TRUE );
2566         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2567         g_object_set_data( holder, "mode_action", act );
2568     }
2570     // rx/ry units menu: create
2571     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2572     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2573     // fixme: add % meaning per cent of the width/height
2574     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2575     g_object_set_data( holder, "tracker", tracker );
2577     /* W */
2578     {
2579         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2580         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2581         eact = create_adjustment_action( "RectWidthAction",
2582                                          _("Width"), _("W:"), _("Width of rectangle"),
2583                                          "tools.shapes.rect", "width", 0,
2584                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2585                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2586                                          labels, values, G_N_ELEMENTS(labels),
2587                                          sp_rtb_width_value_changed );
2588         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2589         g_object_set_data( holder, "width_action", eact );
2590         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2591         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2592     }
2594     /* H */
2595     {
2596         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2597         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2598         eact = create_adjustment_action( "RectHeightAction",
2599                                          _("Height"), _("H:"), _("Height of rectangle"),
2600                                          "tools.shapes.rect", "height", 0,
2601                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2602                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2603                                          labels, values, G_N_ELEMENTS(labels),
2604                                          sp_rtb_height_value_changed );
2605         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2606         g_object_set_data( holder, "height_action", eact );
2607         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2608         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2609     }
2611     /* rx */
2612     {
2613         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2614         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2615         eact = create_adjustment_action( "RadiusXAction",
2616                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2617                                          "tools.shapes.rect", "rx", 0,
2618                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2619                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2620                                          labels, values, G_N_ELEMENTS(labels),
2621                                          sp_rtb_rx_value_changed);
2622         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2623         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2624     }
2626     /* ry */
2627     {
2628         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2629         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2630         eact = create_adjustment_action( "RadiusYAction",
2631                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2632                                          "tools.shapes.rect", "ry", 0,
2633                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2634                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2635                                          labels, values, G_N_ELEMENTS(labels),
2636                                          sp_rtb_ry_value_changed);
2637         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2638         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2639     }
2641     // add the units menu
2642     {
2643         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2644         gtk_action_group_add_action( mainActions, act );
2645     }
2647     /* Reset */
2648     {
2649         InkAction* inky = ink_action_new( "RectResetAction",
2650                                           _("Not rounded"),
2651                                           _("Make corners sharp"),
2652                                           "squared_corner",
2653                                           secondarySize );
2654         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2655         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2656         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2657         g_object_set_data( holder, "not_rounded", inky );
2658     }
2660     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2661     sp_rtb_sensitivize( holder );
2663     sigc::connection *connection = new sigc::connection(
2664         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2665         );
2666     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2667     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2670 //########################
2671 //##       3D Box       ##
2672 //########################
2674 // normalize angle so that it lies in the interval [0,360]
2675 static double box3d_normalize_angle (double a) {
2676     double angle = a + ((int) (a/360.0))*360;
2677     if (angle < 0) {
2678         angle += 360.0;
2679     }
2680     return angle;
2683 static void
2684 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2685                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2686     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2687     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2688     //       are reset).
2689     bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2691     if (is_infinite) {
2692         gtk_toggle_action_set_active(tact, TRUE);
2693         gtk_action_set_sensitive(act, TRUE);
2695         double angle = persp3d_get_infinite_angle(persp, axis);
2696         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2697             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2698         }
2699     } else {
2700         gtk_toggle_action_set_active(tact, FALSE);
2701         gtk_action_set_sensitive(act, FALSE);
2702     }
2705 static void
2706 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2707     if (!persp_repr) {
2708         g_print ("No perspective given to box3d_resync_toolbar().\n");
2709         return;
2710     }
2712     GtkWidget *tbl = GTK_WIDGET(data);
2713     GtkAdjustment *adj = 0;
2714     GtkAction *act = 0;
2715     GtkToggleAction *tact = 0;
2716     Persp3D *persp = persp3d_get_from_repr(persp_repr);
2717     {
2718         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2719         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2720         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2722         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2723     }
2724     {
2725         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2726         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2727         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2729         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2730     }
2731     {
2732         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2733         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2734         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2736         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2737     }
2740 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2741                                                   gchar const */*old_value*/, gchar const */*new_value*/,
2742                                                   bool /*is_interactive*/, gpointer data)
2744     GtkWidget *tbl = GTK_WIDGET(data);
2746     // quit if run by the attr_changed listener
2747     // note: it used to work without the differently called freeze_ attributes (here and in
2748     //       box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2749     if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2750         return;
2751     }
2753     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2754     // sp_document_maybe_done() when the document is undo insensitive)
2755     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2757     // TODO: Only update the appropriate part of the toolbar
2758 //    if (!strcmp(name, "inkscape:vp_z")) {
2759         box3d_resync_toolbar(repr, G_OBJECT(tbl));
2760 //    }
2762     Persp3D *persp = persp3d_get_from_repr(repr);
2763     persp3d_update_box_reprs(persp);
2765     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2768 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2770     NULL, /* child_added */
2771     NULL, /* child_removed */
2772     box3d_persp_tb_event_attr_changed,
2773     NULL, /* content_changed */
2774     NULL  /* order_changed */
2775 };
2777 /**
2778  *  \param selection Should not be NULL.
2779  */
2780 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2781 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2782 static void
2783 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2785     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2786     // disable the angle entry fields for this direction (otherwise entering a value in them should only
2787     // update the perspectives with infinite VPs and leave the other ones untouched).
2789     Inkscape::XML::Node *persp_repr = NULL;
2790     purge_repr_listener(tbl, tbl);
2792     SPItem *item = selection->singleItem();
2793     if (item && SP_IS_BOX3D(item)) {
2794         // FIXME: Also deal with multiple selected boxes
2795         SPBox3D *box = SP_BOX3D(item);
2796         Persp3D *persp = box3d_get_perspective(box);
2797         persp_repr = SP_OBJECT_REPR(persp);
2798         if (persp_repr) {
2799             g_object_set_data(tbl, "repr", persp_repr);
2800             Inkscape::GC::anchor(persp_repr);
2801             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2802             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2803         }
2805         inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2806         prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2808         box3d_resync_toolbar(persp_repr, tbl);
2809     }
2812 static void
2813 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2815     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2816     SPDocument *document = sp_desktop_document(desktop);
2818     // quit if run by the attr_changed listener
2819     // note: it used to work without the differently called freeze_ attributes (here and in
2820     //       box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2821     if (g_object_get_data( dataKludge, "freeze_attr" )) {
2822         return;
2823     }
2825     // in turn, prevent listener from responding
2826     g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2828     //Persp3D *persp = document->current_persp3d;
2829     std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
2830     if (sel_persps.empty()) {
2831         // this can happen when the document is created; we silently ignore it
2832         return;
2833     }
2834     Persp3D *persp = sel_persps.front();
2836     persp->tmat.set_infinite_direction (axis, adj->value);
2837     SP_OBJECT(persp)->updateRepr();
2839     // TODO: use the correct axis here, too
2840     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2842     g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2846 static void
2847 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2849     box3d_angle_value_changed(adj, dataKludge, Proj::X);
2852 static void
2853 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2855     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2858 static void
2859 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2861     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2865 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2867     // TODO: Take all selected perspectives into account
2868     std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
2869     if (sel_persps.empty()) {
2870         // this can happen when the document is created; we silently ignore it
2871         return;
2872     }
2873     Persp3D *persp = sel_persps.front();
2875     bool set_infinite = gtk_toggle_action_get_active(act);
2876     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2879 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2881     box3d_vp_state_changed(act, box3d_angle, Proj::X);
2884 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2886     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2889 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2891     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2894 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2896     EgeAdjustmentAction* eact = 0;
2897     SPDocument *document = sp_desktop_document (desktop);
2898     Persp3D *persp = document->current_persp3d;
2900     EgeAdjustmentAction* box3d_angle_x = 0;
2901     EgeAdjustmentAction* box3d_angle_y = 0;
2902     EgeAdjustmentAction* box3d_angle_z = 0;
2904     /* Angle X */
2905     {
2906         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2907         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2908         eact = create_adjustment_action( "3DBoxAngleXAction",
2909                                          _("Angle in X direction"), _("Angle X:"),
2910                                          // Translators: PL is short for 'perspective line'
2911                                          _("Angle of PLs in X direction"),
2912                                          "tools.shapes.3dbox", "box3d_angle_x", 30,
2913                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2914                                          -360.0, 360.0, 1.0, 10.0,
2915                                          labels, values, G_N_ELEMENTS(labels),
2916                                          box3d_angle_x_value_changed );
2917         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2918         g_object_set_data( holder, "box3d_angle_x_action", eact );
2919         box3d_angle_x = eact;
2920     }
2922     if (!persp3d_VP_is_finite(persp, Proj::X)) {
2923         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2924     } else {
2925         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2926     }
2929     /* VP X state */
2930     {
2931         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2932                                                       // Translators: VP is short for 'vanishing point'
2933                                                       _("State of VP in X direction"),
2934                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2935                                                       "toggle_vp_x",
2936                                                       Inkscape::ICON_SIZE_DECORATION );
2937         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2938         g_object_set_data( holder, "box3d_vp_x_state_action", act );
2939         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2940         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2941         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2942     }
2944     /* Angle Y */
2945     {
2946         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2947         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2948         eact = create_adjustment_action( "3DBoxAngleYAction",
2949                                          _("Angle in Y direction"), _("Angle Y:"),
2950                                          // Translators: PL is short for 'perspective line'
2951                                          _("Angle of PLs in Y direction"),
2952                                          "tools.shapes.3dbox", "box3d_angle_y", 30,
2953                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2954                                          -360.0, 360.0, 1.0, 10.0,
2955                                          labels, values, G_N_ELEMENTS(labels),
2956                                          box3d_angle_y_value_changed );
2957         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2958         g_object_set_data( holder, "box3d_angle_y_action", eact );
2959         box3d_angle_y = eact;
2960     }
2962     if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2963         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2964     } else {
2965         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2966     }
2968     /* VP Y state */
2969     {
2970         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2971                                                       // Translators: VP is short for 'vanishing point'
2972                                                       _("State of VP in Y direction"),
2973                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2974                                                       "toggle_vp_y",
2975                                                       Inkscape::ICON_SIZE_DECORATION );
2976         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2977         g_object_set_data( holder, "box3d_vp_y_state_action", act );
2978         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2979         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2980         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2981     }
2983     /* Angle Z */
2984     {
2985         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2986         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2987         eact = create_adjustment_action( "3DBoxAngleZAction",
2988                                          _("Angle in Z direction"), _("Angle Z:"),
2989                                          // Translators: PL is short for 'perspective line'
2990                                          _("Angle of PLs in Z direction"),
2991                                          "tools.shapes.3dbox", "box3d_angle_z", 30,
2992                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2993                                          -360.0, 360.0, 1.0, 10.0,
2994                                          labels, values, G_N_ELEMENTS(labels),
2995                                          box3d_angle_z_value_changed );
2996         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2997         g_object_set_data( holder, "box3d_angle_z_action", eact );
2998         box3d_angle_z = eact;
2999     }
3001     if (!persp3d_VP_is_finite(persp, Proj::Z)) {
3002         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3003     } else {
3004         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3005     }
3007     /* VP Z state */
3008     {
3009         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3010                                                       // Translators: VP is short for 'vanishing point'
3011                                                       _("State of VP in Z direction"),
3012                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3013                                                       "toggle_vp_z",
3014                                                       Inkscape::ICON_SIZE_DECORATION );
3015         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3016         g_object_set_data( holder, "box3d_vp_z_state_action", act );
3017         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3018         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3019         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3020     }
3022     sigc::connection *connection = new sigc::connection(
3023         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3024        );
3025     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3026     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3029 //########################
3030 //##       Spiral       ##
3031 //########################
3033 static void
3034 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
3036     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3038     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3039         prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
3040     }
3042     // quit if run by the attr_changed listener
3043     if (g_object_get_data( tbl, "freeze" )) {
3044         return;
3045     }
3047     // in turn, prevent listener from responding
3048     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3050     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3052     bool modmade = false;
3053     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3054          items != NULL;
3055          items = items->next)
3056     {
3057         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3058             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3059             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3060             SP_OBJECT((SPItem *) items->data)->updateRepr();
3061             modmade = true;
3062         }
3063     }
3065     g_free(namespaced_name);
3067     if (modmade) {
3068         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3069                                    _("Change spiral"));
3070     }
3072     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3075 static void
3076 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3078     sp_spl_tb_value_changed(adj, tbl, "revolution");
3081 static void
3082 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3084     sp_spl_tb_value_changed(adj, tbl, "expansion");
3087 static void
3088 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3090     sp_spl_tb_value_changed(adj, tbl, "t0");
3093 static void
3094 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3096     GtkWidget *tbl = GTK_WIDGET(obj);
3098     GtkAdjustment *adj;
3100     // fixme: make settable
3101     gdouble rev = 5;
3102     gdouble exp = 1.0;
3103     gdouble t0 = 0.0;
3105     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3106     gtk_adjustment_set_value(adj, rev);
3107     gtk_adjustment_value_changed(adj);
3109     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3110     gtk_adjustment_set_value(adj, exp);
3111     gtk_adjustment_value_changed(adj);
3113     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3114     gtk_adjustment_set_value(adj, t0);
3115     gtk_adjustment_value_changed(adj);
3117     spinbutton_defocus(GTK_OBJECT(tbl));
3121 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3122                                          gchar const */*old_value*/, gchar const */*new_value*/,
3123                                          bool /*is_interactive*/, gpointer data)
3125     GtkWidget *tbl = GTK_WIDGET(data);
3127     // quit if run by the _changed callbacks
3128     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3129         return;
3130     }
3132     // in turn, prevent callbacks from responding
3133     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3135     GtkAdjustment *adj;
3136     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3137     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3139     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3140     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3142     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3143     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3145     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3149 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3150     NULL, /* child_added */
3151     NULL, /* child_removed */
3152     spiral_tb_event_attr_changed,
3153     NULL, /* content_changed */
3154     NULL  /* order_changed */
3155 };
3157 static void
3158 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3160     int n_selected = 0;
3161     Inkscape::XML::Node *repr = NULL;
3163     purge_repr_listener( tbl, tbl );
3165     for (GSList const *items = selection->itemList();
3166          items != NULL;
3167          items = items->next)
3168     {
3169         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3170             n_selected++;
3171             repr = SP_OBJECT_REPR((SPItem *) items->data);
3172         }
3173     }
3175     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3177     if (n_selected == 0) {
3178         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3179     } else if (n_selected == 1) {
3180         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3182         if (repr) {
3183             g_object_set_data( tbl, "repr", repr );
3184             Inkscape::GC::anchor(repr);
3185             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3186             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3187         }
3188     } else {
3189         // FIXME: implement averaging of all parameters for multiple selected
3190         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3191         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3192     }
3196 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3198     EgeAdjustmentAction* eact = 0;
3199     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3201     {
3202         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3203         ege_output_action_set_use_markup( act, TRUE );
3204         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3205         g_object_set_data( holder, "mode_action", act );
3206     }
3208     /* Revolution */
3209     {
3210         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3211         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3212         eact = create_adjustment_action( "SpiralRevolutionAction",
3213                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3214                                          "tools.shapes.spiral", "revolution", 3.0,
3215                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3216                                          0.01, 1024.0, 0.1, 1.0,
3217                                          labels, values, G_N_ELEMENTS(labels),
3218                                          sp_spl_tb_revolution_value_changed, 1, 2);
3219         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3220     }
3222     /* Expansion */
3223     {
3224         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3225         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3226         eact = create_adjustment_action( "SpiralExpansionAction",
3227                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3228                                          "tools.shapes.spiral", "expansion", 1.0,
3229                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3230                                          0.0, 1000.0, 0.01, 1.0,
3231                                          labels, values, G_N_ELEMENTS(labels),
3232                                          sp_spl_tb_expansion_value_changed);
3233         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3234     }
3236     /* T0 */
3237     {
3238         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3239         gdouble values[] = {0, 0.5, 0.9};
3240         eact = create_adjustment_action( "SpiralT0Action",
3241                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3242                                          "tools.shapes.spiral", "t0", 0.0,
3243                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3244                                          0.0, 0.999, 0.01, 1.0,
3245                                          labels, values, G_N_ELEMENTS(labels),
3246                                          sp_spl_tb_t0_value_changed);
3247         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3248     }
3250     /* Reset */
3251     {
3252         InkAction* inky = ink_action_new( "SpiralResetAction",
3253                                           _("Defaults"),
3254                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3255                                           GTK_STOCK_CLEAR,
3256                                           secondarySize );
3257         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3258         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3259     }
3262     sigc::connection *connection = new sigc::connection(
3263         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3264         );
3265     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3266     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3269 //########################
3270 //##     Pen/Pencil    ##
3271 //########################
3273 static void sp_pc_spiro_spline_mode_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
3275     prefs_set_int_attribute("tools.freehand", "spiro-spline-mode", ege_select_one_action_get_active(act));
3278 static void sp_add_spiro_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3280     // FIXME: No action is needed, we only want a simple label. But sp_toolbox_add_label() always
3281     //        adds the label at the end of the toolbar, whence this workarund. How to avoid this?
3282     {
3283         EgeOutputAction* act = ege_output_action_new(
3284             tool_is_pencil ?
3285             "FreehandModeActionPencilTemp" :
3286             "FreehandModeActionPenTemp",
3287             _("<b>Mode:</b>"), "", 0 );
3288         ege_output_action_set_use_markup( act, TRUE );
3289         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3290         g_object_set_data( holder, "freehand_mode_action", act );
3291     }
3293     /* Freehand mode toggle buttons */
3294     {
3295         //gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
3296         //bool isSpiroMode = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
3297         guint spiroMode = prefs_get_int_attribute("tools.freehand", "spiro-spline-mode", 0);
3298         Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3300         {
3301             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3303             GtkTreeIter iter;
3304             gtk_list_store_append( model, &iter );
3305             gtk_list_store_set( model, &iter,
3306                                 0, _("Bézier"),
3307                                 1, _("Regular Bézier mode"),
3308                                 2, "bezier_mode",
3309                                 -1 );
3311             gtk_list_store_append( model, &iter );
3312             gtk_list_store_set( model, &iter,
3313                                 0, _("Spiro"),
3314                                 1, _("Spiro splines mode"),
3315                                 2, "spiro_splines_mode",
3316                                 -1 );
3318             EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3319                                                                 "FreehandModeActionPencil" :
3320                                                                 "FreehandModeActionPen",
3321                                                                 (""), (""), NULL, GTK_TREE_MODEL(model) );
3322             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3323             g_object_set_data( holder, "freehande_mode_action", act );
3325             ege_select_one_action_set_appearance( act, "full" );
3326             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3327             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3328             ege_select_one_action_set_icon_column( act, 2 );
3329             ege_select_one_action_set_icon_size( act, secondarySize );
3330             ege_select_one_action_set_tooltip_column( act, 1  );
3332             ege_select_one_action_set_active( act, spiroMode);
3333             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_pc_spiro_spline_mode_changed), holder);
3334         }
3335     }
3338 static void sp_freehand_change_shape(EgeSelectOneAction* act, GObject */*dataKludge*/) {
3339     gint shape = ege_select_one_action_get_active( act );
3340     prefs_set_int_attribute("tools.freehand", "shape", shape);
3343 /**
3344  * \brief Generate the list of freehand advanced shape option entries.
3345  */
3346 GList * freehand_shape_dropdown_items_list() {
3347     GList *glist = NULL;
3349     glist = g_list_append (glist, _("None"));
3350     glist = g_list_append (glist, _("Clipboard"));
3351     glist = g_list_append (glist, _("Crescendo"));
3352     glist = g_list_append (glist, _("Decrescendo"));
3354     return glist;
3357 static void
3358 sp_freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3359     /*advanced shape options */
3360     {
3361         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3363         GList* items = 0;
3364         gint count = 0;
3365         for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3366         {
3367             GtkTreeIter iter;
3368             gtk_list_store_append( model, &iter );
3369             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3370             count++;
3371         }
3372         g_list_free( items );
3373         items = 0;
3374         EgeSelectOneAction* act1 = ege_select_one_action_new(
3375             tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3376             _("Shape:"), (""), NULL, GTK_TREE_MODEL(model));
3377         g_object_set( act1, "short_label", _("Shape:"), NULL );
3378         ege_select_one_action_set_appearance( act1, "compact" );
3379         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.freehand", "shape", 0) );
3380         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(sp_freehand_change_shape), holder );
3381         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3382         g_object_set_data( holder, "shape_action", act1 );
3383     }
3386 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3388     sp_add_spiro_toggle(mainActions, holder, false);
3389     sp_freehand_add_advanced_shape_options(mainActions, holder, false);
3393 static void
3394 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3396     GtkWidget *tbl = GTK_WIDGET(obj);
3398     GtkAdjustment *adj;
3400     // fixme: make settable
3401     gdouble tolerance = 4;
3403     adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3404     gtk_adjustment_set_value(adj, tolerance);
3405     gtk_adjustment_value_changed(adj);
3407     spinbutton_defocus(GTK_OBJECT(tbl));
3410 static void
3411 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3414     // quit if run by the attr_changed listener
3415     if (g_object_get_data( tbl, "freeze" )) {
3416         return;
3417     }
3418     // in turn, prevent listener from responding
3419     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3420     prefs_set_double_attribute("tools.freehand.pencil",
3421                                "tolerance", adj->value);
3422     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3428 static void
3429 sp_pencil_tb_tolerance_value_changed_external(Inkscape::XML::Node */*repr*/,
3430                                               const gchar */*key*/,
3431                                               const gchar */*oldval*/,
3432                                               const gchar */*newval*/,
3433                                               bool /*is_interactive*/,
3434                                               void * data)
3436     GObject* tbl = G_OBJECT(data);
3437     if (g_object_get_data( tbl, "freeze" )) {
3438         return;
3439     }
3441     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3443     GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl,
3444                                                             "tolerance");
3446     double v = prefs_get_double_attribute("tools.freehand.pencil",
3447                                             "tolerance", adj->value);
3448     gtk_adjustment_set_value(adj, v);
3449     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3453 static Inkscape::XML::NodeEventVector pencil_node_events =
3455     NULL,
3456     NULL,
3457     sp_pencil_tb_tolerance_value_changed_external,
3458     NULL,
3459     NULL,
3460 };
3463 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3465     sp_add_spiro_toggle(mainActions, holder, true);
3467     EgeAdjustmentAction* eact = 0;
3469     /* Tolerance */
3470     {
3471         gchar const* labels[] = {_("(many nodes, rough)"), ("(default)"), 0, 0, 0, 0, ("(few nodes, smooth)")};
3472         gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
3473         eact = create_adjustment_action( "PencilToleranceAction",
3474                                          _("Smoothing:"), _("Smoothing"),
3475                  _("How much smoothing (simplifying) is applied to the line"),
3476                                          "tools.freehand.pencil", "tolerance",
3477                                          3.0,
3478                                          GTK_WIDGET(desktop->canvas), NULL,
3479                                          holder, TRUE, "altx-pencil",
3480                                          1, 100.0, 0.5, 0,
3481                                          labels, values, G_N_ELEMENTS(labels),
3482                                          sp_pencil_tb_tolerance_value_changed,
3483                                          1, 2);
3484         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3485         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3487         Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE,
3488                                                       "tools.freehand.pencil");
3489         repr->addListener(&pencil_node_events, G_OBJECT(holder));
3490         g_object_set_data(G_OBJECT(holder), "repr", repr);
3492     }
3494     /* advanced shape options */
3495     sp_freehand_add_advanced_shape_options(mainActions, holder, true);
3497     /* Reset */
3498     {
3499         InkAction* inky = ink_action_new( "PencilResetAction",
3500                                           _("Defaults"),
3501                                           _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3502                                           GTK_STOCK_CLEAR,
3503                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3504         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
3505         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3506     }
3508     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3513 //########################
3514 //##       Tweak        ##
3515 //########################
3517 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3519     prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3522 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3524     prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3527 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3529     prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3532 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3534     int mode = ege_select_one_action_get_active( act );
3535     prefs_set_int_attribute("tools.tweak", "mode", mode);
3537     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3538     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3539     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3540     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3541     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3542     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3543     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3544         if (doh) gtk_action_set_sensitive (doh, TRUE);
3545         if (dos) gtk_action_set_sensitive (dos, TRUE);
3546         if (dol) gtk_action_set_sensitive (dol, TRUE);
3547         if (doo) gtk_action_set_sensitive (doo, TRUE);
3548         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3549         if (fid) gtk_action_set_sensitive (fid, FALSE);
3550     } else {
3551         if (doh) gtk_action_set_sensitive (doh, FALSE);
3552         if (dos) gtk_action_set_sensitive (dos, FALSE);
3553         if (dol) gtk_action_set_sensitive (dol, FALSE);
3554         if (doo) gtk_action_set_sensitive (doo, FALSE);
3555         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3556         if (fid) gtk_action_set_sensitive (fid, TRUE);
3557     }
3560 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3562     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3565 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3566     bool show = gtk_toggle_action_get_active( act );
3567     prefs_set_int_attribute ("tools.tweak", "doh",  show ? 1 : 0);
3569 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3570     bool show = gtk_toggle_action_get_active( act );
3571     prefs_set_int_attribute ("tools.tweak", "dos",  show ? 1 : 0);
3573 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3574     bool show = gtk_toggle_action_get_active( act );
3575     prefs_set_int_attribute ("tools.tweak", "dol",  show ? 1 : 0);
3577 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3578     bool show = gtk_toggle_action_get_active( act );
3579     prefs_set_int_attribute ("tools.tweak", "doo",  show ? 1 : 0);
3582 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3584     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3586     {
3587         /* Width */
3588         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3589         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3590         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3591                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3592                                                               "tools.tweak", "width", 15,
3593                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3594                                                               1, 100, 1.0, 0.0,
3595                                                               labels, values, G_N_ELEMENTS(labels),
3596                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
3597         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3598         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3599         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3600     }
3603     {
3604         /* Force */
3605         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3606         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3607         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3608                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
3609                                                               "tools.tweak", "force", 20,
3610                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3611                                                               1, 100, 1.0, 0.0,
3612                                                               labels, values, G_N_ELEMENTS(labels),
3613                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
3614         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3615         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3616         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3617     }
3619     /* Mode */
3620     {
3621         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3623         GtkTreeIter iter;
3624         gtk_list_store_append( model, &iter );
3625         gtk_list_store_set( model, &iter,
3626                             0, _("Push mode"),
3627                             1, _("Push parts of paths in any direction"),
3628                             2, "tweak_push_mode",
3629                             -1 );
3631         gtk_list_store_append( model, &iter );
3632         gtk_list_store_set( model, &iter,
3633                             0, _("Shrink mode"),
3634                             1, _("Shrink (inset) parts of paths"),
3635                             2, "tweak_shrink_mode",
3636                             -1 );
3638         gtk_list_store_append( model, &iter );
3639         gtk_list_store_set( model, &iter,
3640                             0, _("Grow mode"),
3641                             1, _("Grow (outset) parts of paths"),
3642                             2, "tweak_grow_mode",
3643                             -1 );
3645         gtk_list_store_append( model, &iter );
3646         gtk_list_store_set( model, &iter,
3647                             0, _("Attract mode"),
3648                             1, _("Attract parts of paths towards cursor"),
3649                             2, "tweak_attract_mode",
3650                             -1 );
3652         gtk_list_store_append( model, &iter );
3653         gtk_list_store_set( model, &iter,
3654                             0, _("Repel mode"),
3655                             1, _("Repel parts of paths from cursor"),
3656                             2, "tweak_repel_mode",
3657                             -1 );
3659         gtk_list_store_append( model, &iter );
3660         gtk_list_store_set( model, &iter,
3661                             0, _("Roughen mode"),
3662                             1, _("Roughen parts of paths"),
3663                             2, "tweak_roughen_mode",
3664                             -1 );
3666         gtk_list_store_append( model, &iter );
3667         gtk_list_store_set( model, &iter,
3668                             0, _("Color paint mode"),
3669                             1, _("Paint the tool's color upon selected objects"),
3670                             2, "tweak_colorpaint_mode",
3671                             -1 );
3673         gtk_list_store_append( model, &iter );
3674         gtk_list_store_set( model, &iter,
3675                             0, _("Color jitter mode"),
3676                             1, _("Jitter the colors of selected objects"),
3677                             2, "tweak_colorjitter_mode",
3678                             -1 );
3680         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3681         g_object_set( act, "short_label", _("Mode:"), NULL );
3682         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3683         g_object_set_data( holder, "mode_action", act );
3685         ege_select_one_action_set_appearance( act, "full" );
3686         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3687         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3688         ege_select_one_action_set_icon_column( act, 2 );
3689         ege_select_one_action_set_icon_size( act, secondarySize );
3690         ege_select_one_action_set_tooltip_column( act, 1  );
3692         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3693         ege_select_one_action_set_active( act, mode );
3694         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3696         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3697     }
3699     guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3701     {
3702         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3703         ege_output_action_set_use_markup( act, TRUE );
3704         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3705         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3706             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3707         g_object_set_data( holder, "tweak_channels_label", act);
3708     }
3710     {
3711         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3712                                                       _("Hue"),
3713                                                       _("In color mode, act on objects' hue"),
3714                                                       NULL,
3715                                                       Inkscape::ICON_SIZE_DECORATION );
3716         //TRANSLATORS:  "H" here stands for hue
3717         g_object_set( act, "short_label", _("H"), NULL );
3718         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3719         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3720         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3721         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3722             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3723         g_object_set_data( holder, "tweak_doh", act);
3724     }
3725     {
3726         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3727                                                       _("Saturation"),
3728                                                       _("In color mode, act on objects' saturation"),
3729                                                       NULL,
3730                                                       Inkscape::ICON_SIZE_DECORATION );
3731         //TRANSLATORS: "S" here stands for Saturation
3732         g_object_set( act, "short_label", _("S"), NULL );
3733         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3734         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3735         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3736         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3737             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3738         g_object_set_data( holder, "tweak_dos", act );
3739     }
3740     {
3741         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3742                                                       _("Lightness"),
3743                                                       _("In color mode, act on objects' lightness"),
3744                                                       NULL,
3745                                                       Inkscape::ICON_SIZE_DECORATION );
3746         //TRANSLATORS: "L" here stands for Lightness
3747         g_object_set( act, "short_label", _("L"), NULL );
3748         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3749         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3750         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3751         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3752             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3753         g_object_set_data( holder, "tweak_dol", act );
3754     }
3755     {
3756         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3757                                                       _("Opacity"),
3758                                                       _("In color mode, act on objects' opacity"),
3759                                                       NULL,
3760                                                       Inkscape::ICON_SIZE_DECORATION );
3761         //TRANSLATORS: "O" here stands for Opacity
3762         g_object_set( act, "short_label", _("O"), NULL );
3763         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3764         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3765         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3766         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3767             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3768         g_object_set_data( holder, "tweak_doo", act );
3769     }
3771     {   /* Fidelity */
3772         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3773         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3774         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3775                                                               _("Fidelity"), _("Fidelity:"),
3776                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3777                                                               "tools.tweak", "fidelity", 50,
3778                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3779                                                               1, 100, 1.0, 10.0,
3780                                                               labels, values, G_N_ELEMENTS(labels),
3781                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
3782         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3783         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3784         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3785             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3786         g_object_set_data( holder, "tweak_fidelity", eact );
3787     }
3790     /* Use Pressure button */
3791     {
3792         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3793                                                       _("Pressure"),
3794                                                       _("Use the pressure of the input device to alter the force of tweak action"),
3795                                                       "use_pressure",
3796                                                       Inkscape::ICON_SIZE_DECORATION );
3797         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3798         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3799         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3800     }
3805 //########################
3806 //##     Calligraphy    ##
3807 //########################
3808 static void update_presets_list(GObject *dataKludge ){
3810     if (g_object_get_data(dataKludge, "presets_blocked")) 
3811         return;
3813     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3814     if (sel) {
3815         ege_select_one_action_set_active(sel, 0 );
3816     }
3819 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3821     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value * 0.01 );
3822     update_presets_list(tbl);
3825 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3827     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value * 0.01 );
3828     update_presets_list(tbl);
3831 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3833     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3834     update_presets_list(tbl);
3837 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3839     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3840     update_presets_list(tbl);
3843 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3845     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value * 0.01 );
3846     update_presets_list(tbl);
3849 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3851     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value * 0.01);
3852     update_presets_list(tbl);
3855 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3857     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value * 0.01 );
3858     update_presets_list(tbl);
3861 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3863     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3864     update_presets_list(tbl);
3867 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
3869     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3870     update_presets_list(tbl);
3873 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
3875     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3876     update_presets_list(tbl);
3879 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
3881     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
3882     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3883     update_presets_list(tbl);
3884     if (calligraphy_angle )
3885         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3889 #define PROFILE_FLOAT_SIZE 7
3890 #define PROFILE_INT_SIZE 4
3891 struct ProfileFloatElement {
3892     char const *name;
3893     double def;
3894     double min;
3895     double max;
3896 };
3897 struct ProfileIntElement {
3898     char const *name;
3899     int def;
3900     int min;
3901     int max;
3902 };
3906 static ProfileFloatElement f_profile[PROFILE_FLOAT_SIZE] = {
3907     {"mass",2, 0.0, 100},
3908     {"wiggle",0.0, 0.0, 100},
3909     {"angle",30.0, -90.0, 90.0},
3910     {"thinning",10, -100, 100},
3911     {"tremor",0.0, 0.0, 100},
3912     {"flatness",90, 0.0, 100},
3913     {"cap_rounding",0.0, 0.0, 5.0}
3914 };
3915 static ProfileIntElement i_profile[PROFILE_INT_SIZE] = {
3916     {"width",15, 1, 100},
3917     {"usepressure",1,0,1},
3918     {"tracebackground",0,0,1},
3919     {"usetilt",1,0,1},
3920 };
3924 static void sp_dcc_save_profile( GtkWidget */*widget*/, GObject *dataKludge ){
3925     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3926     if (! desktop) return;
3928     if (g_object_get_data(dataKludge, "presets_blocked")) 
3929         return;
3931     Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
3932     if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) return;
3933     Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
3935     unsigned int new_index = pref_path_number_of_children("tools.calligraphic.preset") +1;
3936     gchar *profile_id = g_strdup_printf("dcc%d", new_index);
3937     gchar *pref_path = create_pref("tools.calligraphic.preset",profile_id);
3939     for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3940         ProfileFloatElement const &pe = f_profile[i];
3941         double v = prefs_get_double_attribute_limited("tools.calligraphic",pe.name, pe.def, pe.min, pe.max);
3942         prefs_set_double_attribute(pref_path,pe.name,v);
3943     }
3944     for (unsigned i = 0; i < PROFILE_INT_SIZE; ++i) {
3945         ProfileIntElement const &pe = i_profile[i];
3946         int v = prefs_get_int_attribute_limited("tools.calligraphic",pe.name, pe.def,pe.min, pe.max);
3947         prefs_set_int_attribute(pref_path,pe.name,v);
3948     }
3949     prefs_set_string_attribute(pref_path,"name",profile_name.c_str());
3951     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3952     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
3953     GtkTreeIter iter;
3954     gtk_list_store_append( model, &iter );
3955     gtk_list_store_set( model, &iter, 0, profile_name.c_str(), 1, new_index, -1 );
3957     free(profile_id);
3958     free(pref_path);
3960     ege_select_one_action_set_active(selector, new_index);
3964 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
3966     gint preset_index = ege_select_one_action_get_active( act );
3967     gchar *preset_path = get_pref_nth_child("tools.calligraphic.preset", preset_index);
3969     if (preset_path) {
3970         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
3972         Inkscape::XML::Node *preset_repr = inkscape_get_repr(INKSCAPE, preset_path);
3974         for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
3975               iter; 
3976               ++iter ) {
3977             const gchar *attr_name = g_quark_to_string(iter->key);
3978             if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
3979                 continue;
3980             void *widget = g_object_get_data(tbl, attr_name);
3981             if (widget) {
3982                 if (GTK_IS_ADJUSTMENT(widget)) {
3983                     double v = prefs_get_double_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
3984                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
3985                     gtk_adjustment_set_value(adj, v);
3986                     //std::cout << "set adj " << attr_name << " to " << v << "\n";
3987                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
3988                     int v = prefs_get_int_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
3989                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
3990                     gtk_toggle_action_set_active(toggle, v);
3991                     //std::cout << "set toggle " << attr_name << " to " << v << "\n";
3992                 } else {
3993                     g_warning("Unknown widget type for preset: %s\n", attr_name);
3994                 }
3995             } else {
3996                 g_warning("Bad key found in preset: %s\n", attr_name);
3997             }
3998         }
3999         free(preset_path);
4000         g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4001     }
4006 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4008     {
4009         EgeAdjustmentAction* calligraphy_angle = 0;
4011         {
4012         /* Width */
4013         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4014         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4015         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4016                                                               _("Pen Width"), _("Width:"),
4017                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
4018                                                               "tools.calligraphic", "width", 15,
4019                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4020                                                               1, 100, 1.0, 0.0,
4021                                                               labels, values, G_N_ELEMENTS(labels),
4022                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
4023         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4024         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4025         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4026         }
4028         {
4029         /* Thinning */
4030             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4031             gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4032         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4033                                                               _("Stroke Thinning"), _("Thinning:"),
4034                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4035                                                               "tools.calligraphic", "thinning", 10,
4036                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4037                                                               -100, 100, 1, 0.1,
4038                                                               labels, values, G_N_ELEMENTS(labels),
4039                                                               sp_ddc_velthin_value_changed, 0.01, 0, 100);
4040         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4041         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4042         }
4044         {
4045         /* Angle */
4046         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4047         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4048         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4049                                                               _("Pen Angle"), _("Angle:"),
4050                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4051                                                               "tools.calligraphic", "angle", 30,
4052                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4053                                                               -90.0, 90.0, 1.0, 10.0,
4054                                                               labels, values, G_N_ELEMENTS(labels),
4055                                                               sp_ddc_angle_value_changed, 1, 0 );
4056         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4057         g_object_set_data( holder, "angle_action", eact );
4058         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4059         calligraphy_angle = eact;
4060         }
4062         {
4063         /* Fixation */
4064             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4065         gdouble values[] = {0, 20, 40, 60, 90, 100};
4066         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4067                                                               _("Fixation"), _("Fixation:"),
4068                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
4069                                                               "tools.calligraphic", "flatness", 90,
4070                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4071                                                               0.0, 100, 1.0, 10.0,
4072                                                               labels, values, G_N_ELEMENTS(labels),
4073                                                               sp_ddc_flatness_value_changed, 0.01, 0, 100 );
4074         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4075         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4076         }
4078         {
4079         /* Cap Rounding */
4080             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4081         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4082         // TRANSLATORS: "cap" means "end" (both start and finish) here
4083         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4084                                                               _("Cap rounding"), _("Caps:"),
4085                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4086                                                               "tools.calligraphic", "cap_rounding", 0.0,
4087                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4088                                                               0.0, 5.0, 0.01, 0.1,
4089                                                               labels, values, G_N_ELEMENTS(labels),
4090                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4091         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4092         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4093         }
4095         {
4096         /* Tremor */
4097             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4098         gdouble values[] = {0, 10, 20, 40, 60, 100};
4099         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4100                                                               _("Stroke Tremor"), _("Tremor:"),
4101                                                               _("Increase to make strokes rugged and trembling"),
4102                                                               "tools.calligraphic", "tremor", 0.0,
4103                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4104                                                               0.0, 100, 1, 0.0,
4105                                                               labels, values, G_N_ELEMENTS(labels),
4106                                                               sp_ddc_tremor_value_changed, 0.01, 0, 100 );
4108         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4109         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4110         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4111         }
4113         {
4114         /* Wiggle */
4115         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4116         gdouble values[] = {0, 20, 40, 60, 100};
4117         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4118                                                               _("Pen Wiggle"), _("Wiggle:"),
4119                                                               _("Increase to make the pen waver and wiggle"),
4120                                                               "tools.calligraphic", "wiggle", 0.0,
4121                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4122                                                               0.0, 100, 1, 0.0,
4123                                                               labels, values, G_N_ELEMENTS(labels),
4124                                                               sp_ddc_wiggle_value_changed, 0.01, 0, 100 );
4125         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4126         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4127         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4128         }
4130         {
4131         /* Mass */
4132             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4133         gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4134         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4135                                                               _("Pen Mass"), _("Mass:"),
4136                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
4137                                                               "tools.calligraphic", "mass", 2.0,
4138                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4139                                                               0.0, 100, 1, 0.0,
4140                                                               labels, values, G_N_ELEMENTS(labels),
4141                                                               sp_ddc_mass_value_changed, 0.01, 0, 100 );
4142         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4143         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4144         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4145         }
4148         /* Trace Background button */
4149         {
4150             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4151                                                           _("Trace Background"),
4152                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4153                                                           "trace_background",
4154                                                           Inkscape::ICON_SIZE_DECORATION );
4155             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4156             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4157             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
4158             g_object_set_data( holder, "tracebackground", act );
4159         }
4161         /* Use Pressure button */
4162         {
4163             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4164                                                           _("Pressure"),
4165                                                           _("Use the pressure of the input device to alter the width of the pen"),
4166                                                           "use_pressure",
4167                                                           Inkscape::ICON_SIZE_DECORATION );
4168             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4169             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4170             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
4171             g_object_set_data( holder, "usepressure", act );
4172         }
4174         /* Use Tilt button */
4175         {
4176             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4177                                                           _("Tilt"),
4178                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
4179                                                           "use_tilt",
4180                                                           Inkscape::ICON_SIZE_DECORATION );
4181             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4182             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4183             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4184             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4185             g_object_set_data( holder, "usetilt", act );
4186         }
4188         /*calligraphic profile */
4189         {
4190             GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
4191             gchar *pref_path;
4194             GtkTreeIter iter;
4195             gtk_list_store_append( model, &iter );
4196             gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4198             //TODO: switch back to prefs API
4199             Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
4200             Inkscape::XML::Node *child_repr = sp_repr_children(repr);
4201             int ii=1;
4202             while (child_repr) {
4203                 GtkTreeIter iter;
4204                 char *preset_name = (char *) child_repr->attribute("name");
4205                 gtk_list_store_append( model, &iter );
4206                 gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );
4207                 child_repr = sp_repr_next(child_repr);
4208             }
4210             pref_path = NULL;
4211             EgeSelectOneAction* act1 = ege_select_one_action_new( "SetProfileAction", "" , (_("Change calligraphic profile")), NULL, GTK_TREE_MODEL(model) );
4212             ege_select_one_action_set_appearance( act1, "compact" );
4213             g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder );
4214             gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
4215             g_object_set_data( holder, "profile_selector", act1 );
4217         }
4219         /*Save or delete calligraphic profile */
4220         {
4221             GtkAction* act = gtk_action_new( "SaveDeleteProfileAction",
4222                                              _("Defaults"),
4223                                              _("Save current settings as new profile"),
4224                                              GTK_STOCK_SAVE );
4225             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_dcc_save_profile), holder );
4228             gtk_action_group_add_action( mainActions, act );
4229             gtk_action_set_sensitive( act, TRUE );
4230             g_object_set_data( holder, "profile_save_delete", act );
4231         }
4232     }
4236 //########################
4237 //##    Circle / Arc    ##
4238 //########################
4240 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4242     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4243     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4245     if (v1 == 0 && v2 == 0) {
4246         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4247             gtk_action_set_sensitive( ocb, FALSE );
4248             gtk_action_set_sensitive( make_whole, FALSE );
4249         }
4250     } else {
4251         gtk_action_set_sensitive( ocb, TRUE );
4252         gtk_action_set_sensitive( make_whole, TRUE );
4253     }
4256 static void
4257 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4259     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4261     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4262         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
4263     }
4265     // quit if run by the attr_changed listener
4266     if (g_object_get_data( tbl, "freeze" )) {
4267         return;
4268     }
4270     // in turn, prevent listener from responding
4271     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4273     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4275     bool modmade = false;
4276     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4277          items != NULL;
4278          items = items->next)
4279     {
4280         SPItem *item = SP_ITEM(items->data);
4282         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4284             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4285             SPArc *arc = SP_ARC(item);
4287             if (!strcmp(value_name, "start"))
4288                 ge->start = (adj->value * M_PI)/ 180;
4289             else
4290                 ge->end = (adj->value * M_PI)/ 180;
4292             sp_genericellipse_normalize(ge);
4293             ((SPObject *)arc)->updateRepr();
4294             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4296             modmade = true;
4297         }
4298     }
4300     g_free(namespaced_name);
4302     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4304     sp_arctb_sensitivize( tbl, adj->value, other->value );
4306     if (modmade) {
4307         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4308                                    _("Arc: Change start/end"));
4309     }
4311     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4315 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
4317     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
4320 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4322     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
4326 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4328     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4329     gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
4330     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4331         prefs_set_int_attribute( "tools.eraser", "mode", eraserMode );
4332     }
4334     // only take action if run by the attr_changed listener
4335     if (!g_object_get_data( tbl, "freeze" )) {
4336         // in turn, prevent listener from responding
4337         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4339         if ( eraserMode != 0 ) {
4340         } else {
4341         }
4342         // TODO finish implementation
4344         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4345     }
4348 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4350     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4351     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4352         if ( ege_select_one_action_get_active( act ) != 0 ) {
4353             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
4354         } else {
4355             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
4356         }
4357     }
4359     // quit if run by the attr_changed listener
4360     if (g_object_get_data( tbl, "freeze" )) {
4361         return;
4362     }
4364     // in turn, prevent listener from responding
4365     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4367     bool modmade = false;
4369     if ( ege_select_one_action_get_active(act) != 0 ) {
4370         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4371              items != NULL;
4372              items = items->next)
4373         {
4374             if (SP_IS_ARC((SPItem *) items->data)) {
4375                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4376                 repr->setAttribute("sodipodi:open", "true");
4377                 SP_OBJECT((SPItem *) items->data)->updateRepr();
4378                 modmade = true;
4379             }
4380         }
4381     } else {
4382         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4383              items != NULL;
4384              items = items->next)
4385         {
4386             if (SP_IS_ARC((SPItem *) items->data))    {
4387                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4388                 repr->setAttribute("sodipodi:open", NULL);
4389                 SP_OBJECT((SPItem *) items->data)->updateRepr();
4390                 modmade = true;
4391             }
4392         }
4393     }
4395     if (modmade) {
4396         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4397                                    _("Arc: Change open/closed"));
4398     }
4400     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4403 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4405     GtkAdjustment *adj;
4406     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
4407     gtk_adjustment_set_value(adj, 0.0);
4408     gtk_adjustment_value_changed(adj);
4410     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
4411     gtk_adjustment_set_value(adj, 0.0);
4412     gtk_adjustment_value_changed(adj);
4414     spinbutton_defocus( GTK_OBJECT(obj) );
4417 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
4418                                       gchar const */*old_value*/, gchar const */*new_value*/,
4419                                       bool /*is_interactive*/, gpointer data)
4421     GObject *tbl = G_OBJECT(data);
4423     // quit if run by the _changed callbacks
4424     if (g_object_get_data( tbl, "freeze" )) {
4425         return;
4426     }
4428     // in turn, prevent callbacks from responding
4429     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4431     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4432     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4434     GtkAdjustment *adj1,*adj2;
4435     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4436     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4437     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4438     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4440     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4442     char const *openstr = NULL;
4443     openstr = repr->attribute("sodipodi:open");
4444     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4446     if (openstr) {
4447         ege_select_one_action_set_active( ocb, 1 );
4448     } else {
4449         ege_select_one_action_set_active( ocb, 0 );
4450     }
4452     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4455 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4456     NULL, /* child_added */
4457     NULL, /* child_removed */
4458     arc_tb_event_attr_changed,
4459     NULL, /* content_changed */
4460     NULL  /* order_changed */
4461 };
4464 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4466     int n_selected = 0;
4467     Inkscape::XML::Node *repr = NULL;
4469     purge_repr_listener( tbl, tbl );
4471     for (GSList const *items = selection->itemList();
4472          items != NULL;
4473          items = items->next)
4474     {
4475         if (SP_IS_ARC((SPItem *) items->data)) {
4476             n_selected++;
4477             repr = SP_OBJECT_REPR((SPItem *) items->data);
4478         }
4479     }
4481     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4483     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4484     if (n_selected == 0) {
4485         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4486     } else if (n_selected == 1) {
4487         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4488         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4490         if (repr) {
4491             g_object_set_data( tbl, "repr", repr );
4492             Inkscape::GC::anchor(repr);
4493             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4494             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4495         }
4496     } else {
4497         // FIXME: implement averaging of all parameters for multiple selected
4498         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4499         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4500         sp_arctb_sensitivize( tbl, 1, 0 );
4501     }
4505 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4507     EgeAdjustmentAction* eact = 0;
4508     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
4511     {
4512         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4513         ege_output_action_set_use_markup( act, TRUE );
4514         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4515         g_object_set_data( holder, "mode_action", act );
4516     }
4518     /* Start */
4519     {
4520         eact = create_adjustment_action( "ArcStartAction",
4521                                          _("Start"), _("Start:"),
4522                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
4523                                          "tools.shapes.arc", "start", 0.0,
4524                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4525                                          -360.0, 360.0, 1.0, 10.0,
4526                                          0, 0, 0,
4527                                          sp_arctb_start_value_changed);
4528         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4529     }
4531     /* End */
4532     {
4533         eact = create_adjustment_action( "ArcEndAction",
4534                                          _("End"), _("End:"),
4535                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
4536                                          "tools.shapes.arc", "end", 0.0,
4537                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4538                                          -360.0, 360.0, 1.0, 10.0,
4539                                          0, 0, 0,
4540                                          sp_arctb_end_value_changed);
4541         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4542     }
4544     /* Segments / Pie checkbox */
4545     {
4546         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4548         GtkTreeIter iter;
4549         gtk_list_store_append( model, &iter );
4550         gtk_list_store_set( model, &iter,
4551                             0, _("Closed arc"),
4552                             1, _("Switch to segment (closed shape with two radii)"),
4553                             2, "circle_closed_arc",
4554                             -1 );
4556         gtk_list_store_append( model, &iter );
4557         gtk_list_store_set( model, &iter,
4558                             0, _("Open Arc"),
4559                             1, _("Switch to arc (unclosed shape)"),
4560                             2, "circle_open_arc",
4561                             -1 );
4563         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4564         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4565         g_object_set_data( holder, "open_action", act );
4567         ege_select_one_action_set_appearance( act, "full" );
4568         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4569         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4570         ege_select_one_action_set_icon_column( act, 2 );
4571         ege_select_one_action_set_icon_size( act, secondarySize );
4572         ege_select_one_action_set_tooltip_column( act, 1  );
4574         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4575         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4576         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4577         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4578     }
4580     /* Make Whole */
4581     {
4582         InkAction* inky = ink_action_new( "ArcResetAction",
4583                                           _("Make whole"),
4584                                           _("Make the shape a whole ellipse, not arc or segment"),
4585                                           "reset_circle",
4586                                           secondarySize );
4587         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4588         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4589         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4590         g_object_set_data( holder, "make_whole", inky );
4591     }
4593     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4594     // sensitivize make whole and open checkbox
4595     {
4596         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4597         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4598         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4599     }
4602     sigc::connection *connection = new sigc::connection(
4603         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4604         );
4605     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4606     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4612 // toggle button callbacks and updaters
4614 //########################
4615 //##      Dropper       ##
4616 //########################
4618 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4619     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4620     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4621     if ( set_action ) {
4622         if ( gtk_toggle_action_get_active( act ) ) {
4623             gtk_action_set_sensitive( set_action, TRUE );
4624         } else {
4625             gtk_action_set_sensitive( set_action, FALSE );
4626         }
4627     }
4629     spinbutton_defocus(GTK_OBJECT(tbl));
4632 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4633     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4634     spinbutton_defocus(GTK_OBJECT(tbl));
4638 /**
4639  * Dropper auxiliary toolbar construction and setup.
4640  *
4641  * TODO: Would like to add swatch of current color.
4642  * TODO: Add queue of last 5 or so colors selected with new swatches so that
4643  *       can drag and drop places. Will provide a nice mixing palette.
4644  */
4645 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4647     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4649     {
4650         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4651         ege_output_action_set_use_markup( act, TRUE );
4652         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4653     }
4655     {
4656         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4657                                                       _("Pick opacity"),
4658                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4659                                                       NULL,
4660                                                       Inkscape::ICON_SIZE_DECORATION );
4661         g_object_set( act, "short_label", _("Pick"), NULL );
4662         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4663         g_object_set_data( holder, "pick_action", act );
4664         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4665         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4666     }
4668     {
4669         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4670                                                       _("Assign opacity"),
4671                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4672                                                       NULL,
4673                                                       Inkscape::ICON_SIZE_DECORATION );
4674         g_object_set( act, "short_label", _("Assign"), NULL );
4675         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4676         g_object_set_data( holder, "set_action", act );
4677         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4678         // make sure it's disabled if we're not picking alpha
4679         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4680         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4681     }
4686 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4688     {
4689         /* Width */
4690         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4691         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4692         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
4693                                                               _("Pen Width"), _("Width:"),
4694                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
4695                                                               "tools.eraser", "width", 15,
4696                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
4697                                                               1, 100, 1.0, 0.0,
4698                                                               labels, values, G_N_ELEMENTS(labels),
4699                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
4700         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4701         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4702         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4703     }
4705     {
4706         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4708         GtkTreeIter iter;
4709         gtk_list_store_append( model, &iter );
4710         gtk_list_store_set( model, &iter,
4711                             0, _("Delete"),
4712                             1, _("Delete objects touched by the eraser"),
4713                             2, "delete_object",
4714                             -1 );
4716         gtk_list_store_append( model, &iter );
4717         gtk_list_store_set( model, &iter,
4718                             0, _("Cut"),
4719                             1, _("Cut out from objects"),
4720                             2, "difference",
4721                             -1 );
4723         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4724         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4725         g_object_set_data( holder, "eraser_mode_action", act );
4727         ege_select_one_action_set_appearance( act, "full" );
4728         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4729         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4730         ege_select_one_action_set_icon_column( act, 2 );
4731         ege_select_one_action_set_tooltip_column( act, 1  );
4733         gint eraserMode = (prefs_get_int_attribute("tools.eraser", "mode", 0) != 0) ? 1 : 0;
4734         ege_select_one_action_set_active( act, eraserMode );
4735         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
4736     }
4740 //########################
4741 //##    Text Toolbox    ##
4742 //########################
4743 /*
4744 static void
4745 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
4747     //Call back for letter sizing spinbutton
4750 static void
4751 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
4753     //Call back for line height spinbutton
4756 static void
4757 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4759     //Call back for horizontal kerning spinbutton
4762 static void
4763 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4765     //Call back for vertical kerning spinbutton
4768 static void
4769 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
4771     //Call back for letter rotation spinbutton
4772 }*/
4774 namespace {
4776 bool popdown_visible = false;
4777 bool popdown_hasfocus = false;
4779 void
4780 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
4782     SPStyle *query =
4783         sp_style_new (SP_ACTIVE_DOCUMENT);
4785 //    int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4787     int result_family =
4788         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4790     int result_style =
4791         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4793     int result_numbers =
4794         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4796     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4798     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4799     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
4800     {
4801         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
4803         if (repr)
4804         {
4805             sp_style_read_from_repr (query, repr);
4806         }
4807         else
4808         {
4809             return;
4810         }
4811     }
4813     if (query->text)
4814     {
4815         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4816             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4817             gtk_entry_set_text (GTK_ENTRY (entry), "");
4819         } else if (query->text->font_specification.value || query->text->font_family.value) {
4821             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4823             // Get the font that corresponds
4824             Glib::ustring familyName;
4826             font_instance * font = font_factory::Default()->FaceFromStyle(query);
4827             if (font) {
4828                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4829                 font->Unref();
4830                 font = NULL;
4831             }
4833             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4835             Gtk::TreePath path;
4836             try {
4837                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4838             } catch (...) {
4839                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4840                 return;
4841             }
4843             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4844             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4846             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4848             gtk_tree_selection_select_path (tselection, path.gobj());
4849             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4851             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4852         }
4854         //Size
4855         GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4856         char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4857         g_object_set_data (tbl, "size-block", gpointer(1));
4858         gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4859         g_object_set_data (tbl, "size-block", gpointer(0));
4860         free (str);
4862         //Anchor
4863         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4864         {
4865             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4866             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4867             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4868             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4869         }
4870         else
4871         {
4872             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4873             {
4874                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4875                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4876                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4877                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4878             }
4879             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4880             {
4881                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4882                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4883                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4884                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4885             }
4886             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4887             {
4888                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4889                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4890                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4891                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4892             }
4893         }
4895         //Style
4896         {
4897             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4899             gboolean active = gtk_toggle_button_get_active (button);
4900             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4902             if (active != check)
4903             {
4904                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4905                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4906                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4907             }
4908         }
4910         {
4911             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4913             gboolean active = gtk_toggle_button_get_active (button);
4914             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4916             if (active != check)
4917             {
4918                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4919                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4920                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4921             }
4922         }
4924         //Orientation
4925         //locking both buttons, changing one affect all group (both)
4926         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4927         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4929         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4930         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4932         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4933         {
4934             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4935         }
4936         else
4937         {
4938             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4939         }
4940         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4941         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4942     }
4944     sp_style_unref(query);
4947 void
4948 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4950     sp_text_toolbox_selection_changed (selection, tbl);
4953 void
4954 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4956     sp_text_toolbox_selection_changed (NULL, tbl);
4959 void
4960 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
4961                                 GObject             *tbl)
4963     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
4964     GtkTreeModel *model = 0;
4965     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4966     GtkTreeIter   iter;
4967     char         *family = 0;
4969     gdk_pointer_ungrab (GDK_CURRENT_TIME);
4970     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4972     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4973         return;
4974     }
4976     gtk_tree_model_get (model, &iter, 0, &family, -1);
4978     if (g_object_get_data (G_OBJECT (selection), "block"))
4979     {
4980         gtk_entry_set_text (GTK_ENTRY (entry), family);
4981         return;
4982     }
4984     gtk_entry_set_text (GTK_ENTRY (entry), family);
4986     SPStyle *query =
4987         sp_style_new (SP_ACTIVE_DOCUMENT);
4989     int result_fontspec =
4990         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4992     //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4994     SPCSSAttr *css = sp_repr_css_attr_new ();
4997     // First try to get the font spec from the stored value
4998     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
5000     if (fontSpec.empty()) {
5001         // Construct a new font specification if it does not yet exist
5002         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5003         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5004         fontFromStyle->Unref();
5005     }
5007     if (!fontSpec.empty()) {
5008         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
5009         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
5010             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
5011             if (font) {
5012                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5014                 // Set all the these just in case they were altered when finding the best
5015                 // match for the new family and old style...
5017                 gchar c[256];
5019                 font->Family(c, 256);
5020                 sp_repr_css_set_property (css, "font-family", c);
5022                 font->Attribute( "weight", c, 256);
5023                 sp_repr_css_set_property (css, "font-weight", c);
5025                 font->Attribute("style", c, 256);
5026                 sp_repr_css_set_property (css, "font-style", c);
5028                 font->Attribute("stretch", c, 256);
5029                 sp_repr_css_set_property (css, "font-stretch", c);
5031                 font->Attribute("variant", c, 256);
5032                 sp_repr_css_set_property (css, "font-variant", c);
5034                 font->Unref();
5035             }
5036         }
5037     }
5039     // If querying returned nothing, set the default style of the tool (for new texts)
5040     if (result_fontspec == QUERY_STYLE_NOTHING)
5041     {
5042         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5043         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
5044     }
5045     else
5046     {
5047         sp_desktop_set_style (desktop, css, true, true);
5048     }
5050     sp_style_unref(query);
5052     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5053                                    _("Text: Change font family"));
5054     sp_repr_css_attr_unref (css);
5055     free (family);
5056     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5058     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5061 void
5062 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
5063                                        GObject      *tbl)
5065     const char *family = gtk_entry_get_text (entry);
5067     try {
5068         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
5069         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5070         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5071         gtk_tree_selection_select_path (selection, path.gobj());
5072         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5073         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5074     } catch (...) {
5075         if (family && strlen (family))
5076         {
5077             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5078         }
5079     }
5082 void
5083 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
5084                                    gpointer          data)
5086     if (g_object_get_data (G_OBJECT (button), "block")) return;
5087     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
5088     int prop = GPOINTER_TO_INT(data);
5090     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5091     SPCSSAttr *css = sp_repr_css_attr_new ();
5093     switch (prop)
5094     {
5095         case 0:
5096         {
5097             sp_repr_css_set_property (css, "text-anchor", "start");
5098             sp_repr_css_set_property (css, "text-align", "start");
5099             break;
5100         }
5101         case 1:
5102         {
5103             sp_repr_css_set_property (css, "text-anchor", "middle");
5104             sp_repr_css_set_property (css, "text-align", "center");
5105             break;
5106         }
5108         case 2:
5109         {
5110             sp_repr_css_set_property (css, "text-anchor", "end");
5111             sp_repr_css_set_property (css, "text-align", "end");
5112             break;
5113         }
5115         case 3:
5116         {
5117             sp_repr_css_set_property (css, "text-anchor", "start");
5118             sp_repr_css_set_property (css, "text-align", "justify");
5119             break;
5120         }
5121     }
5123     SPStyle *query =
5124         sp_style_new (SP_ACTIVE_DOCUMENT);
5125     int result_numbers =
5126         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5128     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5129     if (result_numbers == QUERY_STYLE_NOTHING)
5130     {
5131         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5132     }
5134     sp_style_unref(query);
5136     sp_desktop_set_style (desktop, css, true, true);
5137     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5138                                    _("Text: Change alignment"));
5139     sp_repr_css_attr_unref (css);
5141     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5144 void
5145 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
5146                                gpointer          data)
5148     if (g_object_get_data (G_OBJECT (button), "block")) return;
5150     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5151     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5152     int          prop       = GPOINTER_TO_INT(data);
5153     bool         active     = gtk_toggle_button_get_active (button);
5155     SPStyle *query =
5156         sp_style_new (SP_ACTIVE_DOCUMENT);
5158     int result_fontspec =
5159         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5161     //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5162     //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5163     //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5165     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
5166     Glib::ustring newFontSpec = "";
5168     if (fontSpec.empty()) {
5169         // Construct a new font specification if it does not yet exist
5170         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5171         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5172         fontFromStyle->Unref();
5173     }
5175     switch (prop)
5176     {
5177         case 0:
5178         {
5179             if (!fontSpec.empty()) {
5180                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
5181             }
5182             if (fontSpec != newFontSpec) {
5183                 // Don't even set the bold if the font didn't exist on the system
5184                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
5185             }
5186             break;
5187         }
5189         case 1:
5190         {
5191             if (!fontSpec.empty()) {
5192                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
5193             }
5194             if (fontSpec != newFontSpec) {
5195                 // Don't even set the italic if the font didn't exist on the system
5196                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
5197             }
5198             break;
5199         }
5200     }
5202     if (!newFontSpec.empty()) {
5203         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5204     }
5206     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5207     if (result_fontspec == QUERY_STYLE_NOTHING)
5208     {
5209         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5210     }
5212     sp_style_unref(query);
5214     sp_desktop_set_style (desktop, css, true, true);
5215     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5216                                    _("Text: Change font style"));
5217     sp_repr_css_attr_unref (css);
5219     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5222 void
5223 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
5224                                      gpointer         data)
5226     if (g_object_get_data (G_OBJECT (button), "block")) {
5227         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5228         return;
5229     }
5231     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5232     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5233     int          prop       = GPOINTER_TO_INT(data);
5235     switch (prop)
5236     {
5237         case 0:
5238         {
5239             sp_repr_css_set_property (css, "writing-mode", "lr");
5240             break;
5241         }
5243         case 1:
5244         {
5245             sp_repr_css_set_property (css, "writing-mode", "tb");
5246             break;
5247         }
5248     }
5250     SPStyle *query =
5251         sp_style_new (SP_ACTIVE_DOCUMENT);
5252     int result_numbers =
5253         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5255     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5256     if (result_numbers == QUERY_STYLE_NOTHING)
5257     {
5258         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5259     }
5261     sp_desktop_set_style (desktop, css, true, true);
5262     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5263                                    _("Text: Change orientation"));
5264     sp_repr_css_attr_unref (css);
5266     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5269 gboolean
5270 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5272     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5273     if (!desktop) return FALSE;
5275     switch (get_group0_keyval (event)) {
5276         case GDK_Escape: // defocus
5277             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5278             sp_text_toolbox_selection_changed (NULL, tbl); // update
5279             return TRUE; // I consumed the event
5280             break;
5281     }
5282     return FALSE;
5285 gboolean
5286 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5288     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5289     if (!desktop) return FALSE;
5291     switch (get_group0_keyval (event)) {
5292         case GDK_KP_Enter:
5293         case GDK_Return:
5294         case GDK_Escape: // defocus
5295             gtk_widget_hide (w);
5296             popdown_visible = false;
5297             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5298             return TRUE; // I consumed the event
5299             break;
5300         case GDK_w:
5301         case GDK_W:
5302             if (event->state & GDK_CONTROL_MASK) {
5303                 gtk_widget_hide (w);
5304                 popdown_visible = false;
5305                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5306                 return TRUE; // I consumed the event
5307             }
5308             break;
5309     }
5310     return FALSE;
5314 void
5315 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
5316                                GObject     *tbl)
5318     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5320     if (g_object_get_data (tbl, "size-block")) return;
5322     // If this is not from selecting a size in the list (in which case get_active will give the
5323     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5324     // process this event. This fixes GTK's stupid insistence on sending an activate change every
5325     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5326     if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5327         return;
5329     gchar *endptr;
5330     gdouble value = -1;
5331     char *text = gtk_combo_box_get_active_text (cbox);
5332     if (text) {
5333         value = g_strtod (text, &endptr);
5334         if (endptr == text) // conversion failed, non-numeric input
5335             value = -1;
5336         free (text);
5337     }
5338     if (value <= 0) {
5339         return; // could not parse value
5340     }
5342     SPCSSAttr *css = sp_repr_css_attr_new ();
5343     Inkscape::CSSOStringStream osfs;
5344     osfs << value;
5345     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5347     SPStyle *query =
5348         sp_style_new (SP_ACTIVE_DOCUMENT);
5349     int result_numbers =
5350         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5352     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5353     if (result_numbers == QUERY_STYLE_NOTHING)
5354     {
5355         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5356     }
5358     sp_style_unref(query);
5360     sp_desktop_set_style (desktop, css, true, true);
5361     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5362                                    _("Text: Change font size"));
5363     sp_repr_css_attr_unref (css);
5365     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5368 gboolean
5369 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5371     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5372     if (!desktop) return FALSE;
5374     if (!g_object_get_data (tbl, "esc-pressed")) {
5375         g_object_set_data (tbl, "enter-pressed", gpointer(1));
5376         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5377         sp_text_toolbox_size_changed (cbox, tbl);
5378         g_object_set_data (tbl, "enter-pressed", gpointer(0));
5379     }
5380     return FALSE; // I consumed the event
5384 gboolean
5385 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5387     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5388     if (!desktop) return FALSE;
5390     switch (get_group0_keyval (event)) {
5391         case GDK_Escape: // defocus
5392             g_object_set_data (tbl, "esc-pressed", gpointer(1));
5393             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5394             g_object_set_data (tbl, "esc-pressed", gpointer(0));
5395             return TRUE; // I consumed the event
5396             break;
5397         case GDK_Return: // defocus
5398         case GDK_KP_Enter:
5399             g_object_set_data (tbl, "enter-pressed", gpointer(1));
5400             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5401             sp_text_toolbox_size_changed (cbox, tbl);
5402             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5403             g_object_set_data (tbl, "enter-pressed", gpointer(0));
5404             return TRUE; // I consumed the event
5405             break;
5406     }
5407     return FALSE;
5410 void
5411 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
5412                                          GObject            *tbl)
5414     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
5415     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5416     int x, y;
5418     if (!popdown_visible)
5419     {
5420         gdk_window_get_origin (widget->window, &x, &y);
5421         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
5422         gtk_widget_show_all (popdown);
5423         //sp_transientize (popdown);
5425         gdk_pointer_grab (widget->window, TRUE,
5426                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
5427                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
5428                                         GDK_POINTER_MOTION_MASK),
5429                           NULL, NULL, GDK_CURRENT_TIME);
5431         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
5433         popdown_visible = true;
5434     }
5435     else
5436     {
5437         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5438         gdk_pointer_ungrab (GDK_CURRENT_TIME);
5439         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5440         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5441         gtk_widget_hide (popdown);
5442         popdown_visible = false;
5443     }
5446 gboolean
5447 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
5448                                  GdkEventFocus    */*event*/,
5449                                  GObject          */*tbl*/)
5451     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
5452     return FALSE;
5455 gboolean
5456 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
5457                                    GdkEventFocus    */*event*/,
5458                                    GObject          */*tbl*/)
5460     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5462     if (popdown_hasfocus) {
5463         gtk_widget_hide (popdown);
5464         popdown_hasfocus = false;
5465         popdown_visible = false;
5466         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5467         return TRUE;
5468     }
5469     return FALSE;
5472 gboolean
5473 sp_text_toolbox_popdown_focus_in (GtkWidget        */*popdown*/,
5474                                    GdkEventFocus    */*event*/,
5475                                    GObject          */*tbl*/)
5477     popdown_hasfocus = true;
5478     return TRUE;
5482 void
5483 cell_data_func  (GtkTreeViewColumn */*column*/,
5484                  GtkCellRenderer   *cell,
5485                  GtkTreeModel      *tree_model,
5486                  GtkTreeIter       *iter,
5487                  gpointer           /*data*/)
5489     char        *family,
5490         *family_escaped,
5491         *sample_escaped;
5493     static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
5495     gtk_tree_model_get (tree_model, iter, 0, &family, -1);
5497     family_escaped = g_markup_escape_text (family, -1);
5498     sample_escaped = g_markup_escape_text (sample, -1);
5500     std::stringstream markup;
5501     markup << family_escaped << "  <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
5502     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
5504     free (family);
5505     free (family_escaped);
5506     free (sample_escaped);
5509 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
5510     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
5511     if (completion) {
5512         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
5513         g_object_unref (completion);
5514     }
5517 GtkWidget*
5518 sp_text_toolbox_new (SPDesktop *desktop)
5520     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
5521     GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
5523     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
5524     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
5526     GtkTooltips *tt = gtk_tooltips_new();
5527     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
5529     ////////////Family
5530     //Window
5531     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5532     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
5534     //Entry
5535     GtkWidget           *entry = gtk_entry_new ();
5536     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
5537     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
5538     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
5539     gtk_entry_completion_set_text_column (completion, 0);
5540     gtk_entry_completion_set_minimum_key_length (completion, 1);
5541     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
5542     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
5543     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
5544     gtk_toolbar_append_widget( tbl, entry, "", "" );
5545     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
5547     //Button
5548     GtkWidget   *button = gtk_button_new ();
5549     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
5550     gtk_toolbar_append_widget( tbl, button, "", "");
5552     //Popdown
5553     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
5554     GtkWidget           *treeview = gtk_tree_view_new ();
5556     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
5557     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
5558     gtk_tree_view_column_pack_start (column, cell, FALSE);
5559     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
5560     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5561     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5563     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5564     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5565     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5567     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5569     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5570     gtk_container_add (GTK_CONTAINER (sw), treeview);
5572     gtk_container_add (GTK_CONTAINER (window), sw);
5573     gtk_widget_set_size_request (window, 300, 450);
5575     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5576     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5577     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5579     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5581     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5582     g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5583     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5585     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5586     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5588     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5589     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5590     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5591     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5592     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5594     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
5595     GtkWidget *box = gtk_event_box_new ();
5596     gtk_container_add (GTK_CONTAINER (box), image);
5597     gtk_toolbar_append_widget( tbl, box, "", "");
5598     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5599     GtkTooltips *tooltips = gtk_tooltips_new ();
5600     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5601     gtk_widget_hide (GTK_WIDGET (box));
5602     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
5604     ////////////Size
5605     const char *sizes[] = {
5606         "4", "6", "8", "9", "10", "11", "12", "13", "14",
5607         "16", "18", "20", "22", "24", "28",
5608         "32", "36", "40", "48", "56", "64", "72", "144"
5609     };
5611     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
5612     for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
5613     gtk_widget_set_size_request (cbox, 80, -1);
5614     gtk_toolbar_append_widget( tbl, cbox, "", "");
5615     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
5616     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
5617     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
5618     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
5620     ////////////Text anchor
5621     GtkWidget *group   = gtk_radio_button_new (NULL);
5622     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
5623     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
5625     // left
5626     GtkWidget *rbutton = group;
5627     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5628     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
5629     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5631     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5632     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
5633     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
5634     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
5636     // center
5637     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5638     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5639     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
5640     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5642     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5643     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
5644     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5645     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5647     // right
5648     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5649     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5650     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
5651     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5653     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5654     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
5655     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5656     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5658     // fill
5659     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5660     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5661     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
5662     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5664     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5665     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
5666     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
5667     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
5669     gtk_toolbar_append_widget( tbl, row, "", "");
5671     //spacer
5672     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5674     ////////////Text style
5675     row = gtk_hbox_new (FALSE, 4);
5677     // bold
5678     rbutton = gtk_toggle_button_new ();
5679     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5680     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
5681     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5682     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
5684     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5685     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
5686     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
5688     // italic
5689     rbutton = gtk_toggle_button_new ();
5690     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5691     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
5692     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5693     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
5695     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5696     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
5697     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
5699     gtk_toolbar_append_widget( tbl, row, "", "");
5701     //spacer
5702     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5704     ////////////Text orientation
5705     group   = gtk_radio_button_new (NULL);
5706     row     = gtk_hbox_new (FALSE, 4);
5707     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
5709     // horizontal
5710     rbutton = group;
5711     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5712     gtk_container_add           (GTK_CONTAINER (rbutton),
5713                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
5714     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5715     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
5717     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5718     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
5719     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
5721     // vertical
5722     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5723     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5724     gtk_container_add           (GTK_CONTAINER (rbutton),
5725                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
5726     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5727     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
5729     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5730     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
5731     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
5732     gtk_toolbar_append_widget( tbl, row, "", "" );
5735     //watch selection
5736     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
5738     sigc::connection *c_selection_changed =
5739         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5740                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
5741     pool->add_connection ("selection-changed", c_selection_changed);
5743     sigc::connection *c_selection_modified =
5744         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5745                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
5746     pool->add_connection ("selection-modified", c_selection_modified);
5748     sigc::connection *c_subselection_changed =
5749         new sigc::connection (desktop->connectToolSubselectionChanged
5750                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
5751     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
5753     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
5756     gtk_widget_show_all( GTK_WIDGET(tbl) );
5758     return GTK_WIDGET(tbl);
5759 } // end of sp_text_toolbox_new()
5761 }//<unnamed> namespace
5764 //#########################
5765 //##      Connector      ##
5766 //#########################
5768 static void sp_connector_path_set_avoid(void)
5770     cc_selection_set_avoid(true);
5774 static void sp_connector_path_set_ignore(void)
5776     cc_selection_set_avoid(false);
5781 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
5783     // quit if run by the _changed callbacks
5784     if (g_object_get_data( tbl, "freeze" )) {
5785         return;
5786     }
5788     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5789     SPDocument *doc = sp_desktop_document(desktop);
5791     if (!sp_document_get_undo_sensitive(doc))
5792     {
5793         return;
5794     }
5796     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5798     if ( repr->attribute("inkscape:connector-spacing") ) {
5799         gdouble priorValue = gtk_adjustment_get_value(adj);
5800         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
5801         if ( priorValue == gtk_adjustment_get_value(adj) ) {
5802             return;
5803         }
5804     } else if ( adj->value == defaultConnSpacing ) {
5805         return;
5806     }
5808     // in turn, prevent callbacks from responding
5809     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5811     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
5812     SP_OBJECT(desktop->namedview)->updateRepr();
5814     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
5815     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
5816         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
5817         NR::Matrix m = NR::identity();
5818         avoid_item_move(&m, item);
5819     }
5821     if (items) {
5822         g_slist_free(items);
5823     }
5825     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
5826             _("Change connector spacing"));
5828     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5830     spinbutton_defocus(GTK_OBJECT(tbl));
5833 static void sp_connector_graph_layout(void)
5835     if (!SP_ACTIVE_DESKTOP) return;
5837     // hack for clones, see comment in align-and-distribute.cpp
5838     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5839     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5841     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5843     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5845     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5848 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5850     if ( gtk_toggle_action_get_active( act ) ) {
5851         prefs_set_string_attribute("tools.connector", "directedlayout",
5852                 "true");
5853     } else {
5854         prefs_set_string_attribute("tools.connector", "directedlayout",
5855                 "false");
5856     }
5859 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5861     if ( gtk_toggle_action_get_active( act ) ) {
5862         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5863                 "true");
5864     } else {
5865         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5866                 "false");
5867     }
5871 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5873     prefs_set_double_attribute("tools.connector", "length", adj->value);
5874     spinbutton_defocus(GTK_OBJECT(tbl));
5877 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5878                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5879                                             bool /*is_interactive*/, gpointer data)
5881     GtkWidget *tbl = GTK_WIDGET(data);
5883     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5884         return;
5885     }
5886     if (strcmp(name, "inkscape:connector-spacing") != 0) {
5887         return;
5888     }
5890     GtkAdjustment *adj = (GtkAdjustment*)
5891             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5892     gdouble spacing = defaultConnSpacing;
5893     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5895     gtk_adjustment_set_value(adj, spacing);
5899 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5900     NULL, /* child_added */
5901     NULL, /* child_removed */
5902     connector_tb_event_attr_changed,
5903     NULL, /* content_changed */
5904     NULL  /* order_changed */
5905 };
5908 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5910     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
5912     {
5913         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5914                                           _("Avoid"),
5915                                           _("Make connectors avoid selected objects"),
5916                                           "connector_avoid",
5917                                           secondarySize );
5918         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5919         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5920     }
5922     {
5923         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5924                                           _("Ignore"),
5925                                           _("Make connectors ignore selected objects"),
5926                                           "connector_ignore",
5927                                           secondarySize );
5928         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5929         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5930     }
5932     EgeAdjustmentAction* eact = 0;
5934     // Spacing spinbox
5935     eact = create_adjustment_action( "ConnectorSpacingAction",
5936                                      _("Connector Spacing"), _("Spacing:"),
5937                                      _("The amount of space left around objects by auto-routing connectors"),
5938                                      "tools.connector", "spacing", defaultConnSpacing,
5939                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5940                                      0, 100, 1.0, 10.0,
5941                                      0, 0, 0,
5942                                      connector_spacing_changed, 1, 0 );
5943     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5945     // Graph (connector network) layout
5946     {
5947         InkAction* inky = ink_action_new( "ConnectorGraphAction",
5948                                           _("Graph"),
5949                                           _("Nicely arrange selected connector network"),
5950                                           "graph_layout",
5951                                           secondarySize );
5952         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5953         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5954     }
5956     // Default connector length spinbox
5957     eact = create_adjustment_action( "ConnectorLengthAction",
5958                                      _("Connector Length"), _("Length:"),
5959                                      _("Ideal length for connectors when layout is applied"),
5960                                      "tools.connector", "length", 100,
5961                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5962                                      10, 1000, 10.0, 100.0,
5963                                      0, 0, 0,
5964                                      connector_length_changed, 1, 0 );
5965     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5968     // Directed edges toggle button
5969     {
5970         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5971                                                       _("Downwards"),
5972                                                       _("Make connectors with end-markers (arrows) point downwards"),
5973                                                       "directed_graph",
5974                                                       Inkscape::ICON_SIZE_DECORATION );
5975         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5977         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5978         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5979                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5981         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5982     }
5984     // Avoid overlaps toggle button
5985     {
5986         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5987                                                       _("Remove overlaps"),
5988                                                       _("Do not allow overlapping shapes"),
5989                                                       "remove_overlaps",
5990                                                       Inkscape::ICON_SIZE_DECORATION );
5991         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5993         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5994         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5995                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5997         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5998     }
6000     // Code to watch for changes to the connector-spacing attribute in
6001     // the XML.
6002     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6003     g_assert(repr != NULL);
6005     purge_repr_listener( holder, holder );
6007     if (repr) {
6008         g_object_set_data( holder, "repr", repr );
6009         Inkscape::GC::anchor(repr);
6010         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
6011         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
6012     }
6013 } // end of sp_connector_toolbox_prep()
6016 //#########################
6017 //##     Paintbucket     ##
6018 //#########################
6020 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
6022     gint channels = ege_select_one_action_get_active( act );
6023     flood_channels_set_channels( channels );
6026 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
6028     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
6031 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
6033     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
6036 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
6038     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
6039     SPUnit const *unit = tracker->getActiveUnit();
6041     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
6043     prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
6046 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
6048     // FIXME: make defaults settable via Inkscape Options
6049     struct KeyValue {
6050         char const *key;
6051         double value;
6052     } const key_values[] = {
6053         {"threshold", 15},
6054         {"offset", 0.0}
6055     };
6057     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
6058         KeyValue const &kv = key_values[i];
6059         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
6060         if ( adj ) {
6061             gtk_adjustment_set_value(adj, kv.value);
6062         }
6063     }
6065     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
6066     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
6067     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
6068     ege_select_one_action_set_active( autogap_action, 0 );
6071 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6073     EgeAdjustmentAction* eact = 0;
6075     {
6076         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6078         GList* items = 0;
6079         gint count = 0;
6080         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
6081         {
6082             GtkTreeIter iter;
6083             gtk_list_store_append( model, &iter );
6084             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6085             count++;
6086         }
6087         g_list_free( items );
6088         items = 0;
6089         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
6090         g_object_set( act1, "short_label", _("Fill by:"), NULL );
6091         ege_select_one_action_set_appearance( act1, "compact" );
6092         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
6093         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
6094         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
6095         g_object_set_data( holder, "channels_action", act1 );
6096     }
6098     // Spacing spinbox
6099     {
6100         eact = create_adjustment_action(
6101             "ThresholdAction",
6102             _("Fill Threshold"), _("Threshold:"),
6103             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
6104             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
6105             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
6106             0, 0, 0,
6107             paintbucket_threshold_changed, 1, 0 );
6109         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6110         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6111     }
6113     // Create the units menu.
6114     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
6115     const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
6116     if (stored_unit)
6117         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
6118     g_object_set_data( holder, "tracker", tracker );
6119     {
6120         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
6121         gtk_action_group_add_action( mainActions, act );
6122     }
6124     // Offset spinbox
6125     {
6126         eact = create_adjustment_action(
6127             "OffsetAction",
6128             _("Grow/shrink by"), _("Grow/shrink by:"),
6129             _("The amount to grow (positive) or shrink (negative) the created fill path"),
6130             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
6131             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
6132             0, 0, 0,
6133             paintbucket_offset_changed, 1, 2);
6134         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
6136         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6137     }
6139     /* Auto Gap */
6140     {
6141         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6143         GList* items = 0;
6144         gint count = 0;
6145         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
6146         {
6147             GtkTreeIter iter;
6148             gtk_list_store_append( model, &iter );
6149             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6150             count++;
6151         }
6152         g_list_free( items );
6153         items = 0;
6154         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
6155         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
6156         ege_select_one_action_set_appearance( act2, "compact" );
6157         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
6158         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
6159         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
6160         g_object_set_data( holder, "autogap_action", act2 );
6161     }
6163     /* Reset */
6164     {
6165         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
6166                                           _("Defaults"),
6167                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
6168                                           GTK_STOCK_CLEAR );
6169         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
6170         gtk_action_group_add_action( mainActions, act );
6171         gtk_action_set_sensitive( act, TRUE );
6172     }
6176 /*
6177   Local Variables:
6178   mode:c++
6179   c-file-style:"stroustrup"
6180   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
6181   indent-tabs-mode:nil
6182   fill-column:99
6183   End:
6184 */
6185 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :