Code

fix reading and setting presets
[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"));
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         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4058         calligraphy_angle = eact;
4059         }
4061         {
4062         /* Fixation */
4063             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4064         gdouble values[] = {0, 20, 40, 60, 90, 100};
4065         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4066                                                               _("Fixation"), _("Fixation:"),
4067                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
4068                                                               "tools.calligraphic", "flatness", 90,
4069                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4070                                                               0.0, 100, 1.0, 10.0,
4071                                                               labels, values, G_N_ELEMENTS(labels),
4072                                                               sp_ddc_flatness_value_changed, 0.01, 0, 100 );
4073         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4074         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4075         }
4077         {
4078         /* Cap Rounding */
4079             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4080         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4081         // TRANSLATORS: "cap" means "end" (both start and finish) here
4082         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4083                                                               _("Cap rounding"), _("Caps:"),
4084                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4085                                                               "tools.calligraphic", "cap_rounding", 0.0,
4086                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4087                                                               0.0, 5.0, 0.01, 0.1,
4088                                                               labels, values, G_N_ELEMENTS(labels),
4089                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4090         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4091         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4092         }
4094         {
4095         /* Tremor */
4096             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4097         gdouble values[] = {0, 10, 20, 40, 60, 100};
4098         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4099                                                               _("Stroke Tremor"), _("Tremor:"),
4100                                                               _("Increase to make strokes rugged and trembling"),
4101                                                               "tools.calligraphic", "tremor", 0.0,
4102                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4103                                                               0.0, 100, 1, 0.0,
4104                                                               labels, values, G_N_ELEMENTS(labels),
4105                                                               sp_ddc_tremor_value_changed, 0.01, 0, 100 );
4107         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4108         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4109         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4110         }
4112         {
4113         /* Wiggle */
4114         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4115         gdouble values[] = {0, 20, 40, 60, 100};
4116         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4117                                                               _("Pen Wiggle"), _("Wiggle:"),
4118                                                               _("Increase to make the pen waver and wiggle"),
4119                                                               "tools.calligraphic", "wiggle", 0.0,
4120                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4121                                                               0.0, 100, 1, 0.0,
4122                                                               labels, values, G_N_ELEMENTS(labels),
4123                                                               sp_ddc_wiggle_value_changed, 0.01, 0, 100 );
4124         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4125         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4126         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4127         }
4129         {
4130         /* Mass */
4131             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4132         gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4133         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4134                                                               _("Pen Mass"), _("Mass:"),
4135                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
4136                                                               "tools.calligraphic", "mass", 2.0,
4137                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4138                                                               0.0, 100, 1, 0.0,
4139                                                               labels, values, G_N_ELEMENTS(labels),
4140                                                               sp_ddc_mass_value_changed, 0.01, 0, 100 );
4141         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4142         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4143         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4144         }
4147         /* Trace Background button */
4148         {
4149             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4150                                                           _("Trace Background"),
4151                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4152                                                           "trace_background",
4153                                                           Inkscape::ICON_SIZE_DECORATION );
4154             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4155             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4156             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
4157             g_object_set_data( holder, "tracebackground", act );
4158         }
4160         /* Use Pressure button */
4161         {
4162             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4163                                                           _("Pressure"),
4164                                                           _("Use the pressure of the input device to alter the width of the pen"),
4165                                                           "use_pressure",
4166                                                           Inkscape::ICON_SIZE_DECORATION );
4167             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4168             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4169             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
4170             g_object_set_data( holder, "usepressure", act );
4171         }
4173         /* Use Tilt button */
4174         {
4175             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4176                                                           _("Tilt"),
4177                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
4178                                                           "use_tilt",
4179                                                           Inkscape::ICON_SIZE_DECORATION );
4180             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4181             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4182             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4183             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4184             g_object_set_data( holder, "usetilt", act );
4185         }
4187         /*calligraphic profile */
4188         {
4189             GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
4190             gchar *pref_path;
4193             GtkTreeIter iter;
4194             gtk_list_store_append( model, &iter );
4195             gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4197             //TODO: switch back to prefs API
4198             Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
4199             Inkscape::XML::Node *child_repr = sp_repr_children(repr);
4200             int ii=1;
4201             while (child_repr) {
4202                 GtkTreeIter iter;
4203                 char *preset_name = (char *) child_repr->attribute("name");
4204                 gtk_list_store_append( model, &iter );
4205                 gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );
4206                 child_repr = sp_repr_next(child_repr);
4207             }
4209             pref_path = NULL;
4210             EgeSelectOneAction* act1 = ege_select_one_action_new( "SetProfileAction", "" , (_("Change calligraphic profile")), NULL, GTK_TREE_MODEL(model) );
4211             ege_select_one_action_set_appearance( act1, "compact" );
4212             g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder );
4213             gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
4214             g_object_set_data( holder, "profile_selector", act1 );
4216         }
4218         /*Save or delete calligraphic profile */
4219         {
4220             GtkAction* act = gtk_action_new( "SaveDeleteProfileAction",
4221                                              _("Defaults"),
4222                                              _("Save current settings as new profile"),
4223                                              GTK_STOCK_SAVE );
4224             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_dcc_save_profile), holder );
4227             gtk_action_group_add_action( mainActions, act );
4228             gtk_action_set_sensitive( act, TRUE );
4229             g_object_set_data( holder, "profile_save_delete", act );
4230         }
4231     }
4235 //########################
4236 //##    Circle / Arc    ##
4237 //########################
4239 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4241     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4242     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4244     if (v1 == 0 && v2 == 0) {
4245         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4246             gtk_action_set_sensitive( ocb, FALSE );
4247             gtk_action_set_sensitive( make_whole, FALSE );
4248         }
4249     } else {
4250         gtk_action_set_sensitive( ocb, TRUE );
4251         gtk_action_set_sensitive( make_whole, TRUE );
4252     }
4255 static void
4256 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4258     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4260     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4261         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
4262     }
4264     // quit if run by the attr_changed listener
4265     if (g_object_get_data( tbl, "freeze" )) {
4266         return;
4267     }
4269     // in turn, prevent listener from responding
4270     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4272     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4274     bool modmade = false;
4275     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4276          items != NULL;
4277          items = items->next)
4278     {
4279         SPItem *item = SP_ITEM(items->data);
4281         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4283             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4284             SPArc *arc = SP_ARC(item);
4286             if (!strcmp(value_name, "start"))
4287                 ge->start = (adj->value * M_PI)/ 180;
4288             else
4289                 ge->end = (adj->value * M_PI)/ 180;
4291             sp_genericellipse_normalize(ge);
4292             ((SPObject *)arc)->updateRepr();
4293             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4295             modmade = true;
4296         }
4297     }
4299     g_free(namespaced_name);
4301     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4303     sp_arctb_sensitivize( tbl, adj->value, other->value );
4305     if (modmade) {
4306         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4307                                    _("Arc: Change start/end"));
4308     }
4310     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4314 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
4316     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
4319 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4321     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
4325 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4327     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4328     gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
4329     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4330         prefs_set_int_attribute( "tools.eraser", "mode", eraserMode );
4331     }
4333     // only take action if run by the attr_changed listener
4334     if (!g_object_get_data( tbl, "freeze" )) {
4335         // in turn, prevent listener from responding
4336         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4338         if ( eraserMode != 0 ) {
4339         } else {
4340         }
4341         // TODO finish implementation
4343         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4344     }
4347 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4349     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4350     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4351         if ( ege_select_one_action_get_active( act ) != 0 ) {
4352             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
4353         } else {
4354             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
4355         }
4356     }
4358     // quit if run by the attr_changed listener
4359     if (g_object_get_data( tbl, "freeze" )) {
4360         return;
4361     }
4363     // in turn, prevent listener from responding
4364     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4366     bool modmade = false;
4368     if ( ege_select_one_action_get_active(act) != 0 ) {
4369         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4370              items != NULL;
4371              items = items->next)
4372         {
4373             if (SP_IS_ARC((SPItem *) items->data)) {
4374                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4375                 repr->setAttribute("sodipodi:open", "true");
4376                 SP_OBJECT((SPItem *) items->data)->updateRepr();
4377                 modmade = true;
4378             }
4379         }
4380     } else {
4381         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4382              items != NULL;
4383              items = items->next)
4384         {
4385             if (SP_IS_ARC((SPItem *) items->data))    {
4386                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4387                 repr->setAttribute("sodipodi:open", NULL);
4388                 SP_OBJECT((SPItem *) items->data)->updateRepr();
4389                 modmade = true;
4390             }
4391         }
4392     }
4394     if (modmade) {
4395         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4396                                    _("Arc: Change open/closed"));
4397     }
4399     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4402 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4404     GtkAdjustment *adj;
4405     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
4406     gtk_adjustment_set_value(adj, 0.0);
4407     gtk_adjustment_value_changed(adj);
4409     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
4410     gtk_adjustment_set_value(adj, 0.0);
4411     gtk_adjustment_value_changed(adj);
4413     spinbutton_defocus( GTK_OBJECT(obj) );
4416 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
4417                                       gchar const */*old_value*/, gchar const */*new_value*/,
4418                                       bool /*is_interactive*/, gpointer data)
4420     GObject *tbl = G_OBJECT(data);
4422     // quit if run by the _changed callbacks
4423     if (g_object_get_data( tbl, "freeze" )) {
4424         return;
4425     }
4427     // in turn, prevent callbacks from responding
4428     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4430     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4431     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4433     GtkAdjustment *adj1,*adj2;
4434     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4435     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4436     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4437     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4439     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4441     char const *openstr = NULL;
4442     openstr = repr->attribute("sodipodi:open");
4443     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4445     if (openstr) {
4446         ege_select_one_action_set_active( ocb, 1 );
4447     } else {
4448         ege_select_one_action_set_active( ocb, 0 );
4449     }
4451     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4454 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4455     NULL, /* child_added */
4456     NULL, /* child_removed */
4457     arc_tb_event_attr_changed,
4458     NULL, /* content_changed */
4459     NULL  /* order_changed */
4460 };
4463 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4465     int n_selected = 0;
4466     Inkscape::XML::Node *repr = NULL;
4468     purge_repr_listener( tbl, tbl );
4470     for (GSList const *items = selection->itemList();
4471          items != NULL;
4472          items = items->next)
4473     {
4474         if (SP_IS_ARC((SPItem *) items->data)) {
4475             n_selected++;
4476             repr = SP_OBJECT_REPR((SPItem *) items->data);
4477         }
4478     }
4480     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4482     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4483     if (n_selected == 0) {
4484         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4485     } else if (n_selected == 1) {
4486         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4487         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4489         if (repr) {
4490             g_object_set_data( tbl, "repr", repr );
4491             Inkscape::GC::anchor(repr);
4492             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4493             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4494         }
4495     } else {
4496         // FIXME: implement averaging of all parameters for multiple selected
4497         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4498         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4499         sp_arctb_sensitivize( tbl, 1, 0 );
4500     }
4504 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4506     EgeAdjustmentAction* eact = 0;
4507     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
4510     {
4511         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4512         ege_output_action_set_use_markup( act, TRUE );
4513         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4514         g_object_set_data( holder, "mode_action", act );
4515     }
4517     /* Start */
4518     {
4519         eact = create_adjustment_action( "ArcStartAction",
4520                                          _("Start"), _("Start:"),
4521                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
4522                                          "tools.shapes.arc", "start", 0.0,
4523                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4524                                          -360.0, 360.0, 1.0, 10.0,
4525                                          0, 0, 0,
4526                                          sp_arctb_start_value_changed);
4527         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4528     }
4530     /* End */
4531     {
4532         eact = create_adjustment_action( "ArcEndAction",
4533                                          _("End"), _("End:"),
4534                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
4535                                          "tools.shapes.arc", "end", 0.0,
4536                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4537                                          -360.0, 360.0, 1.0, 10.0,
4538                                          0, 0, 0,
4539                                          sp_arctb_end_value_changed);
4540         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4541     }
4543     /* Segments / Pie checkbox */
4544     {
4545         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4547         GtkTreeIter iter;
4548         gtk_list_store_append( model, &iter );
4549         gtk_list_store_set( model, &iter,
4550                             0, _("Closed arc"),
4551                             1, _("Switch to segment (closed shape with two radii)"),
4552                             2, "circle_closed_arc",
4553                             -1 );
4555         gtk_list_store_append( model, &iter );
4556         gtk_list_store_set( model, &iter,
4557                             0, _("Open Arc"),
4558                             1, _("Switch to arc (unclosed shape)"),
4559                             2, "circle_open_arc",
4560                             -1 );
4562         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4563         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4564         g_object_set_data( holder, "open_action", act );
4566         ege_select_one_action_set_appearance( act, "full" );
4567         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4568         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4569         ege_select_one_action_set_icon_column( act, 2 );
4570         ege_select_one_action_set_icon_size( act, secondarySize );
4571         ege_select_one_action_set_tooltip_column( act, 1  );
4573         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4574         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4575         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4576         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4577     }
4579     /* Make Whole */
4580     {
4581         InkAction* inky = ink_action_new( "ArcResetAction",
4582                                           _("Make whole"),
4583                                           _("Make the shape a whole ellipse, not arc or segment"),
4584                                           "reset_circle",
4585                                           secondarySize );
4586         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4587         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4588         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4589         g_object_set_data( holder, "make_whole", inky );
4590     }
4592     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4593     // sensitivize make whole and open checkbox
4594     {
4595         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4596         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4597         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4598     }
4601     sigc::connection *connection = new sigc::connection(
4602         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4603         );
4604     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4605     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4611 // toggle button callbacks and updaters
4613 //########################
4614 //##      Dropper       ##
4615 //########################
4617 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4618     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4619     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4620     if ( set_action ) {
4621         if ( gtk_toggle_action_get_active( act ) ) {
4622             gtk_action_set_sensitive( set_action, TRUE );
4623         } else {
4624             gtk_action_set_sensitive( set_action, FALSE );
4625         }
4626     }
4628     spinbutton_defocus(GTK_OBJECT(tbl));
4631 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4632     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4633     spinbutton_defocus(GTK_OBJECT(tbl));
4637 /**
4638  * Dropper auxiliary toolbar construction and setup.
4639  *
4640  * TODO: Would like to add swatch of current color.
4641  * TODO: Add queue of last 5 or so colors selected with new swatches so that
4642  *       can drag and drop places. Will provide a nice mixing palette.
4643  */
4644 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4646     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4648     {
4649         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4650         ege_output_action_set_use_markup( act, TRUE );
4651         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4652     }
4654     {
4655         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4656                                                       _("Pick opacity"),
4657                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4658                                                       NULL,
4659                                                       Inkscape::ICON_SIZE_DECORATION );
4660         g_object_set( act, "short_label", _("Pick"), NULL );
4661         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4662         g_object_set_data( holder, "pick_action", act );
4663         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4664         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4665     }
4667     {
4668         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4669                                                       _("Assign opacity"),
4670                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4671                                                       NULL,
4672                                                       Inkscape::ICON_SIZE_DECORATION );
4673         g_object_set( act, "short_label", _("Assign"), NULL );
4674         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4675         g_object_set_data( holder, "set_action", act );
4676         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4677         // make sure it's disabled if we're not picking alpha
4678         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4679         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4680     }
4685 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4687     {
4688         /* Width */
4689         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4690         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4691         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
4692                                                               _("Pen Width"), _("Width:"),
4693                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
4694                                                               "tools.eraser", "width", 15,
4695                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
4696                                                               1, 100, 1.0, 0.0,
4697                                                               labels, values, G_N_ELEMENTS(labels),
4698                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
4699         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4700         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4701         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4702     }
4704     {
4705         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4707         GtkTreeIter iter;
4708         gtk_list_store_append( model, &iter );
4709         gtk_list_store_set( model, &iter,
4710                             0, _("Delete"),
4711                             1, _("Delete objects touched by the eraser"),
4712                             2, "delete_object",
4713                             -1 );
4715         gtk_list_store_append( model, &iter );
4716         gtk_list_store_set( model, &iter,
4717                             0, _("Cut"),
4718                             1, _("Cut out from objects"),
4719                             2, "difference",
4720                             -1 );
4722         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4723         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4724         g_object_set_data( holder, "eraser_mode_action", act );
4726         ege_select_one_action_set_appearance( act, "full" );
4727         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4728         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4729         ege_select_one_action_set_icon_column( act, 2 );
4730         ege_select_one_action_set_tooltip_column( act, 1  );
4732         gint eraserMode = (prefs_get_int_attribute("tools.eraser", "mode", 0) != 0) ? 1 : 0;
4733         ege_select_one_action_set_active( act, eraserMode );
4734         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
4735     }
4739 //########################
4740 //##    Text Toolbox    ##
4741 //########################
4742 /*
4743 static void
4744 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
4746     //Call back for letter sizing spinbutton
4749 static void
4750 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
4752     //Call back for line height spinbutton
4755 static void
4756 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4758     //Call back for horizontal kerning spinbutton
4761 static void
4762 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4764     //Call back for vertical kerning spinbutton
4767 static void
4768 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
4770     //Call back for letter rotation spinbutton
4771 }*/
4773 namespace {
4775 bool popdown_visible = false;
4776 bool popdown_hasfocus = false;
4778 void
4779 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
4781     SPStyle *query =
4782         sp_style_new (SP_ACTIVE_DOCUMENT);
4784 //    int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4786     int result_family =
4787         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4789     int result_style =
4790         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4792     int result_numbers =
4793         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4795     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4797     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4798     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
4799     {
4800         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
4802         if (repr)
4803         {
4804             sp_style_read_from_repr (query, repr);
4805         }
4806         else
4807         {
4808             return;
4809         }
4810     }
4812     if (query->text)
4813     {
4814         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4815             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4816             gtk_entry_set_text (GTK_ENTRY (entry), "");
4818         } else if (query->text->font_specification.value || query->text->font_family.value) {
4820             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4822             // Get the font that corresponds
4823             Glib::ustring familyName;
4825             font_instance * font = font_factory::Default()->FaceFromStyle(query);
4826             if (font) {
4827                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4828                 font->Unref();
4829                 font = NULL;
4830             }
4832             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4834             Gtk::TreePath path;
4835             try {
4836                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4837             } catch (...) {
4838                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4839                 return;
4840             }
4842             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4843             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4845             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4847             gtk_tree_selection_select_path (tselection, path.gobj());
4848             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4850             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4851         }
4853         //Size
4854         GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4855         char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4856         g_object_set_data (tbl, "size-block", gpointer(1));
4857         gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4858         g_object_set_data (tbl, "size-block", gpointer(0));
4859         free (str);
4861         //Anchor
4862         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4863         {
4864             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4865             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4866             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4867             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4868         }
4869         else
4870         {
4871             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4872             {
4873                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4874                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4875                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4876                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4877             }
4878             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4879             {
4880                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4881                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4882                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4883                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4884             }
4885             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4886             {
4887                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4888                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4889                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4890                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4891             }
4892         }
4894         //Style
4895         {
4896             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4898             gboolean active = gtk_toggle_button_get_active (button);
4899             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4901             if (active != check)
4902             {
4903                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4904                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4905                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4906             }
4907         }
4909         {
4910             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4912             gboolean active = gtk_toggle_button_get_active (button);
4913             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4915             if (active != check)
4916             {
4917                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4918                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4919                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4920             }
4921         }
4923         //Orientation
4924         //locking both buttons, changing one affect all group (both)
4925         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4926         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4928         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4929         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4931         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4932         {
4933             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4934         }
4935         else
4936         {
4937             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4938         }
4939         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4940         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4941     }
4943     sp_style_unref(query);
4946 void
4947 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4949     sp_text_toolbox_selection_changed (selection, tbl);
4952 void
4953 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4955     sp_text_toolbox_selection_changed (NULL, tbl);
4958 void
4959 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
4960                                 GObject             *tbl)
4962     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
4963     GtkTreeModel *model = 0;
4964     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4965     GtkTreeIter   iter;
4966     char         *family = 0;
4968     gdk_pointer_ungrab (GDK_CURRENT_TIME);
4969     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4971     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4972         return;
4973     }
4975     gtk_tree_model_get (model, &iter, 0, &family, -1);
4977     if (g_object_get_data (G_OBJECT (selection), "block"))
4978     {
4979         gtk_entry_set_text (GTK_ENTRY (entry), family);
4980         return;
4981     }
4983     gtk_entry_set_text (GTK_ENTRY (entry), family);
4985     SPStyle *query =
4986         sp_style_new (SP_ACTIVE_DOCUMENT);
4988     int result_fontspec =
4989         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4991     //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4993     SPCSSAttr *css = sp_repr_css_attr_new ();
4996     // First try to get the font spec from the stored value
4997     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
4999     if (fontSpec.empty()) {
5000         // Construct a new font specification if it does not yet exist
5001         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5002         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5003         fontFromStyle->Unref();
5004     }
5006     if (!fontSpec.empty()) {
5007         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
5008         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
5009             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
5010             if (font) {
5011                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5013                 // Set all the these just in case they were altered when finding the best
5014                 // match for the new family and old style...
5016                 gchar c[256];
5018                 font->Family(c, 256);
5019                 sp_repr_css_set_property (css, "font-family", c);
5021                 font->Attribute( "weight", c, 256);
5022                 sp_repr_css_set_property (css, "font-weight", c);
5024                 font->Attribute("style", c, 256);
5025                 sp_repr_css_set_property (css, "font-style", c);
5027                 font->Attribute("stretch", c, 256);
5028                 sp_repr_css_set_property (css, "font-stretch", c);
5030                 font->Attribute("variant", c, 256);
5031                 sp_repr_css_set_property (css, "font-variant", c);
5033                 font->Unref();
5034             }
5035         }
5036     }
5038     // If querying returned nothing, set the default style of the tool (for new texts)
5039     if (result_fontspec == QUERY_STYLE_NOTHING)
5040     {
5041         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5042         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
5043     }
5044     else
5045     {
5046         sp_desktop_set_style (desktop, css, true, true);
5047     }
5049     sp_style_unref(query);
5051     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5052                                    _("Text: Change font family"));
5053     sp_repr_css_attr_unref (css);
5054     free (family);
5055     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5057     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5060 void
5061 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
5062                                        GObject      *tbl)
5064     const char *family = gtk_entry_get_text (entry);
5066     try {
5067         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
5068         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5069         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5070         gtk_tree_selection_select_path (selection, path.gobj());
5071         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5072         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5073     } catch (...) {
5074         if (family && strlen (family))
5075         {
5076             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5077         }
5078     }
5081 void
5082 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
5083                                    gpointer          data)
5085     if (g_object_get_data (G_OBJECT (button), "block")) return;
5086     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
5087     int prop = GPOINTER_TO_INT(data);
5089     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5090     SPCSSAttr *css = sp_repr_css_attr_new ();
5092     switch (prop)
5093     {
5094         case 0:
5095         {
5096             sp_repr_css_set_property (css, "text-anchor", "start");
5097             sp_repr_css_set_property (css, "text-align", "start");
5098             break;
5099         }
5100         case 1:
5101         {
5102             sp_repr_css_set_property (css, "text-anchor", "middle");
5103             sp_repr_css_set_property (css, "text-align", "center");
5104             break;
5105         }
5107         case 2:
5108         {
5109             sp_repr_css_set_property (css, "text-anchor", "end");
5110             sp_repr_css_set_property (css, "text-align", "end");
5111             break;
5112         }
5114         case 3:
5115         {
5116             sp_repr_css_set_property (css, "text-anchor", "start");
5117             sp_repr_css_set_property (css, "text-align", "justify");
5118             break;
5119         }
5120     }
5122     SPStyle *query =
5123         sp_style_new (SP_ACTIVE_DOCUMENT);
5124     int result_numbers =
5125         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5127     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5128     if (result_numbers == QUERY_STYLE_NOTHING)
5129     {
5130         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5131     }
5133     sp_style_unref(query);
5135     sp_desktop_set_style (desktop, css, true, true);
5136     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5137                                    _("Text: Change alignment"));
5138     sp_repr_css_attr_unref (css);
5140     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5143 void
5144 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
5145                                gpointer          data)
5147     if (g_object_get_data (G_OBJECT (button), "block")) return;
5149     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5150     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5151     int          prop       = GPOINTER_TO_INT(data);
5152     bool         active     = gtk_toggle_button_get_active (button);
5154     SPStyle *query =
5155         sp_style_new (SP_ACTIVE_DOCUMENT);
5157     int result_fontspec =
5158         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5160     //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5161     //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5162     //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5164     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
5165     Glib::ustring newFontSpec = "";
5167     if (fontSpec.empty()) {
5168         // Construct a new font specification if it does not yet exist
5169         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5170         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5171         fontFromStyle->Unref();
5172     }
5174     switch (prop)
5175     {
5176         case 0:
5177         {
5178             if (!fontSpec.empty()) {
5179                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
5180             }
5181             if (fontSpec != newFontSpec) {
5182                 // Don't even set the bold if the font didn't exist on the system
5183                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
5184             }
5185             break;
5186         }
5188         case 1:
5189         {
5190             if (!fontSpec.empty()) {
5191                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
5192             }
5193             if (fontSpec != newFontSpec) {
5194                 // Don't even set the italic if the font didn't exist on the system
5195                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
5196             }
5197             break;
5198         }
5199     }
5201     if (!newFontSpec.empty()) {
5202         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5203     }
5205     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5206     if (result_fontspec == QUERY_STYLE_NOTHING)
5207     {
5208         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5209     }
5211     sp_style_unref(query);
5213     sp_desktop_set_style (desktop, css, true, true);
5214     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5215                                    _("Text: Change font style"));
5216     sp_repr_css_attr_unref (css);
5218     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5221 void
5222 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
5223                                      gpointer         data)
5225     if (g_object_get_data (G_OBJECT (button), "block")) {
5226         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5227         return;
5228     }
5230     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5231     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5232     int          prop       = GPOINTER_TO_INT(data);
5234     switch (prop)
5235     {
5236         case 0:
5237         {
5238             sp_repr_css_set_property (css, "writing-mode", "lr");
5239             break;
5240         }
5242         case 1:
5243         {
5244             sp_repr_css_set_property (css, "writing-mode", "tb");
5245             break;
5246         }
5247     }
5249     SPStyle *query =
5250         sp_style_new (SP_ACTIVE_DOCUMENT);
5251     int result_numbers =
5252         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5254     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5255     if (result_numbers == QUERY_STYLE_NOTHING)
5256     {
5257         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5258     }
5260     sp_desktop_set_style (desktop, css, true, true);
5261     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5262                                    _("Text: Change orientation"));
5263     sp_repr_css_attr_unref (css);
5265     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5268 gboolean
5269 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5271     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5272     if (!desktop) return FALSE;
5274     switch (get_group0_keyval (event)) {
5275         case GDK_Escape: // defocus
5276             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5277             sp_text_toolbox_selection_changed (NULL, tbl); // update
5278             return TRUE; // I consumed the event
5279             break;
5280     }
5281     return FALSE;
5284 gboolean
5285 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5287     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5288     if (!desktop) return FALSE;
5290     switch (get_group0_keyval (event)) {
5291         case GDK_KP_Enter:
5292         case GDK_Return:
5293         case GDK_Escape: // defocus
5294             gtk_widget_hide (w);
5295             popdown_visible = false;
5296             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5297             return TRUE; // I consumed the event
5298             break;
5299         case GDK_w:
5300         case GDK_W:
5301             if (event->state & GDK_CONTROL_MASK) {
5302                 gtk_widget_hide (w);
5303                 popdown_visible = false;
5304                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5305                 return TRUE; // I consumed the event
5306             }
5307             break;
5308     }
5309     return FALSE;
5313 void
5314 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
5315                                GObject     *tbl)
5317     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5319     if (g_object_get_data (tbl, "size-block")) return;
5321     // If this is not from selecting a size in the list (in which case get_active will give the
5322     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5323     // process this event. This fixes GTK's stupid insistence on sending an activate change every
5324     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5325     if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5326         return;
5328     gchar *endptr;
5329     gdouble value = -1;
5330     char *text = gtk_combo_box_get_active_text (cbox);
5331     if (text) {
5332         value = g_strtod (text, &endptr);
5333         if (endptr == text) // conversion failed, non-numeric input
5334             value = -1;
5335         free (text);
5336     }
5337     if (value <= 0) {
5338         return; // could not parse value
5339     }
5341     SPCSSAttr *css = sp_repr_css_attr_new ();
5342     Inkscape::CSSOStringStream osfs;
5343     osfs << value;
5344     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5346     SPStyle *query =
5347         sp_style_new (SP_ACTIVE_DOCUMENT);
5348     int result_numbers =
5349         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5351     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5352     if (result_numbers == QUERY_STYLE_NOTHING)
5353     {
5354         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5355     }
5357     sp_style_unref(query);
5359     sp_desktop_set_style (desktop, css, true, true);
5360     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5361                                    _("Text: Change font size"));
5362     sp_repr_css_attr_unref (css);
5364     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5367 gboolean
5368 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5370     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5371     if (!desktop) return FALSE;
5373     if (!g_object_get_data (tbl, "esc-pressed")) {
5374         g_object_set_data (tbl, "enter-pressed", gpointer(1));
5375         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5376         sp_text_toolbox_size_changed (cbox, tbl);
5377         g_object_set_data (tbl, "enter-pressed", gpointer(0));
5378     }
5379     return FALSE; // I consumed the event
5383 gboolean
5384 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5386     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5387     if (!desktop) return FALSE;
5389     switch (get_group0_keyval (event)) {
5390         case GDK_Escape: // defocus
5391             g_object_set_data (tbl, "esc-pressed", gpointer(1));
5392             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5393             g_object_set_data (tbl, "esc-pressed", gpointer(0));
5394             return TRUE; // I consumed the event
5395             break;
5396         case GDK_Return: // defocus
5397         case GDK_KP_Enter:
5398             g_object_set_data (tbl, "enter-pressed", gpointer(1));
5399             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5400             sp_text_toolbox_size_changed (cbox, tbl);
5401             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5402             g_object_set_data (tbl, "enter-pressed", gpointer(0));
5403             return TRUE; // I consumed the event
5404             break;
5405     }
5406     return FALSE;
5409 void
5410 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
5411                                          GObject            *tbl)
5413     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
5414     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5415     int x, y;
5417     if (!popdown_visible)
5418     {
5419         gdk_window_get_origin (widget->window, &x, &y);
5420         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
5421         gtk_widget_show_all (popdown);
5422         //sp_transientize (popdown);
5424         gdk_pointer_grab (widget->window, TRUE,
5425                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
5426                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
5427                                         GDK_POINTER_MOTION_MASK),
5428                           NULL, NULL, GDK_CURRENT_TIME);
5430         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
5432         popdown_visible = true;
5433     }
5434     else
5435     {
5436         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5437         gdk_pointer_ungrab (GDK_CURRENT_TIME);
5438         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5439         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5440         gtk_widget_hide (popdown);
5441         popdown_visible = false;
5442     }
5445 gboolean
5446 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
5447                                  GdkEventFocus    */*event*/,
5448                                  GObject          */*tbl*/)
5450     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
5451     return FALSE;
5454 gboolean
5455 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
5456                                    GdkEventFocus    */*event*/,
5457                                    GObject          */*tbl*/)
5459     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5461     if (popdown_hasfocus) {
5462         gtk_widget_hide (popdown);
5463         popdown_hasfocus = false;
5464         popdown_visible = false;
5465         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5466         return TRUE;
5467     }
5468     return FALSE;
5471 gboolean
5472 sp_text_toolbox_popdown_focus_in (GtkWidget        */*popdown*/,
5473                                    GdkEventFocus    */*event*/,
5474                                    GObject          */*tbl*/)
5476     popdown_hasfocus = true;
5477     return TRUE;
5481 void
5482 cell_data_func  (GtkTreeViewColumn */*column*/,
5483                  GtkCellRenderer   *cell,
5484                  GtkTreeModel      *tree_model,
5485                  GtkTreeIter       *iter,
5486                  gpointer           /*data*/)
5488     char        *family,
5489         *family_escaped,
5490         *sample_escaped;
5492     static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
5494     gtk_tree_model_get (tree_model, iter, 0, &family, -1);
5496     family_escaped = g_markup_escape_text (family, -1);
5497     sample_escaped = g_markup_escape_text (sample, -1);
5499     std::stringstream markup;
5500     markup << family_escaped << "  <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
5501     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
5503     free (family);
5504     free (family_escaped);
5505     free (sample_escaped);
5508 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
5509     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
5510     if (completion) {
5511         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
5512         g_object_unref (completion);
5513     }
5516 GtkWidget*
5517 sp_text_toolbox_new (SPDesktop *desktop)
5519     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
5520     GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
5522     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
5523     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
5525     GtkTooltips *tt = gtk_tooltips_new();
5526     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
5528     ////////////Family
5529     //Window
5530     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5531     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
5533     //Entry
5534     GtkWidget           *entry = gtk_entry_new ();
5535     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
5536     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
5537     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
5538     gtk_entry_completion_set_text_column (completion, 0);
5539     gtk_entry_completion_set_minimum_key_length (completion, 1);
5540     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
5541     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
5542     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
5543     gtk_toolbar_append_widget( tbl, entry, "", "" );
5544     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
5546     //Button
5547     GtkWidget   *button = gtk_button_new ();
5548     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
5549     gtk_toolbar_append_widget( tbl, button, "", "");
5551     //Popdown
5552     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
5553     GtkWidget           *treeview = gtk_tree_view_new ();
5555     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
5556     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
5557     gtk_tree_view_column_pack_start (column, cell, FALSE);
5558     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
5559     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5560     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5562     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5563     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5564     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5566     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5568     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5569     gtk_container_add (GTK_CONTAINER (sw), treeview);
5571     gtk_container_add (GTK_CONTAINER (window), sw);
5572     gtk_widget_set_size_request (window, 300, 450);
5574     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5575     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5576     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5578     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5580     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5581     g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5582     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5584     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5585     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5587     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5588     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5589     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5590     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5591     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5593     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
5594     GtkWidget *box = gtk_event_box_new ();
5595     gtk_container_add (GTK_CONTAINER (box), image);
5596     gtk_toolbar_append_widget( tbl, box, "", "");
5597     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5598     GtkTooltips *tooltips = gtk_tooltips_new ();
5599     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5600     gtk_widget_hide (GTK_WIDGET (box));
5601     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
5603     ////////////Size
5604     const char *sizes[] = {
5605         "4", "6", "8", "9", "10", "11", "12", "13", "14",
5606         "16", "18", "20", "22", "24", "28",
5607         "32", "36", "40", "48", "56", "64", "72", "144"
5608     };
5610     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
5611     for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
5612     gtk_widget_set_size_request (cbox, 80, -1);
5613     gtk_toolbar_append_widget( tbl, cbox, "", "");
5614     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
5615     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
5616     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
5617     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
5619     ////////////Text anchor
5620     GtkWidget *group   = gtk_radio_button_new (NULL);
5621     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
5622     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
5624     // left
5625     GtkWidget *rbutton = group;
5626     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5627     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
5628     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5630     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5631     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
5632     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
5633     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
5635     // center
5636     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5637     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5638     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
5639     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5641     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5642     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
5643     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5644     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5646     // right
5647     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5648     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5649     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
5650     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5652     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5653     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
5654     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5655     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5657     // fill
5658     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5659     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5660     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
5661     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5663     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5664     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
5665     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
5666     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
5668     gtk_toolbar_append_widget( tbl, row, "", "");
5670     //spacer
5671     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5673     ////////////Text style
5674     row = gtk_hbox_new (FALSE, 4);
5676     // bold
5677     rbutton = gtk_toggle_button_new ();
5678     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5679     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
5680     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5681     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
5683     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5684     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
5685     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
5687     // italic
5688     rbutton = gtk_toggle_button_new ();
5689     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5690     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
5691     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5692     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
5694     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5695     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
5696     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
5698     gtk_toolbar_append_widget( tbl, row, "", "");
5700     //spacer
5701     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5703     ////////////Text orientation
5704     group   = gtk_radio_button_new (NULL);
5705     row     = gtk_hbox_new (FALSE, 4);
5706     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
5708     // horizontal
5709     rbutton = group;
5710     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5711     gtk_container_add           (GTK_CONTAINER (rbutton),
5712                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
5713     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5714     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
5716     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5717     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
5718     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
5720     // vertical
5721     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5722     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5723     gtk_container_add           (GTK_CONTAINER (rbutton),
5724                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
5725     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5726     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
5728     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
5729     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
5730     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
5731     gtk_toolbar_append_widget( tbl, row, "", "" );
5734     //watch selection
5735     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
5737     sigc::connection *c_selection_changed =
5738         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5739                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
5740     pool->add_connection ("selection-changed", c_selection_changed);
5742     sigc::connection *c_selection_modified =
5743         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5744                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
5745     pool->add_connection ("selection-modified", c_selection_modified);
5747     sigc::connection *c_subselection_changed =
5748         new sigc::connection (desktop->connectToolSubselectionChanged
5749                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
5750     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
5752     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
5755     gtk_widget_show_all( GTK_WIDGET(tbl) );
5757     return GTK_WIDGET(tbl);
5758 } // end of sp_text_toolbox_new()
5760 }//<unnamed> namespace
5763 //#########################
5764 //##      Connector      ##
5765 //#########################
5767 static void sp_connector_path_set_avoid(void)
5769     cc_selection_set_avoid(true);
5773 static void sp_connector_path_set_ignore(void)
5775     cc_selection_set_avoid(false);
5780 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
5782     // quit if run by the _changed callbacks
5783     if (g_object_get_data( tbl, "freeze" )) {
5784         return;
5785     }
5787     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5788     SPDocument *doc = sp_desktop_document(desktop);
5790     if (!sp_document_get_undo_sensitive(doc))
5791     {
5792         return;
5793     }
5795     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5797     if ( repr->attribute("inkscape:connector-spacing") ) {
5798         gdouble priorValue = gtk_adjustment_get_value(adj);
5799         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
5800         if ( priorValue == gtk_adjustment_get_value(adj) ) {
5801             return;
5802         }
5803     } else if ( adj->value == defaultConnSpacing ) {
5804         return;
5805     }
5807     // in turn, prevent callbacks from responding
5808     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5810     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
5811     SP_OBJECT(desktop->namedview)->updateRepr();
5813     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
5814     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
5815         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
5816         NR::Matrix m = NR::identity();
5817         avoid_item_move(&m, item);
5818     }
5820     if (items) {
5821         g_slist_free(items);
5822     }
5824     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
5825             _("Change connector spacing"));
5827     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5829     spinbutton_defocus(GTK_OBJECT(tbl));
5832 static void sp_connector_graph_layout(void)
5834     if (!SP_ACTIVE_DESKTOP) return;
5836     // hack for clones, see comment in align-and-distribute.cpp
5837     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5838     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5840     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5842     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5844     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5847 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5849     if ( gtk_toggle_action_get_active( act ) ) {
5850         prefs_set_string_attribute("tools.connector", "directedlayout",
5851                 "true");
5852     } else {
5853         prefs_set_string_attribute("tools.connector", "directedlayout",
5854                 "false");
5855     }
5858 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5860     if ( gtk_toggle_action_get_active( act ) ) {
5861         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5862                 "true");
5863     } else {
5864         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5865                 "false");
5866     }
5870 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5872     prefs_set_double_attribute("tools.connector", "length", adj->value);
5873     spinbutton_defocus(GTK_OBJECT(tbl));
5876 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5877                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5878                                             bool /*is_interactive*/, gpointer data)
5880     GtkWidget *tbl = GTK_WIDGET(data);
5882     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5883         return;
5884     }
5885     if (strcmp(name, "inkscape:connector-spacing") != 0) {
5886         return;
5887     }
5889     GtkAdjustment *adj = (GtkAdjustment*)
5890             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5891     gdouble spacing = defaultConnSpacing;
5892     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5894     gtk_adjustment_set_value(adj, spacing);
5898 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5899     NULL, /* child_added */
5900     NULL, /* child_removed */
5901     connector_tb_event_attr_changed,
5902     NULL, /* content_changed */
5903     NULL  /* order_changed */
5904 };
5907 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5909     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
5911     {
5912         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5913                                           _("Avoid"),
5914                                           _("Make connectors avoid selected objects"),
5915                                           "connector_avoid",
5916                                           secondarySize );
5917         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5918         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5919     }
5921     {
5922         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5923                                           _("Ignore"),
5924                                           _("Make connectors ignore selected objects"),
5925                                           "connector_ignore",
5926                                           secondarySize );
5927         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5928         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5929     }
5931     EgeAdjustmentAction* eact = 0;
5933     // Spacing spinbox
5934     eact = create_adjustment_action( "ConnectorSpacingAction",
5935                                      _("Connector Spacing"), _("Spacing:"),
5936                                      _("The amount of space left around objects by auto-routing connectors"),
5937                                      "tools.connector", "spacing", defaultConnSpacing,
5938                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5939                                      0, 100, 1.0, 10.0,
5940                                      0, 0, 0,
5941                                      connector_spacing_changed, 1, 0 );
5942     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5944     // Graph (connector network) layout
5945     {
5946         InkAction* inky = ink_action_new( "ConnectorGraphAction",
5947                                           _("Graph"),
5948                                           _("Nicely arrange selected connector network"),
5949                                           "graph_layout",
5950                                           secondarySize );
5951         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5952         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5953     }
5955     // Default connector length spinbox
5956     eact = create_adjustment_action( "ConnectorLengthAction",
5957                                      _("Connector Length"), _("Length:"),
5958                                      _("Ideal length for connectors when layout is applied"),
5959                                      "tools.connector", "length", 100,
5960                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5961                                      10, 1000, 10.0, 100.0,
5962                                      0, 0, 0,
5963                                      connector_length_changed, 1, 0 );
5964     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5967     // Directed edges toggle button
5968     {
5969         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5970                                                       _("Downwards"),
5971                                                       _("Make connectors with end-markers (arrows) point downwards"),
5972                                                       "directed_graph",
5973                                                       Inkscape::ICON_SIZE_DECORATION );
5974         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5976         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5977         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5978                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5980         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5981     }
5983     // Avoid overlaps toggle button
5984     {
5985         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5986                                                       _("Remove overlaps"),
5987                                                       _("Do not allow overlapping shapes"),
5988                                                       "remove_overlaps",
5989                                                       Inkscape::ICON_SIZE_DECORATION );
5990         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5992         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5993         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5994                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5996         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5997     }
5999     // Code to watch for changes to the connector-spacing attribute in
6000     // the XML.
6001     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6002     g_assert(repr != NULL);
6004     purge_repr_listener( holder, holder );
6006     if (repr) {
6007         g_object_set_data( holder, "repr", repr );
6008         Inkscape::GC::anchor(repr);
6009         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
6010         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
6011     }
6012 } // end of sp_connector_toolbox_prep()
6015 //#########################
6016 //##     Paintbucket     ##
6017 //#########################
6019 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
6021     gint channels = ege_select_one_action_get_active( act );
6022     flood_channels_set_channels( channels );
6025 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
6027     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
6030 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
6032     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
6035 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
6037     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
6038     SPUnit const *unit = tracker->getActiveUnit();
6040     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
6042     prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
6045 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
6047     // FIXME: make defaults settable via Inkscape Options
6048     struct KeyValue {
6049         char const *key;
6050         double value;
6051     } const key_values[] = {
6052         {"threshold", 15},
6053         {"offset", 0.0}
6054     };
6056     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
6057         KeyValue const &kv = key_values[i];
6058         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
6059         if ( adj ) {
6060             gtk_adjustment_set_value(adj, kv.value);
6061         }
6062     }
6064     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
6065     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
6066     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
6067     ege_select_one_action_set_active( autogap_action, 0 );
6070 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6072     EgeAdjustmentAction* eact = 0;
6074     {
6075         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6077         GList* items = 0;
6078         gint count = 0;
6079         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
6080         {
6081             GtkTreeIter iter;
6082             gtk_list_store_append( model, &iter );
6083             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6084             count++;
6085         }
6086         g_list_free( items );
6087         items = 0;
6088         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
6089         g_object_set( act1, "short_label", _("Fill by:"), NULL );
6090         ege_select_one_action_set_appearance( act1, "compact" );
6091         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
6092         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
6093         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
6094         g_object_set_data( holder, "channels_action", act1 );
6095     }
6097     // Spacing spinbox
6098     {
6099         eact = create_adjustment_action(
6100             "ThresholdAction",
6101             _("Fill Threshold"), _("Threshold:"),
6102             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
6103             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
6104             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
6105             0, 0, 0,
6106             paintbucket_threshold_changed, 1, 0 );
6108         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6109         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6110     }
6112     // Create the units menu.
6113     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
6114     const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
6115     if (stored_unit)
6116         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
6117     g_object_set_data( holder, "tracker", tracker );
6118     {
6119         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
6120         gtk_action_group_add_action( mainActions, act );
6121     }
6123     // Offset spinbox
6124     {
6125         eact = create_adjustment_action(
6126             "OffsetAction",
6127             _("Grow/shrink by"), _("Grow/shrink by:"),
6128             _("The amount to grow (positive) or shrink (negative) the created fill path"),
6129             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
6130             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
6131             0, 0, 0,
6132             paintbucket_offset_changed, 1, 2);
6133         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
6135         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6136     }
6138     /* Auto Gap */
6139     {
6140         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6142         GList* items = 0;
6143         gint count = 0;
6144         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
6145         {
6146             GtkTreeIter iter;
6147             gtk_list_store_append( model, &iter );
6148             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6149             count++;
6150         }
6151         g_list_free( items );
6152         items = 0;
6153         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
6154         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
6155         ege_select_one_action_set_appearance( act2, "compact" );
6156         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
6157         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
6158         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
6159         g_object_set_data( holder, "autogap_action", act2 );
6160     }
6162     /* Reset */
6163     {
6164         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
6165                                           _("Defaults"),
6166                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
6167                                           GTK_STOCK_CLEAR );
6168         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
6169         gtk_action_group_add_action( mainActions, act );
6170         gtk_action_set_sensitive( act, TRUE );
6171     }
6175 /*
6176   Local Variables:
6177   mode:c++
6178   c-file-style:"stroustrup"
6179   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
6180   indent-tabs-mode:nil
6181   fill-column:99
6182   End:
6183 */
6184 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :