Code

185b8041363d0bc5523605fe675b36e61a366c69
[inkscape.git] / src / widgets / toolbox.cpp
1 /** \file
2  * Controls bars for some of Inkscape's tools
3  * (for some tools, they are in their own files)
4  */
6 /*
7 *
8 * Authors:
9 *   MenTaLguY <mental@rydia.net>
10 *   Lauris Kaplinski <lauris@kaplinski.com>
11 *   bulia byak <buliabyak@users.sf.net>
12 *   Frank Felfe <innerspace@iname.com>
13 *   John Cliff <simarilius@yahoo.com>
14 *   David Turner <novalis@gnu.org>
15 *   Josh Andler <scislac@scislac.com>
16 *   Jon A. Cruz <jon@joncruz.org>
17 *   Maximilian Albert <maximilian.albert@gmail.com>
18 *
19 * Copyright (C) 2004 David Turner
20 * Copyright (C) 2003 MenTaLguY
21 * Copyright (C) 1999-2008 authors
22 * Copyright (C) 2001-2002 Ximian, Inc.
23 *
24 * Released under GNU GPL, read the file 'COPYING' for more information
25 */
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
31 #include <cstring>
32 #include <string>
34 #include <gtkmm.h>
35 #include <gtk/gtk.h>
36 #include <iostream>
37 #include <sstream>
39 #include "widgets/button.h"
40 #include "widgets/widget-sizes.h"
41 #include "widgets/spw-utilities.h"
42 #include "widgets/spinbutton-events.h"
43 #include "dialogs/text-edit.h"
44 #include "dialogs/dialog-events.h"
46 #include "ui/widget/style-swatch.h"
48 #include "prefs-utils.h"
49 #include "verbs.h"
50 #include "sp-namedview.h"
51 #include "desktop.h"
52 #include "desktop-handles.h"
53 #include "xml/repr.h"
54 #include "xml/node-event-vector.h"
55 #include "xml/attribute-record.h"
56 #include <glibmm/i18n.h>
57 #include "helper/unit-menu.h"
58 #include "helper/units.h"
59 #include "live_effects/effect.h"
61 #include "inkscape.h"
62 #include "conn-avoid-ref.h"
65 #include "select-toolbar.h"
66 #include "gradient-toolbar.h"
68 #include "connector-context.h"
69 #include "node-context.h"
70 #include "pen-context.h"
71 #include "lpe-tool-context.h"
72 #include "live_effects/lpe-line_segment.h"
73 #include "shape-editor.h"
74 #include "tweak-context.h"
75 #include "sp-rect.h"
76 #include "box3d.h"
77 #include "box3d-context.h"
78 #include "sp-star.h"
79 #include "sp-spiral.h"
80 #include "sp-ellipse.h"
81 #include "sp-text.h"
82 #include "sp-flowtext.h"
83 #include "sp-clippath.h"
84 #include "sp-mask.h"
85 #include "style.h"
86 #include "tools-switch.h"
87 #include "selection.h"
88 #include "selection-chemistry.h"
89 #include "document-private.h"
90 #include "desktop-style.h"
91 #include "../libnrtype/font-lister.h"
92 #include "../libnrtype/font-instance.h"
93 #include "../connection-pool.h"
94 #include "../prefs-utils.h"
95 #include "../inkscape-stock.h"
96 #include "icon.h"
97 #include "graphlayout/graphlayout.h"
98 #include "interface.h"
99 #include "shortcuts.h"
101 #include "mod360.h"
103 #include "toolbox.h"
105 #include "flood-context.h"
107 #include "ink-action.h"
108 #include "ege-adjustment-action.h"
109 #include "ege-output-action.h"
110 #include "ege-select-one-action.h"
111 #include "helper/unit-tracker.h"
112 #include "live_effects/lpe-angle_bisector.h"
114 #include "svg/css-ostringstream.h"
116 #include "widgets/calligraphic-profile-rename.h"
118 using Inkscape::UnitTracker;
120 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
121 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
123 static void       sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 static void       sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
126 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
127 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
128 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
129 static void       box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
130 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
131 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
132 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
133 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
134 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
135 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
136 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
137 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
138 static void       sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
139 static void       sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
141 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
144 Inkscape::IconSize prefToSize( gchar const *path, gchar const *attr, int base ) {
145     static Inkscape::IconSize sizeChoices[] = {
146         Inkscape::ICON_SIZE_LARGE_TOOLBAR,
147         Inkscape::ICON_SIZE_SMALL_TOOLBAR,
148         Inkscape::ICON_SIZE_MENU
149     };
150     int index = prefs_get_int_attribute_limited( path, attr, base, 0, G_N_ELEMENTS(sizeChoices) );
151     return sizeChoices[index];
154 static struct {
155     gchar const *type_name;
156     gchar const *data_name;
157     sp_verb_t verb;
158     sp_verb_t doubleclick_verb;
159 } const tools[] = {
160     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
161     { "SPNodeContext",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
162     { "SPTweakContext",    "tweak_tool",     SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
163     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
164     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
165     { "Box3DContext",      "3dbox_tool",     SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
166     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
167     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
168     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
169     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
170     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
171     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
172     { "SPLPEToolContext",  "lpetool_tool",   SP_VERB_CONTEXT_LPETOOL, SP_VERB_CONTEXT_LPETOOL_PREFS },
173     { "SPEraserContext",   "eraser_tool",    SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
174     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
175     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
176     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
177     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
178     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
179     { NULL, NULL, 0, 0 }
180 };
182 static struct {
183     gchar const *type_name;
184     gchar const *data_name;
185     GtkWidget *(*create_func)(SPDesktop *desktop);
186     void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
187     gchar const *ui_name;
188     gint swatch_verb_id;
189     gchar const *swatch_tool;
190     gchar const *swatch_tip;
191 } const aux_toolboxes[] = {
192     { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep,            "SelectToolbar",
193       SP_VERB_INVALID, 0, 0},
194     { "SPNodeContext",   "node_toolbox",   0, sp_node_toolbox_prep,              "NodeToolbar",
195       SP_VERB_INVALID, 0, 0},
196     { "SPTweakContext",   "tweak_toolbox",   0, sp_tweak_toolbox_prep,              "TweakToolbar",
197       SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", N_("Color/opacity used for color tweaking")},
198     { "SPZoomContext",   "zoom_toolbox",   0, sp_zoom_toolbox_prep,              "ZoomToolbar",
199       SP_VERB_INVALID, 0, 0},
200     { "SPStarContext",   "star_toolbox",   0, sp_star_toolbox_prep,              "StarToolbar",
201       SP_VERB_CONTEXT_STAR_PREFS,   "tools.shapes.star",     N_("Style of new stars")},
202     { "SPRectContext",   "rect_toolbox",   0, sp_rect_toolbox_prep,              "RectToolbar",
203       SP_VERB_CONTEXT_RECT_PREFS,   "tools.shapes.rect",     N_("Style of new rectangles")},
204     { "Box3DContext",  "3dbox_toolbox",  0, box3d_toolbox_prep,             "3DBoxToolbar",
205       SP_VERB_CONTEXT_3DBOX_PREFS,  "tools.shapes.3dbox",    N_("Style of new 3D boxes")},
206     { "SPArcContext",    "arc_toolbox",    0, sp_arc_toolbox_prep,               "ArcToolbar",
207       SP_VERB_CONTEXT_ARC_PREFS,    "tools.shapes.arc",      N_("Style of new ellipses")},
208     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
209       SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral",   N_("Style of new spirals")},
210     { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep,            "PencilToolbar",
211       SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", N_("Style of new paths created by Pencil")},
212     { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep,                     "PenToolbar",
213       SP_VERB_CONTEXT_PEN_PREFS,    "tools.freehand.pen",    N_("Style of new paths created by Pen")},
214     { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
215       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", N_("Style of new calligraphic strokes")},
216     { "SPEraserContext", "eraser_toolbox", 0, sp_eraser_toolbox_prep,"EraserToolbar",
217       SP_VERB_CONTEXT_ERASER_PREFS, "tools.eraser", _("TBD")},
218     { "SPLPEToolContext", "lpetool_toolbox", 0, sp_lpetool_toolbox_prep, "LPEToolToolbar",
219       SP_VERB_CONTEXT_LPETOOL_PREFS, "tools.lpetool", _("TBD")},
220     { "SPTextContext",   "text_toolbox",   sp_text_toolbox_new, 0,               0,
221       SP_VERB_INVALID, 0, 0},
222     { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep,         "DropperToolbar",
223       SP_VERB_INVALID, 0, 0},
224     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0,       0,
225       SP_VERB_INVALID, 0, 0},
226     { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep,   "ConnectorToolbar",
227       SP_VERB_INVALID, 0, 0},
228     { "SPFloodContext",  "paintbucket_toolbox",  0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
229       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", N_("Style of Paint Bucket fill objects")},
230     { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
231 };
233 #define TOOLBAR_SLIDER_HINT "full"
235 static gchar const * ui_descr =
236         "<ui>"
237         "  <toolbar name='SelectToolbar'>"
238         "    <toolitem action='EditSelectAll' />"
239         "    <toolitem action='EditSelectAllInAllLayers' />"
240         "    <toolitem action='EditDeselect' />"
241         "    <separator />"
242         "    <toolitem action='ObjectRotate90CCW' />"
243         "    <toolitem action='ObjectRotate90' />"
244         "    <toolitem action='ObjectFlipHorizontally' />"
245         "    <toolitem action='ObjectFlipVertically' />"
246         "    <separator />"
247         "    <toolitem action='SelectionToBack' />"
248         "    <toolitem action='SelectionLower' />"
249         "    <toolitem action='SelectionRaise' />"
250         "    <toolitem action='SelectionToFront' />"
251         "    <separator />"
252         "    <toolitem action='XAction' />"
253         "    <toolitem action='YAction' />"
254         "    <toolitem action='WidthAction' />"
255         "    <toolitem action='LockAction' />"
256         "    <toolitem action='HeightAction' />"
257         "    <toolitem action='UnitsAction' />"
258         "    <separator />"
259         "    <toolitem action='transform_affect_label' />"
260         "    <toolitem action='transform_stroke' />"
261         "    <toolitem action='transform_corners' />"
262         "    <toolitem action='transform_gradient' />"
263         "    <toolitem action='transform_pattern' />"
264         "  </toolbar>"
266         "  <toolbar name='NodeToolbar'>"
267         "    <toolitem action='NodeInsertAction' />"
268         "    <toolitem action='NodeDeleteAction' />"
269         "    <separator />"
270         "    <toolitem action='NodeJoinAction' />"
271         "    <toolitem action='NodeBreakAction' />"
272         "    <separator />"
273         "    <toolitem action='NodeJoinSegmentAction' />"
274         "    <toolitem action='NodeDeleteSegmentAction' />"
275         "    <separator />"
276         "    <toolitem action='NodeCuspAction' />"
277         "    <toolitem action='NodeSmoothAction' />"
278         "    <toolitem action='NodeSymmetricAction' />"
279         "    <separator />"
280         "    <toolitem action='NodeLineAction' />"
281         "    <toolitem action='NodeCurveAction' />"
282         "    <separator />"
283         "    <toolitem action='ObjectToPath' />"
284         "    <toolitem action='StrokeToPath' />"
285         "    <separator />"
286         "    <toolitem action='NodeXAction' />"
287         "    <toolitem action='NodeYAction' />"
288         "    <toolitem action='NodeUnitsAction' />"
289         "    <separator />"
290         "    <toolitem action='ObjectEditClipPathAction' />"
291         "    <toolitem action='ObjectEditMaskPathAction' />"
292         "    <toolitem action='EditNextLPEParameterAction' />"
293         "    <separator />"
294         "    <toolitem action='NodesShowHandlesAction' />"
295         "    <toolitem action='NodesShowHelperpath' />"
296         "  </toolbar>"
298         "  <toolbar name='TweakToolbar'>"
299         "    <toolitem action='TweakWidthAction' />"
300         "    <separator />"
301         "    <toolitem action='TweakForceAction' />"
302         "    <toolitem action='TweakPressureAction' />"
303         "    <separator />"
304         "    <toolitem action='TweakModeAction' />"
305         "    <separator />"
306         "    <toolitem action='TweakFidelityAction' />"
307         "    <separator />"
308         "    <toolitem action='TweakChannelsLabel' />"
309         "    <toolitem action='TweakDoH' />"
310         "    <toolitem action='TweakDoS' />"
311         "    <toolitem action='TweakDoL' />"
312         "    <toolitem action='TweakDoO' />"
313         "  </toolbar>"
315         "  <toolbar name='ZoomToolbar'>"
316         "    <toolitem action='ZoomIn' />"
317         "    <toolitem action='ZoomOut' />"
318         "    <separator />"
319         "    <toolitem action='Zoom1:0' />"
320         "    <toolitem action='Zoom1:2' />"
321         "    <toolitem action='Zoom2:1' />"
322         "    <separator />"
323         "    <toolitem action='ZoomSelection' />"
324         "    <toolitem action='ZoomDrawing' />"
325         "    <toolitem action='ZoomPage' />"
326         "    <toolitem action='ZoomPageWidth' />"
327         "    <separator />"
328         "    <toolitem action='ZoomPrev' />"
329         "    <toolitem action='ZoomNext' />"
330         "  </toolbar>"
332         "  <toolbar name='StarToolbar'>"
333         "    <separator />"
334         "    <toolitem action='StarStateAction' />"
335         "    <separator />"
336         "    <toolitem action='FlatAction' />"
337         "    <separator />"
338         "    <toolitem action='MagnitudeAction' />"
339         "    <toolitem action='SpokeAction' />"
340         "    <toolitem action='RoundednessAction' />"
341         "    <toolitem action='RandomizationAction' />"
342         "    <separator />"
343         "    <toolitem action='StarResetAction' />"
344         "  </toolbar>"
346         "  <toolbar name='RectToolbar'>"
347         "    <toolitem action='RectStateAction' />"
348         "    <toolitem action='RectWidthAction' />"
349         "    <toolitem action='RectHeightAction' />"
350         "    <toolitem action='RadiusXAction' />"
351         "    <toolitem action='RadiusYAction' />"
352         "    <toolitem action='RectUnitsAction' />"
353         "    <separator />"
354         "    <toolitem action='RectResetAction' />"
355         "  </toolbar>"
357         "  <toolbar name='3DBoxToolbar'>"
358         "    <toolitem action='3DBoxAngleXAction' />"
359         "    <toolitem action='3DBoxVPXStateAction' />"
360         "    <separator />"
361         "    <toolitem action='3DBoxAngleYAction' />"
362         "    <toolitem action='3DBoxVPYStateAction' />"
363         "    <separator />"
364         "    <toolitem action='3DBoxAngleZAction' />"
365         "    <toolitem action='3DBoxVPZStateAction' />"
366         "  </toolbar>"
368         "  <toolbar name='SpiralToolbar'>"
369         "    <toolitem action='SpiralStateAction' />"
370         "    <toolitem action='SpiralRevolutionAction' />"
371         "    <toolitem action='SpiralExpansionAction' />"
372         "    <toolitem action='SpiralT0Action' />"
373         "    <separator />"
374         "    <toolitem action='SpiralResetAction' />"
375         "  </toolbar>"
377         "  <toolbar name='PenToolbar'>"
378         "    <toolitem action='FreehandModeActionPen' />"
379         "    <separator />"
380         "    <toolitem action='SetPenShapeAction'/>"
381         "  </toolbar>"
383         "  <toolbar name='PencilToolbar'>"
384         "    <toolitem action='FreehandModeActionPencil' />"
385         "    <separator />"
386         "    <toolitem action='PencilToleranceAction' />"
387         "    <separator />"
388         "    <toolitem action='PencilResetAction' />"
389         "    <separator />"
390         "    <toolitem action='SetPencilShapeAction'/>"
391         "  </toolbar>"
393         "  <toolbar name='CalligraphyToolbar'>"
394         "    <separator />"
395         "    <toolitem action='SetProfileAction'/>"
396         "    <separator />"
397         "    <toolitem action='CalligraphyWidthAction' />"
398         "    <toolitem action='PressureAction' />"
399         "    <toolitem action='TraceAction' />"
400         "    <toolitem action='ThinningAction' />"
401         "    <separator />"
402         "    <toolitem action='AngleAction' />"
403         "    <toolitem action='TiltAction' />"
404         "    <toolitem action='FixationAction' />"
405         "    <separator />"
406         "    <toolitem action='CapRoundingAction' />"
407         "    <separator />"
408         "    <toolitem action='TremorAction' />"
409         "    <toolitem action='WiggleAction' />"
410         "    <toolitem action='MassAction' />"
411         "    <separator />"
412         "  </toolbar>"
414         "  <toolbar name='ArcToolbar'>"
415         "    <toolitem action='ArcStateAction' />"
416         "    <separator />"
417         "    <toolitem action='ArcStartAction' />"
418         "    <toolitem action='ArcEndAction' />"
419         "    <separator />"
420         "    <toolitem action='ArcOpenAction' />"
421         "    <separator />"
422         "    <toolitem action='ArcResetAction' />"
423         "    <separator />"
424         "  </toolbar>"
426         "  <toolbar name='PaintbucketToolbar'>"
427         "    <toolitem action='ChannelsAction' />"
428         "    <separator />"
429         "    <toolitem action='ThresholdAction' />"
430         "    <separator />"
431         "    <toolitem action='OffsetAction' />"
432         "    <toolitem action='PaintbucketUnitsAction' />"
433         "    <separator />"
434         "    <toolitem action='AutoGapAction' />"
435         "    <separator />"
436         "    <toolitem action='PaintbucketResetAction' />"
437         "  </toolbar>"
439         "  <toolbar name='EraserToolbar'>"
440         "    <toolitem action='EraserWidthAction' />"
441         "    <separator />"
442         "    <toolitem action='EraserModeAction' />"
443         "  </toolbar>"
445         "  <toolbar name='LPEToolToolbar'>"
446         "    <toolitem action='LPEToolModeAction' />"
447         "    <separator />"
448         "    <toolitem action='LPEShowBBoxAction' />"
449         "    <toolitem action='LPEBBoxFromSelectionAction' />"
450         "    <separator />"
451         "    <toolitem action='LPELineSegmentAction' />"
452         "    <separator />"
453         "    <toolitem action='LPEMeasuringAction' />"
454         "    <toolitem action='LPEToolUnitsAction' />"
455         "    <separator />"
456         "    <toolitem action='LPEOpenLPEDialogAction' />"
457         "  </toolbar>"
459         "  <toolbar name='DropperToolbar'>"
460         "    <toolitem action='DropperOpacityAction' />"
461         "    <toolitem action='DropperPickAlphaAction' />"
462         "    <toolitem action='DropperSetAlphaAction' />"
463         "  </toolbar>"
465         "  <toolbar name='ConnectorToolbar'>"
466         "    <toolitem action='ConnectorAvoidAction' />"
467         "    <toolitem action='ConnectorIgnoreAction' />"
468         "    <toolitem action='ConnectorSpacingAction' />"
469         "    <toolitem action='ConnectorGraphAction' />"
470         "    <toolitem action='ConnectorLengthAction' />"
471         "    <toolitem action='ConnectorDirectedAction' />"
472         "    <toolitem action='ConnectorOverlapAction' />"
473         "  </toolbar>"
475         "</ui>"
478 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
480 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
482 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
483 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
485 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
486 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
488 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
489 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
492 GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick( GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
493                                                               Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
494                                                               Inkscape::UI::View::View *view, GtkTooltips *tt);
496 class VerbAction : public Gtk::Action {
497 public:
498     static Glib::RefPtr<VerbAction> create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
500     virtual ~VerbAction();
501     virtual void set_active(bool active = true);
503 protected:
504     virtual Gtk::Widget* create_menu_item_vfunc();
505     virtual Gtk::Widget* create_tool_item_vfunc();
507     virtual void connect_proxy_vfunc(Gtk::Widget* proxy);
508     virtual void disconnect_proxy_vfunc(Gtk::Widget* proxy);
510     virtual void on_activate();
512 private:
513     Inkscape::Verb* verb;
514     Inkscape::Verb* verb2;
515     Inkscape::UI::View::View *view;
516     GtkTooltips *tooltips;
517     bool active;
519     VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
520 };
523 Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips)
525     Glib::RefPtr<VerbAction> result;
526     SPAction *action = verb->get_action(view);
527     if ( action ) {
528         //SPAction* action2 = verb2 ? verb2->get_action(view) : 0;
529         result = Glib::RefPtr<VerbAction>(new VerbAction(verb, verb2, view, tooltips));
530     }
532     return result;
535 VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips) :
536     Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(verb->get_image()), Glib::ustring(_(verb->get_name())), Glib::ustring(_(verb->get_tip()))),
537     verb(verb),
538     verb2(verb2),
539     view(view),
540     tooltips(tooltips),
541     active(false)
545 VerbAction::~VerbAction()
549 Gtk::Widget* VerbAction::create_menu_item_vfunc()
551 // First call in to get the icon rendered if present in SVG
552     Gtk::Widget *widget = sp_icon_get_icon( property_stock_id().get_value().get_string(), Inkscape::ICON_SIZE_MENU );
553     delete widget;
554     widget = 0;
556     Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
557 //     g_message("create_menu_item_vfunc() = %p  for '%s'", widg, verb->get_id());
558     return widg;
561 Gtk::Widget* VerbAction::create_tool_item_vfunc()
563 //     Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
564     Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
565     GtkWidget* toolbox = 0;
566     GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
567                                                                           SP_BUTTON_TYPE_TOGGLE,
568                                                                           verb,
569                                                                           verb2,
570                                                                           view,
571                                                                           tooltips );
572     if ( active ) {
573         sp_button_toggle_set_down( SP_BUTTON(button), active);
574     }
575     gtk_widget_show_all( button );
576     Gtk::Widget* wrapped = Glib::wrap(button);
577     Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
578     holder->add(*wrapped);
580 //     g_message("create_tool_item_vfunc() = %p  for '%s'", holder, verb->get_id());
581     return holder;
584 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
586 //     g_message("connect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
587     Gtk::Action::connect_proxy_vfunc(proxy);
590 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
592 //     g_message("disconnect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
593     Gtk::Action::disconnect_proxy_vfunc(proxy);
596 void VerbAction::set_active(bool active)
598     this->active = active;
599     Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
600     for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
601         Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
602         if (ti) {
603             // *should* have one child that is the SPButton
604             Gtk::Widget* child = ti->get_child();
605             if ( child && SP_IS_BUTTON(child->gobj()) ) {
606                 SPButton* button = SP_BUTTON(child->gobj());
607                 sp_button_toggle_set_down( button, active );
608             }
609         }
610     }
613 void VerbAction::on_activate()
615     if ( verb ) {
616         SPAction *action = verb->get_action(view);
617         if ( action ) {
618             sp_action_perform(action, 0);
619         }
620     }
623 /* Global text entry widgets necessary for update */
624 /* GtkWidget *dropper_rgb_entry,
625           *dropper_opacity_entry ; */
626 // should be made a private member once this is converted to class
628 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
629     connection->disconnect();
630     delete connection;
633 static void purge_repr_listener( GObject* obj, GObject* tbl )
635     (void)obj;
636     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
637     if (oldrepr) { // remove old listener
638         sp_repr_remove_listener_by_data(oldrepr, tbl);
639         Inkscape::GC::release(oldrepr);
640         oldrepr = 0;
641         g_object_set_data( tbl, "repr", NULL );
642     }
645 GtkWidget *
646 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
647                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
648                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
650     SPAction *action = verb->get_action(view);
651     if (!action) return NULL;
653     SPAction *doubleclick_action;
654     if (doubleclick_verb)
655         doubleclick_action = doubleclick_verb->get_action(view);
656     else
657         doubleclick_action = NULL;
659     /* fixme: Handle sensitive/unsensitive */
660     /* fixme: Implement sp_button_new_from_action */
661     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
662     gtk_widget_show(b);
665     unsigned int shortcut = sp_shortcut_get_primary(verb);
666     if (shortcut) {
667         gchar key[256];
668         sp_ui_shortcut_string(shortcut, key);
669         gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
670         if ( t ) {
671             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
672         }
673         g_free(tip);
674     } else {
675         if ( t ) {
676             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
677         }
678     }
680     return b;
684 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
686     SPAction* targetAction = SP_ACTION(user_data);
687     if ( targetAction ) {
688         sp_action_perform( targetAction, NULL );
689     }
692 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
694     if ( data ) {
695         GtkAction* act = GTK_ACTION(data);
696         gtk_action_set_sensitive( act, sensitive );
697     }
700 static SPActionEventVector action_event_vector = {
701     {NULL},
702     NULL,
703     NULL,
704     sp_action_action_set_sensitive,
705     NULL,
706     NULL
707 };
709 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
711     GtkAction* act = 0;
713     SPAction* targetAction = verb->get_action(view);
714     InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size  );
715     act = GTK_ACTION(inky);
716     gtk_action_set_sensitive( act, targetAction->sensitive );
718     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
720     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
721     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
723     return act;
726 Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
728     Inkscape::UI::View::View *view = desktop;
729     gint verbsToUse[] = {
730         // disabled until we have icons for them:
731         //find
732         //SP_VERB_EDIT_TILE,
733         //SP_VERB_EDIT_UNTILE,
734         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
735         SP_VERB_DIALOG_DISPLAY,
736         SP_VERB_DIALOG_FILL_STROKE,
737         SP_VERB_DIALOG_NAMEDVIEW,
738         SP_VERB_DIALOG_TEXT,
739         SP_VERB_DIALOG_XML_EDITOR,
740         SP_VERB_EDIT_CLONE,
741         SP_VERB_EDIT_COPY,
742         SP_VERB_EDIT_CUT,
743         SP_VERB_EDIT_DUPLICATE,
744         SP_VERB_EDIT_PASTE,
745         SP_VERB_EDIT_REDO,
746         SP_VERB_EDIT_UNDO,
747         SP_VERB_EDIT_UNLINK_CLONE,
748         SP_VERB_FILE_EXPORT,
749         SP_VERB_FILE_IMPORT,
750         SP_VERB_FILE_NEW,
751         SP_VERB_FILE_OPEN,
752         SP_VERB_FILE_PRINT,
753         SP_VERB_FILE_SAVE,
754         SP_VERB_OBJECT_TO_CURVE,
755         SP_VERB_SELECTION_GROUP,
756         SP_VERB_SELECTION_OUTLINE,
757         SP_VERB_SELECTION_UNGROUP,
758         SP_VERB_ZOOM_1_1,
759         SP_VERB_ZOOM_1_2,
760         SP_VERB_ZOOM_2_1,
761         SP_VERB_ZOOM_DRAWING,
762         SP_VERB_ZOOM_IN,
763         SP_VERB_ZOOM_NEXT,
764         SP_VERB_ZOOM_OUT,
765         SP_VERB_ZOOM_PAGE,
766         SP_VERB_ZOOM_PAGE_WIDTH,
767         SP_VERB_ZOOM_PREV,
768         SP_VERB_ZOOM_SELECTION,
769     };
771     Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
773     static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
774     Glib::RefPtr<Gtk::ActionGroup> mainActions;
775     if ( groups.find(desktop) != groups.end() ) {
776         mainActions = groups[desktop];
777     }
779     if ( !mainActions ) {
780         mainActions = Gtk::ActionGroup::create("main");
781         groups[desktop] = mainActions;
782     }
784     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
785         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
786         if ( verb ) {
787             if (!mainActions->get_action(verb->get_id())) {
788                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
789                 mainActions->add(Glib::wrap(act));
790             }
791         }
792     }
794     if ( !mainActions->get_action("ToolZoom") ) {
795         GtkTooltips *tt = gtk_tooltips_new();
796         for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
797             Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
798             if ( va ) {
799                 mainActions->add(va);
800                 if ( i == 0 ) {
801                     va->set_active(true);
802                 }
803             }
804         }
805     }
808     return mainActions;
812 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
814     gtk_widget_set_size_request( widget,
815                                  widget->allocation.width,
816                                  widget->allocation.height );
819 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
821     gtk_widget_set_size_request( widget, -1, -1 );
826 GtkWidget *
827 sp_tool_toolbox_new()
829     GtkTooltips *tt = gtk_tooltips_new();
830     GtkWidget* tb = gtk_toolbar_new();
831     gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
832     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
834     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
835     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
837     gtk_widget_set_sensitive(tb, FALSE);
839     GtkWidget *hb = gtk_handle_box_new();
840     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
841     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
842     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
844     gtk_container_add(GTK_CONTAINER(hb), tb);
845     gtk_widget_show(GTK_WIDGET(tb));
847     sigc::connection* conn = new sigc::connection;
848     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
850     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
851     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
853     return hb;
856 GtkWidget *
857 sp_aux_toolbox_new()
859     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
861     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
863     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
865     gtk_widget_set_sensitive(tb, FALSE);
867     GtkWidget *hb = gtk_handle_box_new();
868     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
869     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
870     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
872     gtk_container_add(GTK_CONTAINER(hb), tb);
873     gtk_widget_show(GTK_WIDGET(tb));
875     sigc::connection* conn = new sigc::connection;
876     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
878     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
879     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
881     return hb;
884 //####################################
885 //# Commands Bar
886 //####################################
888 GtkWidget *
889 sp_commands_toolbox_new()
891     GtkWidget *tb = gtk_toolbar_new();
893     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
894     gtk_widget_set_sensitive(tb, FALSE);
896     GtkWidget *hb = gtk_handle_box_new();
897     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
898     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
899     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
901     gtk_container_add(GTK_CONTAINER(hb), tb);
902     gtk_widget_show(GTK_WIDGET(tb));
904     sigc::connection* conn = new sigc::connection;
905     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
907     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
908     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
910     return hb;
914 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
915                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
916                                                        gchar const *path, gchar const *data, gdouble def,
917                                                        GtkWidget *focusTarget,
918                                                        GtkWidget *us,
919                                                        GObject *dataKludge,
920                                                        gboolean altx, gchar const *altx_mark,
921                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
922                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
923                                                        void (*callback)(GtkAdjustment *, GObject *),
924                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
926     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
927                                                              lower, upper, step, page, page ) );
928     if (us) {
929         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
930     }
932     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
934     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
935     if ( shortLabel ) {
936         g_object_set( act, "short_label", shortLabel, NULL );
937     }
939     if ( (descrCount > 0) && descrLabels && descrValues ) {
940         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
941     }
943     if ( focusTarget ) {
944         ege_adjustment_action_set_focuswidget( act, focusTarget );
945     }
947     if ( altx && altx_mark ) {
948         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
949     }
951     if ( dataKludge ) {
952         g_object_set_data( dataKludge, data, adj );
953     }
955     // Using a cast just to make sure we pass in the right kind of function pointer
956     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
958     return act;
962 //####################################
963 //# node editing callbacks
964 //####################################
966 /**
967  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
968  */
969 static ShapeEditor *get_current_shape_editor()
971     if (!SP_ACTIVE_DESKTOP) {
972         return NULL;
973     }
975     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
977     if (!SP_IS_NODE_CONTEXT(event_context)) {
978         return NULL;
979     }
981     return SP_NODE_CONTEXT(event_context)->shape_editor;
985 void
986 sp_node_path_edit_add(void)
988     ShapeEditor *shape_editor = get_current_shape_editor();
989     if (shape_editor) shape_editor->add_node();
992 void
993 sp_node_path_edit_delete(void)
995     ShapeEditor *shape_editor = get_current_shape_editor();
996     if (shape_editor) shape_editor->delete_nodes_preserving_shape();
999 void
1000 sp_node_path_edit_delete_segment(void)
1002     ShapeEditor *shape_editor = get_current_shape_editor();
1003     if (shape_editor) shape_editor->delete_segment();
1006 void
1007 sp_node_path_edit_break(void)
1009     ShapeEditor *shape_editor = get_current_shape_editor();
1010     if (shape_editor) shape_editor->break_at_nodes();
1013 void
1014 sp_node_path_edit_join(void)
1016     ShapeEditor *shape_editor = get_current_shape_editor();
1017     if (shape_editor) shape_editor->join_nodes();
1020 void
1021 sp_node_path_edit_join_segment(void)
1023     ShapeEditor *shape_editor = get_current_shape_editor();
1024     if (shape_editor) shape_editor->join_segments();
1027 void
1028 sp_node_path_edit_toline(void)
1030     ShapeEditor *shape_editor = get_current_shape_editor();
1031     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1034 void
1035 sp_node_path_edit_tocurve(void)
1037     ShapeEditor *shape_editor = get_current_shape_editor();
1038     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1041 void
1042 sp_node_path_edit_cusp(void)
1044     ShapeEditor *shape_editor = get_current_shape_editor();
1045     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1048 void
1049 sp_node_path_edit_smooth(void)
1051     ShapeEditor *shape_editor = get_current_shape_editor();
1052     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1055 void
1056 sp_node_path_edit_symmetrical(void)
1058     ShapeEditor *shape_editor = get_current_shape_editor();
1059     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1062 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1063     bool show = gtk_toggle_action_get_active( act );
1064     prefs_set_int_attribute ("tools.nodes", "show_handles",  show ? 1 : 0);
1065     ShapeEditor *shape_editor = get_current_shape_editor();
1066     if (shape_editor) shape_editor->show_handles(show);
1069 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1070     bool show = gtk_toggle_action_get_active( act );
1071     prefs_set_int_attribute ("tools.nodes", "show_helperpath",  show ? 1 : 0);
1072     ShapeEditor *shape_editor = get_current_shape_editor();
1073     if (shape_editor) shape_editor->show_helperpath(show);
1076 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1077     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1080 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1081     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1084 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1085     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1088 /* is called when the node selection is modified */
1089 static void
1090 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1092     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1093     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1094     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1095     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1097     // quit if run by the attr_changed listener
1098     if (g_object_get_data( tbl, "freeze" )) {
1099         return;
1100     }
1102     // in turn, prevent listener from responding
1103     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1105     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1106     SPUnit const *unit = tracker->getActiveUnit();
1108     ShapeEditor *shape_editor = get_current_shape_editor();
1109     if (shape_editor && shape_editor->has_nodepath()) {
1110         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1111         int n_selected = 0;
1112         if (nodepath) {
1113             n_selected = nodepath->numSelected();
1114         }
1116         if (n_selected == 0) {
1117             gtk_action_set_sensitive(xact, FALSE);
1118             gtk_action_set_sensitive(yact, FALSE);
1119         } else {
1120             gtk_action_set_sensitive(xact, TRUE);
1121             gtk_action_set_sensitive(yact, TRUE);
1122             Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1123             Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1125             if (n_selected == 1) {
1126                 Geom::Point sel_node = nodepath->singleSelectedCoords();
1127                 if (oldx != sel_node[Geom::X] || oldy != sel_node[Geom::Y]) {
1128                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[Geom::X], *unit));
1129                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[Geom::Y], *unit));
1130                 }
1131             } else {
1132                 boost::optional<Geom::Coord> x = sp_node_selected_common_coord(nodepath, Geom::X);
1133                 boost::optional<Geom::Coord> y = sp_node_selected_common_coord(nodepath, Geom::Y);
1134                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1135                     /* Note: Currently x and y will always have a value, even if the coordinates of the
1136                        selected nodes don't coincide (in this case we use the coordinates of the center
1137                        of the bounding box). So the entries are never set to zero. */
1138                     // FIXME: Maybe we should clear the entry if several nodes are selected
1139                     //        instead of providing a kind of average value
1140                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1141                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1142                 }
1143             }
1144         }
1145     } else {
1146         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1147         gtk_action_set_sensitive(xact, FALSE);
1148         gtk_action_set_sensitive(yact, FALSE);
1149     }
1151     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1154 static void
1155 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1157     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1159     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1160     SPUnit const *unit = tracker->getActiveUnit();
1162     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1163         prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
1164     }
1166     // quit if run by the attr_changed listener
1167     if (g_object_get_data( tbl, "freeze" )) {
1168         return;
1169     }
1171     // in turn, prevent listener from responding
1172     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1174     ShapeEditor *shape_editor = get_current_shape_editor();
1175     if (shape_editor && shape_editor->has_nodepath()) {
1176         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1177         if (!strcmp(value_name, "x")) {
1178             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::X);
1179         }
1180         if (!strcmp(value_name, "y")) {
1181             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::Y);
1182         }
1183     }
1185     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1188 static void
1189 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1191     sp_node_path_value_changed(adj, tbl, "x");
1194 static void
1195 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1197     sp_node_path_value_changed(adj, tbl, "y");
1200 void
1201 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1203     {
1204     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1205     SPItem *item = selection->singleItem();
1206     if (item && SP_IS_LPE_ITEM(item)) {
1207        if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1208            gtk_action_set_sensitive(w, TRUE);
1209        } else {
1210            gtk_action_set_sensitive(w, FALSE);
1211        }
1212     } else {
1213        gtk_action_set_sensitive(w, FALSE);
1214     }
1215     }
1217     {
1218     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1219     SPItem *item = selection->singleItem();
1220     if (item && item->clip_ref && item->clip_ref->getObject()) {
1221        gtk_action_set_sensitive(w, TRUE);
1222     } else {
1223        gtk_action_set_sensitive(w, FALSE);
1224     }
1225     }
1227     {
1228     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1229     SPItem *item = selection->singleItem();
1230     if (item && item->mask_ref && item->mask_ref->getObject()) {
1231        gtk_action_set_sensitive(w, TRUE);
1232     } else {
1233        gtk_action_set_sensitive(w, FALSE);
1234     }
1235     }
1238 void
1239 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1241     sp_node_toolbox_sel_changed (selection, tbl);
1246 //################################
1247 //##    Node Editing Toolbox    ##
1248 //################################
1250 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1252     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1253     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1254     g_object_set_data( holder, "tracker", tracker );
1256     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
1258     {
1259         InkAction* inky = ink_action_new( "NodeInsertAction",
1260                                           _("Insert node"),
1261                                           _("Insert new nodes into selected segments"),
1262                                           "node_insert",
1263                                           secondarySize );
1264         g_object_set( inky, "short_label", _("Insert"), NULL );
1265         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1266         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1267     }
1269     {
1270         InkAction* inky = ink_action_new( "NodeDeleteAction",
1271                                           _("Delete node"),
1272                                           _("Delete selected nodes"),
1273                                           "node_delete",
1274                                           secondarySize );
1275         g_object_set( inky, "short_label", _("Delete"), NULL );
1276         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1277         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1278     }
1280     {
1281         InkAction* inky = ink_action_new( "NodeJoinAction",
1282                                           _("Join endnodes"),
1283                                           _("Join selected endnodes"),
1284                                           "node_join",
1285                                           secondarySize );
1286         g_object_set( inky, "short_label", _("Join"), NULL );
1287         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1288         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1289     }
1291     {
1292         InkAction* inky = ink_action_new( "NodeBreakAction",
1293                                           _("Break nodes"),
1294                                           _("Break path at selected nodes"),
1295                                           "node_break",
1296                                           secondarySize );
1297         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1298         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1299     }
1302     {
1303         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1304                                           _("Join with segment"),
1305                                           _("Join selected endnodes with a new segment"),
1306                                           "node_join_segment",
1307                                           secondarySize );
1308         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1309         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1310     }
1312     {
1313         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1314                                           _("Delete segment"),
1315                                           _("Delete segment between two non-endpoint nodes"),
1316                                           "node_delete_segment",
1317                                           secondarySize );
1318         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1319         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1320     }
1322     {
1323         InkAction* inky = ink_action_new( "NodeCuspAction",
1324                                           _("Node Cusp"),
1325                                           _("Make selected nodes corner"),
1326                                           "node_cusp",
1327                                           secondarySize );
1328         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1329         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1330     }
1332     {
1333         InkAction* inky = ink_action_new( "NodeSmoothAction",
1334                                           _("Node Smooth"),
1335                                           _("Make selected nodes smooth"),
1336                                           "node_smooth",
1337                                           secondarySize );
1338         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1339         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1340     }
1342     {
1343         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1344                                           _("Node Symmetric"),
1345                                           _("Make selected nodes symmetric"),
1346                                           "node_symmetric",
1347                                           secondarySize );
1348         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1349         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1350     }
1352     {
1353         InkAction* inky = ink_action_new( "NodeLineAction",
1354                                           _("Node Line"),
1355                                           _("Make selected segments lines"),
1356                                           "node_line",
1357                                           secondarySize );
1358         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1359         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1360     }
1362     {
1363         InkAction* inky = ink_action_new( "NodeCurveAction",
1364                                           _("Node Curve"),
1365                                           _("Make selected segments curves"),
1366                                           "node_curve",
1367                                           secondarySize );
1368         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1369         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1370     }
1372     {
1373         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1374                                                       _("Show Handles"),
1375                                                       _("Show the Bezier handles of selected nodes"),
1376                                                       "nodes_show_handles",
1377                                                       Inkscape::ICON_SIZE_DECORATION );
1378         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1379         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1380         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1381     }
1383     {
1384         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1385                                                       _("Show Outline"),
1386                                                       _("Show the outline of the path"),
1387                                                       "nodes_show_helperpath",
1388                                                       Inkscape::ICON_SIZE_DECORATION );
1389         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1390         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1391         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_helperpath", 0 ) );
1392     }
1394     {
1395         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1396                                           _("Next path effect parameter"),
1397                                           _("Show next path effect parameter for editing"),
1398                                           "edit_next_parameter",
1399                                           Inkscape::ICON_SIZE_DECORATION );
1400         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1401         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1402         g_object_set_data( holder, "nodes_lpeedit", inky);
1403     }
1405     {
1406         InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1407                                           _("Edit clipping path"),
1408                                           _("Edit the clipping path of the object"),
1409                                           "nodeedit-clippath",
1410                                           Inkscape::ICON_SIZE_DECORATION );
1411         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1412         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1413         g_object_set_data( holder, "nodes_clippathedit", inky);
1414     }
1416     {
1417         InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1418                                           _("Edit mask path"),
1419                                           _("Edit the mask of the object"),
1420                                           "nodeedit-mask",
1421                                           Inkscape::ICON_SIZE_DECORATION );
1422         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1423         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1424         g_object_set_data( holder, "nodes_maskedit", inky);
1425     }
1427     /* X coord of selected node(s) */
1428     {
1429         EgeAdjustmentAction* eact = 0;
1430         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1431         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1432         eact = create_adjustment_action( "NodeXAction",
1433                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1434                                          "tools.nodes", "Xcoord", 0,
1435                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1436                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1437                                          labels, values, G_N_ELEMENTS(labels),
1438                                          sp_node_path_x_value_changed );
1439         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1440         g_object_set_data( holder, "nodes_x_action", eact );
1441         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1442         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1443     }
1445     /* Y coord of selected node(s) */
1446     {
1447         EgeAdjustmentAction* eact = 0;
1448         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1449         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1450         eact = create_adjustment_action( "NodeYAction",
1451                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1452                                          "tools.nodes", "Ycoord", 0,
1453                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1454                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1455                                          labels, values, G_N_ELEMENTS(labels),
1456                                          sp_node_path_y_value_changed );
1457         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1458         g_object_set_data( holder, "nodes_y_action", eact );
1459         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1460         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1461     }
1463     // add the units menu
1464     {
1465         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1466         gtk_action_group_add_action( mainActions, act );
1467     }
1470     sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1472     //watch selection
1473     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1475     sigc::connection *c_selection_changed =
1476         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1477                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1478     pool->add_connection ("selection-changed", c_selection_changed);
1480     sigc::connection *c_selection_modified =
1481         new sigc::connection (sp_desktop_selection (desktop)->connectModified
1482                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1483     pool->add_connection ("selection-modified", c_selection_modified);
1485     sigc::connection *c_subselection_changed =
1486         new sigc::connection (desktop->connectToolSubselectionChanged
1487                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1488     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1490     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1492     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1493 } // end of sp_node_toolbox_prep()
1496 //########################
1497 //##    Zoom Toolbox    ##
1498 //########################
1500 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1502     // no custom GtkAction setup needed
1503 } // end of sp_zoom_toolbox_prep()
1505 void
1506 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1508     toolbox_set_desktop(toolbox,
1509                         desktop,
1510                         setup_tool_toolbox,
1511                         update_tool_toolbox,
1512                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1513                                                                          "event_context_connection")));
1517 void
1518 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1520     toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1521                         desktop,
1522                         setup_aux_toolbox,
1523                         update_aux_toolbox,
1524                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1525                                                                          "event_context_connection")));
1528 void
1529 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1531     toolbox_set_desktop(toolbox,
1532                         desktop,
1533                         setup_commands_toolbox,
1534                         update_commands_toolbox,
1535                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1536                                                                          "event_context_connection")));
1539 static void
1540 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1542     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1543     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1545     if (old_desktop) {
1546         GList *children, *iter;
1548         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1549         for ( iter = children ; iter ; iter = iter->next ) {
1550             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1551         }
1552         g_list_free(children);
1553     }
1555     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1557     if (desktop) {
1558         gtk_widget_set_sensitive(toolbox, TRUE);
1559         setup_func(toolbox, desktop);
1560         update_func(desktop, desktop->event_context, toolbox);
1561         *conn = desktop->connectEventContextChanged
1562             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1563     } else {
1564         gtk_widget_set_sensitive(toolbox, FALSE);
1565     }
1567 } // end of toolbox_set_desktop()
1570 static void
1571 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1573     gchar const * descr =
1574         "<ui>"
1575         "  <toolbar name='ToolToolbar'>"
1576         "    <toolitem action='ToolSelector' />"
1577         "    <toolitem action='ToolNode' />"
1578         "    <toolitem action='ToolTweak' />"
1579         "    <toolitem action='ToolZoom' />"
1580         "    <toolitem action='ToolRect' />"
1581         "    <toolitem action='Tool3DBox' />"
1582         "    <toolitem action='ToolArc' />"
1583         "    <toolitem action='ToolStar' />"
1584         "    <toolitem action='ToolSpiral' />"
1585         "    <toolitem action='ToolPencil' />"
1586         "    <toolitem action='ToolPen' />"
1587         "    <toolitem action='ToolCalligraphic' />"
1588         "    <toolitem action='ToolEraser' />"
1589 //        "    <toolitem action='ToolLPETool' />"
1590         "    <toolitem action='ToolPaintBucket' />"
1591         "    <toolitem action='ToolText' />"
1592         "    <toolitem action='ToolConnector' />"
1593         "    <toolitem action='ToolGradient' />"
1594         "    <toolitem action='ToolDropper' />"
1595         "  </toolbar>"
1596         "</ui>";
1597     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1598     GtkUIManager* mgr = gtk_ui_manager_new();
1599     GError* errVal = 0;
1601     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1602     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1604     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" );
1605     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1606         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1607     }
1608     Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1609     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1611     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
1612     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1614     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1616     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1617     if ( child ) {
1618         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1619     }
1621     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1622 //     Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1626 static void
1627 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1629     gchar const *const tname = ( eventcontext
1630                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1631                                  : NULL );
1632     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1634     for (int i = 0 ; tools[i].type_name ; i++ ) {
1635         Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1636         if ( act ) {
1637             bool setActive = tname && !strcmp(tname, tools[i].type_name);
1638             Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1639             if ( verbAct ) {
1640                 verbAct->set_active(setActive);
1641             }
1642         }
1643     }
1646 static void
1647 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1649     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1650     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1651     GtkUIManager* mgr = gtk_ui_manager_new();
1652     GError* errVal = 0;
1653     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1654     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1656     std::map<std::string, GtkWidget*> dataHolders;
1658     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1659         if ( aux_toolboxes[i].prep_func ) {
1660             // converted to GtkActions and UIManager
1662             GtkWidget* kludge = gtk_toolbar_new();
1663             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1664             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1665             dataHolders[aux_toolboxes[i].type_name] = kludge;
1666             aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1667         } else {
1669             GtkWidget *sub_toolbox = 0;
1670             if (aux_toolboxes[i].create_func == NULL)
1671                 sub_toolbox = sp_empty_toolbox_new(desktop);
1672             else {
1673                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1674             }
1676             gtk_size_group_add_widget( grouper, sub_toolbox );
1678             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1679             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1681         }
1682     }
1684     // Second pass to create toolbars *after* all GtkActions are created
1685     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1686         if ( aux_toolboxes[i].prep_func ) {
1687             // converted to GtkActions and UIManager
1689             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1691             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1692             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1694             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1695             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1696             g_free( tmp );
1697             tmp = 0;
1699             Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1700             if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1701                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1702             }
1703             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1706             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1708             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1709                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1710                 swatch->setDesktop( desktop );
1711                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1712                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1713                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1714                 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 );
1715             }
1717             gtk_widget_show_all( holder );
1718             sp_set_font_size_smaller( holder );
1720             gtk_size_group_add_widget( grouper, holder );
1722             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1723             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1724         }
1725     }
1727     g_object_unref( G_OBJECT(grouper) );
1730 static void
1731 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1733     gchar const *tname = ( eventcontext
1734                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1735                            : NULL );
1736     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1737         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1738         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1739             gtk_widget_show_all(sub_toolbox);
1740             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1741         } else {
1742             gtk_widget_hide(sub_toolbox);
1743         }
1744     }
1747 static void
1748 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1750     gchar const * descr =
1751         "<ui>"
1752         "  <toolbar name='CommandsToolbar'>"
1753         "    <toolitem action='FileNew' />"
1754         "    <toolitem action='FileOpen' />"
1755         "    <toolitem action='FileSave' />"
1756         "    <toolitem action='FilePrint' />"
1757         "    <separator />"
1758         "    <toolitem action='FileImport' />"
1759         "    <toolitem action='FileExport' />"
1760         "    <separator />"
1761         "    <toolitem action='EditUndo' />"
1762         "    <toolitem action='EditRedo' />"
1763         "    <separator />"
1764         "    <toolitem action='EditCopy' />"
1765         "    <toolitem action='EditCut' />"
1766         "    <toolitem action='EditPaste' />"
1767         "    <separator />"
1768         "    <toolitem action='ZoomSelection' />"
1769         "    <toolitem action='ZoomDrawing' />"
1770         "    <toolitem action='ZoomPage' />"
1771         "    <separator />"
1772         "    <toolitem action='EditDuplicate' />"
1773         "    <toolitem action='EditClone' />"
1774         "    <toolitem action='EditUnlinkClone' />"
1775         "    <separator />"
1776         "    <toolitem action='SelectionGroup' />"
1777         "    <toolitem action='SelectionUnGroup' />"
1778         "    <separator />"
1779         "    <toolitem action='DialogFillStroke' />"
1780         "    <toolitem action='DialogText' />"
1781         "    <toolitem action='DialogXMLEditor' />"
1782         "    <toolitem action='DialogAlignDistribute' />"
1783         "    <separator />"
1784         "    <toolitem action='DialogPreferences' />"
1785         "    <toolitem action='DialogDocumentProperties' />"
1786         "  </toolbar>"
1787         "</ui>";
1788     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1791     GtkUIManager* mgr = gtk_ui_manager_new();
1792     GError* errVal = 0;
1794     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1795     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1797     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1798     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1799         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1800     }
1802     Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1803     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1805     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1806     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1809     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1811     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1812     if ( child ) {
1813         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1814     }
1816     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1819 static void
1820 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1824 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1826     gtk_widget_show(toolbox_toplevel);
1827     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1829     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1830     if (!shown_toolbox) {
1831         return;
1832     }
1833     gtk_widget_show(toolbox);
1835     gtk_widget_show_all(shown_toolbox);
1838 static GtkWidget *
1839 sp_empty_toolbox_new(SPDesktop *desktop)
1841     GtkWidget *tbl = gtk_toolbar_new();
1842     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1843     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1845     gtk_widget_show_all(tbl);
1846     sp_set_font_size_smaller (tbl);
1848     return tbl;
1851 #define MODE_LABEL_WIDTH 70
1853 //########################
1854 //##       Star         ##
1855 //########################
1857 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1859     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1861     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1862         // do not remember prefs if this call is initiated by an undo change, because undoing object
1863         // creation sets bogus values to its attributes before it is deleted
1864         prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1865     }
1867     // quit if run by the attr_changed listener
1868     if (g_object_get_data( dataKludge, "freeze" )) {
1869         return;
1870     }
1872     // in turn, prevent listener from responding
1873     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1875     bool modmade = false;
1877     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1878     GSList const *items = selection->itemList();
1879     for (; items != NULL; items = items->next) {
1880         if (SP_IS_STAR((SPItem *) items->data)) {
1881             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1882             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1883             sp_repr_set_svg_double(repr, "sodipodi:arg2",
1884                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1885                                     + M_PI / (gint)adj->value));
1886             SP_OBJECT((SPItem *) items->data)->updateRepr();
1887             modmade = true;
1888         }
1889     }
1890     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1891                                    _("Star: Change number of corners"));
1893     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1896 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1898     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1900     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1901         prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1902     }
1904     // quit if run by the attr_changed listener
1905     if (g_object_get_data( dataKludge, "freeze" )) {
1906         return;
1907     }
1909     // in turn, prevent listener from responding
1910     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1912     bool modmade = false;
1913     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1914     GSList const *items = selection->itemList();
1915     for (; items != NULL; items = items->next) {
1916         if (SP_IS_STAR((SPItem *) items->data)) {
1917             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1919             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1920             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1921             if (r2 < r1) {
1922                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1923             } else {
1924                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1925             }
1927             SP_OBJECT((SPItem *) items->data)->updateRepr();
1928             modmade = true;
1929         }
1930     }
1932     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1933                                    _("Star: Change spoke ratio"));
1935     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1938 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1940     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1941     bool flat = ege_select_one_action_get_active( act ) == 0;
1943     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1944         prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1945                                     flat ? "true" : "false" );
1946     }
1948     // quit if run by the attr_changed listener
1949     if (g_object_get_data( dataKludge, "freeze" )) {
1950         return;
1951     }
1953     // in turn, prevent listener from responding
1954     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1956     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1957     GSList const *items = selection->itemList();
1958     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1959     bool modmade = false;
1961     if ( prop_action ) {
1962         gtk_action_set_sensitive( prop_action, !flat );
1963     }
1965     for (; items != NULL; items = items->next) {
1966         if (SP_IS_STAR((SPItem *) items->data)) {
1967             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1968             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1969             SP_OBJECT((SPItem *) items->data)->updateRepr();
1970             modmade = true;
1971         }
1972     }
1974     if (modmade) {
1975         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1976                          flat ? _("Make polygon") : _("Make star"));
1977     }
1979     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1982 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1984     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1986     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1987         prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1988     }
1990     // quit if run by the attr_changed listener
1991     if (g_object_get_data( dataKludge, "freeze" )) {
1992         return;
1993     }
1995     // in turn, prevent listener from responding
1996     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1998     bool modmade = false;
2000     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2001     GSList const *items = selection->itemList();
2002     for (; items != NULL; items = items->next) {
2003         if (SP_IS_STAR((SPItem *) items->data)) {
2004             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2005             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2006             SP_OBJECT(items->data)->updateRepr();
2007             modmade = true;
2008         }
2009     }
2010     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2011                                    _("Star: Change rounding"));
2013     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2016 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2018     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2020     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2021         prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
2022     }
2024     // quit if run by the attr_changed listener
2025     if (g_object_get_data( dataKludge, "freeze" )) {
2026         return;
2027     }
2029     // in turn, prevent listener from responding
2030     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2032     bool modmade = false;
2034     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2035     GSList const *items = selection->itemList();
2036     for (; items != NULL; items = items->next) {
2037         if (SP_IS_STAR((SPItem *) items->data)) {
2038             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2039             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2040             SP_OBJECT(items->data)->updateRepr();
2041             modmade = true;
2042         }
2043     }
2044     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2045                                    _("Star: Change randomization"));
2047     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2051 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2052                                        gchar const */*old_value*/, gchar const */*new_value*/,
2053                                        bool /*is_interactive*/, gpointer data)
2055     GtkWidget *tbl = GTK_WIDGET(data);
2057     // quit if run by the _changed callbacks
2058     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2059         return;
2060     }
2062     // in turn, prevent callbacks from responding
2063     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2065     GtkAdjustment *adj = 0;
2067     gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2068     bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2070     if (!strcmp(name, "inkscape:randomized")) {
2071         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2072         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2073     } else if (!strcmp(name, "inkscape:rounded")) {
2074         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2075         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2076     } else if (!strcmp(name, "inkscape:flatsided")) {
2077         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2078         char const *flatsides = repr->attribute("inkscape:flatsided");
2079         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2080         if ( flatsides && !strcmp(flatsides,"false") ) {
2081             ege_select_one_action_set_active( flat_action, 1 );
2082             gtk_action_set_sensitive( prop_action, TRUE );
2083         } else {
2084             ege_select_one_action_set_active( flat_action, 0 );
2085             gtk_action_set_sensitive( prop_action, FALSE );
2086         }
2087     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2088         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2089         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2090         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2091         if (r2 < r1) {
2092             gtk_adjustment_set_value(adj, r2/r1);
2093         } else {
2094             gtk_adjustment_set_value(adj, r1/r2);
2095         }
2096     } else if (!strcmp(name, "sodipodi:sides")) {
2097         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2098         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2099     }
2101     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2105 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2107     NULL, /* child_added */
2108     NULL, /* child_removed */
2109     star_tb_event_attr_changed,
2110     NULL, /* content_changed */
2111     NULL  /* order_changed */
2112 };
2115 /**
2116  *  \param selection Should not be NULL.
2117  */
2118 static void
2119 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2121     int n_selected = 0;
2122     Inkscape::XML::Node *repr = NULL;
2124     purge_repr_listener( tbl, tbl );
2126     for (GSList const *items = selection->itemList();
2127          items != NULL;
2128          items = items->next)
2129     {
2130         if (SP_IS_STAR((SPItem *) items->data)) {
2131             n_selected++;
2132             repr = SP_OBJECT_REPR((SPItem *) items->data);
2133         }
2134     }
2136     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2138     if (n_selected == 0) {
2139         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2140     } else if (n_selected == 1) {
2141         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2143         if (repr) {
2144             g_object_set_data( tbl, "repr", repr );
2145             Inkscape::GC::anchor(repr);
2146             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2147             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2148         }
2149     } else {
2150         // FIXME: implement averaging of all parameters for multiple selected stars
2151         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2152         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2153     }
2157 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2159     // FIXME: in this and all other _default functions, set some flag telling the value_changed
2160     // callbacks to lump all the changes for all selected objects in one undo step
2162     GtkAdjustment *adj = 0;
2164     // fixme: make settable in prefs!
2165     gint mag = 5;
2166     gdouble prop = 0.5;
2167     gboolean flat = FALSE;
2168     gdouble randomized = 0;
2169     gdouble rounded = 0;
2171     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2172     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2174     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2175     gtk_action_set_sensitive( sb2, !flat );
2177     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2178     gtk_adjustment_set_value(adj, mag);
2179     gtk_adjustment_value_changed(adj);
2181     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2182     gtk_adjustment_set_value(adj, prop);
2183     gtk_adjustment_value_changed(adj);
2185     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2186     gtk_adjustment_set_value(adj, rounded);
2187     gtk_adjustment_value_changed(adj);
2189     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2190     gtk_adjustment_set_value(adj, randomized);
2191     gtk_adjustment_value_changed(adj);
2195 void
2196 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2198     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2199     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2200     GtkWidget *l = gtk_label_new(NULL);
2201     gtk_label_set_markup(GTK_LABEL(l), title);
2202     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2203     if ( GTK_IS_TOOLBAR(tbl) ) {
2204         gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2205     } else {
2206         gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2207     }
2208     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2212 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2214     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2216     {
2217         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2218         ege_output_action_set_use_markup( act, TRUE );
2219         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2220         g_object_set_data( holder, "mode_action", act );
2221     }
2223     {
2224         EgeAdjustmentAction* eact = 0;
2225         gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2226         bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2228         /* Flatsided checkbox */
2229         {
2230             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2232             GtkTreeIter iter;
2233             gtk_list_store_append( model, &iter );
2234             gtk_list_store_set( model, &iter,
2235                                 0, _("Polygon"),
2236                                 1, _("Regular polygon (with one handle) instead of a star"),
2237                                 2, "star_flat",
2238                                 -1 );
2240             gtk_list_store_append( model, &iter );
2241             gtk_list_store_set( model, &iter,
2242                                 0, _("Star"),
2243                                 1, _("Star instead of a regular polygon (with one handle)"),
2244                                 2, "star_angled",
2245                                 -1 );
2247             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2248             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2249             g_object_set_data( holder, "flat_action", act );
2251             ege_select_one_action_set_appearance( act, "full" );
2252             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2253             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2254             ege_select_one_action_set_icon_column( act, 2 );
2255             ege_select_one_action_set_icon_size( act, secondarySize );
2256             ege_select_one_action_set_tooltip_column( act, 1  );
2258             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2259             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2260         }
2262         /* Magnitude */
2263         {
2264         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2265         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2266         eact = create_adjustment_action( "MagnitudeAction",
2267                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2268                                          "tools.shapes.star", "magnitude", 3,
2269                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2270                                          3, 1024, 1, 5,
2271                                          labels, values, G_N_ELEMENTS(labels),
2272                                          sp_stb_magnitude_value_changed,
2273                                          1.0, 0 );
2274         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2275         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2276         }
2278         /* Spoke ratio */
2279         {
2280         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2281         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2282         eact = create_adjustment_action( "SpokeAction",
2283                                          _("Spoke ratio"), _("Spoke ratio:"),
2284                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2285                                          // Base radius is the same for the closest handle.
2286                                          _("Base radius to tip radius ratio"),
2287                                          "tools.shapes.star", "proportion", 0.5,
2288                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2289                                          0.01, 1.0, 0.01, 0.1,
2290                                          labels, values, G_N_ELEMENTS(labels),
2291                                          sp_stb_proportion_value_changed );
2292         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2293         g_object_set_data( holder, "prop_action", eact );
2294         }
2296         if ( !isFlatSided ) {
2297             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2298         } else {
2299             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2300         }
2302         /* Roundedness */
2303         {
2304         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2305         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2306         eact = create_adjustment_action( "RoundednessAction",
2307                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2308                                          "tools.shapes.star", "rounded", 0.0,
2309                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2310                                          -10.0, 10.0, 0.01, 0.1,
2311                                          labels, values, G_N_ELEMENTS(labels),
2312                                          sp_stb_rounded_value_changed );
2313         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2314         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2315         }
2317         /* Randomization */
2318         {
2319         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2320         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2321         eact = create_adjustment_action( "RandomizationAction",
2322                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2323                                          "tools.shapes.star", "randomized", 0.0,
2324                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2325                                          -10.0, 10.0, 0.001, 0.01,
2326                                          labels, values, G_N_ELEMENTS(labels),
2327                                          sp_stb_randomized_value_changed, 0.1, 3 );
2328         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2329         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2330         }
2331     }
2333     {
2334         /* Reset */
2335         {
2336             GtkAction* act = gtk_action_new( "StarResetAction",
2337                                              _("Defaults"),
2338                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2339                                              GTK_STOCK_CLEAR );
2340             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2341             gtk_action_group_add_action( mainActions, act );
2342             gtk_action_set_sensitive( act, TRUE );
2343         }
2344     }
2346     sigc::connection *connection = new sigc::connection(
2347         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2348         );
2349     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2350     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2354 //########################
2355 //##       Rect         ##
2356 //########################
2358 static void sp_rtb_sensitivize( GObject *tbl )
2360     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2361     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2362     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2364     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2365         gtk_action_set_sensitive( not_rounded, FALSE );
2366     } else {
2367         gtk_action_set_sensitive( not_rounded, TRUE );
2368     }
2372 static void
2373 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2374                           void (*setter)(SPRect *, gdouble))
2376     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2378     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2379     SPUnit const *unit = tracker->getActiveUnit();
2381     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2382         prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2383     }
2385     // quit if run by the attr_changed listener
2386     if (g_object_get_data( tbl, "freeze" )) {
2387         return;
2388     }
2390     // in turn, prevent listener from responding
2391     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2393     bool modmade = false;
2394     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2395     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2396         if (SP_IS_RECT(items->data)) {
2397             if (adj->value != 0) {
2398                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2399             } else {
2400                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2401             }
2402             modmade = true;
2403         }
2404     }
2406     sp_rtb_sensitivize( tbl );
2408     if (modmade) {
2409         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2410                                    _("Change rectangle"));
2411     }
2413     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2416 static void
2417 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2419     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2422 static void
2423 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2425     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2428 static void
2429 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2431     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2434 static void
2435 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2437     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2442 static void
2443 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2445     GtkAdjustment *adj = 0;
2447     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2448     gtk_adjustment_set_value(adj, 0.0);
2449     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2450     gtk_adjustment_value_changed(adj);
2452     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2453     gtk_adjustment_set_value(adj, 0.0);
2454     gtk_adjustment_value_changed(adj);
2456     sp_rtb_sensitivize( obj );
2459 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2460                                        gchar const */*old_value*/, gchar const */*new_value*/,
2461                                        bool /*is_interactive*/, gpointer data)
2463     GObject *tbl = G_OBJECT(data);
2465     // quit if run by the _changed callbacks
2466     if (g_object_get_data( tbl, "freeze" )) {
2467         return;
2468     }
2470     // in turn, prevent callbacks from responding
2471     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2473     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2474     SPUnit const *unit = tracker->getActiveUnit();
2476     gpointer item = g_object_get_data( tbl, "item" );
2477     if (item && SP_IS_RECT(item)) {
2478         {
2479             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2480             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2481             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2482         }
2484         {
2485             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2486             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2487             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2488         }
2490         {
2491             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2492             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2493             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2494         }
2496         {
2497             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2498             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2499             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2500         }
2501     }
2503     sp_rtb_sensitivize( tbl );
2505     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2509 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2510     NULL, /* child_added */
2511     NULL, /* child_removed */
2512     rect_tb_event_attr_changed,
2513     NULL, /* content_changed */
2514     NULL  /* order_changed */
2515 };
2517 /**
2518  *  \param selection should not be NULL.
2519  */
2520 static void
2521 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2523     int n_selected = 0;
2524     Inkscape::XML::Node *repr = NULL;
2525     SPItem *item = NULL;
2527     if ( g_object_get_data( tbl, "repr" ) ) {
2528         g_object_set_data( tbl, "item", NULL );
2529     }
2530     purge_repr_listener( tbl, tbl );
2532     for (GSList const *items = selection->itemList();
2533          items != NULL;
2534          items = items->next) {
2535         if (SP_IS_RECT((SPItem *) items->data)) {
2536             n_selected++;
2537             item = (SPItem *) items->data;
2538             repr = SP_OBJECT_REPR(item);
2539         }
2540     }
2542     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2544     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2546     if (n_selected == 0) {
2547         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2549         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2550         gtk_action_set_sensitive(w, FALSE);
2551         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2552         gtk_action_set_sensitive(h, FALSE);
2554     } else if (n_selected == 1) {
2555         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2556         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2558         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2559         gtk_action_set_sensitive(w, TRUE);
2560         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2561         gtk_action_set_sensitive(h, TRUE);
2563         if (repr) {
2564             g_object_set_data( tbl, "repr", repr );
2565             g_object_set_data( tbl, "item", item );
2566             Inkscape::GC::anchor(repr);
2567             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2568             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2569         }
2570     } else {
2571         // FIXME: implement averaging of all parameters for multiple selected
2572         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2573         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2574         sp_rtb_sensitivize( tbl );
2575     }
2579 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2581     EgeAdjustmentAction* eact = 0;
2582     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2584     {
2585         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2586         ege_output_action_set_use_markup( act, TRUE );
2587         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2588         g_object_set_data( holder, "mode_action", act );
2589     }
2591     // rx/ry units menu: create
2592     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2593     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2594     // fixme: add % meaning per cent of the width/height
2595     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2596     g_object_set_data( holder, "tracker", tracker );
2598     /* W */
2599     {
2600         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2601         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2602         eact = create_adjustment_action( "RectWidthAction",
2603                                          _("Width"), _("W:"), _("Width of rectangle"),
2604                                          "tools.shapes.rect", "width", 0,
2605                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2606                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2607                                          labels, values, G_N_ELEMENTS(labels),
2608                                          sp_rtb_width_value_changed );
2609         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2610         g_object_set_data( holder, "width_action", eact );
2611         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2612         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2613     }
2615     /* H */
2616     {
2617         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2618         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2619         eact = create_adjustment_action( "RectHeightAction",
2620                                          _("Height"), _("H:"), _("Height of rectangle"),
2621                                          "tools.shapes.rect", "height", 0,
2622                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2623                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2624                                          labels, values, G_N_ELEMENTS(labels),
2625                                          sp_rtb_height_value_changed );
2626         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2627         g_object_set_data( holder, "height_action", eact );
2628         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2629         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2630     }
2632     /* rx */
2633     {
2634         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2635         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2636         eact = create_adjustment_action( "RadiusXAction",
2637                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2638                                          "tools.shapes.rect", "rx", 0,
2639                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2640                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2641                                          labels, values, G_N_ELEMENTS(labels),
2642                                          sp_rtb_rx_value_changed);
2643         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2644         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2645     }
2647     /* ry */
2648     {
2649         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2650         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2651         eact = create_adjustment_action( "RadiusYAction",
2652                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2653                                          "tools.shapes.rect", "ry", 0,
2654                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2655                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2656                                          labels, values, G_N_ELEMENTS(labels),
2657                                          sp_rtb_ry_value_changed);
2658         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2659         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2660     }
2662     // add the units menu
2663     {
2664         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2665         gtk_action_group_add_action( mainActions, act );
2666     }
2668     /* Reset */
2669     {
2670         InkAction* inky = ink_action_new( "RectResetAction",
2671                                           _("Not rounded"),
2672                                           _("Make corners sharp"),
2673                                           "squared_corner",
2674                                           secondarySize );
2675         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2676         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2677         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2678         g_object_set_data( holder, "not_rounded", inky );
2679     }
2681     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2682     sp_rtb_sensitivize( holder );
2684     sigc::connection *connection = new sigc::connection(
2685         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2686         );
2687     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2688     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2691 //########################
2692 //##       3D Box       ##
2693 //########################
2695 // normalize angle so that it lies in the interval [0,360]
2696 static double box3d_normalize_angle (double a) {
2697     double angle = a + ((int) (a/360.0))*360;
2698     if (angle < 0) {
2699         angle += 360.0;
2700     }
2701     return angle;
2704 static void
2705 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2706                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2707     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2708     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2709     //       are reset).
2710     bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2712     if (is_infinite) {
2713         gtk_toggle_action_set_active(tact, TRUE);
2714         gtk_action_set_sensitive(act, TRUE);
2716         double angle = persp3d_get_infinite_angle(persp, axis);
2717         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2718             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2719         }
2720     } else {
2721         gtk_toggle_action_set_active(tact, FALSE);
2722         gtk_action_set_sensitive(act, FALSE);
2723     }
2726 static void
2727 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2728     if (!persp_repr) {
2729         g_print ("No perspective given to box3d_resync_toolbar().\n");
2730         return;
2731     }
2733     GtkWidget *tbl = GTK_WIDGET(data);
2734     GtkAdjustment *adj = 0;
2735     GtkAction *act = 0;
2736     GtkToggleAction *tact = 0;
2737     Persp3D *persp = persp3d_get_from_repr(persp_repr);
2738     {
2739         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2740         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2741         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2743         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2744     }
2745     {
2746         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2747         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2748         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2750         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2751     }
2752     {
2753         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2754         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2755         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2757         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2758     }
2761 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2762                                                   gchar const */*old_value*/, gchar const */*new_value*/,
2763                                                   bool /*is_interactive*/, gpointer data)
2765     GtkWidget *tbl = GTK_WIDGET(data);
2767     // quit if run by the attr_changed listener
2768     // note: it used to work without the differently called freeze_ attributes (here and in
2769     //       box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2770     if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2771         return;
2772     }
2774     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2775     // sp_document_maybe_done() when the document is undo insensitive)
2776     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2778     // TODO: Only update the appropriate part of the toolbar
2779 //    if (!strcmp(name, "inkscape:vp_z")) {
2780         box3d_resync_toolbar(repr, G_OBJECT(tbl));
2781 //    }
2783     Persp3D *persp = persp3d_get_from_repr(repr);
2784     persp3d_update_box_reprs(persp);
2786     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2789 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2791     NULL, /* child_added */
2792     NULL, /* child_removed */
2793     box3d_persp_tb_event_attr_changed,
2794     NULL, /* content_changed */
2795     NULL  /* order_changed */
2796 };
2798 /**
2799  *  \param selection Should not be NULL.
2800  */
2801 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2802 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2803 static void
2804 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2806     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2807     // disable the angle entry fields for this direction (otherwise entering a value in them should only
2808     // update the perspectives with infinite VPs and leave the other ones untouched).
2810     Inkscape::XML::Node *persp_repr = NULL;
2811     purge_repr_listener(tbl, tbl);
2813     SPItem *item = selection->singleItem();
2814     if (item && SP_IS_BOX3D(item)) {
2815         // FIXME: Also deal with multiple selected boxes
2816         SPBox3D *box = SP_BOX3D(item);
2817         Persp3D *persp = box3d_get_perspective(box);
2818         persp_repr = SP_OBJECT_REPR(persp);
2819         if (persp_repr) {
2820             g_object_set_data(tbl, "repr", persp_repr);
2821             Inkscape::GC::anchor(persp_repr);
2822             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2823             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2824         }
2826         inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2827         prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2829         box3d_resync_toolbar(persp_repr, tbl);
2830     }
2833 static void
2834 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2836     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2837     SPDocument *document = sp_desktop_document(desktop);
2839     // quit if run by the attr_changed listener
2840     // note: it used to work without the differently called freeze_ attributes (here and in
2841     //       box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2842     if (g_object_get_data( dataKludge, "freeze_attr" )) {
2843         return;
2844     }
2846     // in turn, prevent listener from responding
2847     g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2849     //Persp3D *persp = document->current_persp3d;
2850     std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
2851     if (sel_persps.empty()) {
2852         // this can happen when the document is created; we silently ignore it
2853         return;
2854     }
2855     Persp3D *persp = sel_persps.front();
2857     persp->tmat.set_infinite_direction (axis, adj->value);
2858     SP_OBJECT(persp)->updateRepr();
2860     // TODO: use the correct axis here, too
2861     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2863     g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2867 static void
2868 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2870     box3d_angle_value_changed(adj, dataKludge, Proj::X);
2873 static void
2874 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2876     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2879 static void
2880 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2882     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2886 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2888     // TODO: Take all selected perspectives into account
2889     std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
2890     if (sel_persps.empty()) {
2891         // this can happen when the document is created; we silently ignore it
2892         return;
2893     }
2894     Persp3D *persp = sel_persps.front();
2896     bool set_infinite = gtk_toggle_action_get_active(act);
2897     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2900 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2902     box3d_vp_state_changed(act, box3d_angle, Proj::X);
2905 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2907     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2910 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2912     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2915 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2917     EgeAdjustmentAction* eact = 0;
2918     SPDocument *document = sp_desktop_document (desktop);
2919     Persp3D *persp = document->current_persp3d;
2921     EgeAdjustmentAction* box3d_angle_x = 0;
2922     EgeAdjustmentAction* box3d_angle_y = 0;
2923     EgeAdjustmentAction* box3d_angle_z = 0;
2925     /* Angle X */
2926     {
2927         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2928         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2929         eact = create_adjustment_action( "3DBoxAngleXAction",
2930                                          _("Angle in X direction"), _("Angle X:"),
2931                                          // Translators: PL is short for 'perspective line'
2932                                          _("Angle of PLs in X direction"),
2933                                          "tools.shapes.3dbox", "box3d_angle_x", 30,
2934                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2935                                          -360.0, 360.0, 1.0, 10.0,
2936                                          labels, values, G_N_ELEMENTS(labels),
2937                                          box3d_angle_x_value_changed );
2938         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2939         g_object_set_data( holder, "box3d_angle_x_action", eact );
2940         box3d_angle_x = eact;
2941     }
2943     if (!persp3d_VP_is_finite(persp, Proj::X)) {
2944         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2945     } else {
2946         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2947     }
2950     /* VP X state */
2951     {
2952         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2953                                                       // Translators: VP is short for 'vanishing point'
2954                                                       _("State of VP in X direction"),
2955                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2956                                                       "toggle_vp_x",
2957                                                       Inkscape::ICON_SIZE_DECORATION );
2958         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2959         g_object_set_data( holder, "box3d_vp_x_state_action", act );
2960         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2961         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2962         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2963     }
2965     /* Angle Y */
2966     {
2967         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2968         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2969         eact = create_adjustment_action( "3DBoxAngleYAction",
2970                                          _("Angle in Y direction"), _("Angle Y:"),
2971                                          // Translators: PL is short for 'perspective line'
2972                                          _("Angle of PLs in Y direction"),
2973                                          "tools.shapes.3dbox", "box3d_angle_y", 30,
2974                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2975                                          -360.0, 360.0, 1.0, 10.0,
2976                                          labels, values, G_N_ELEMENTS(labels),
2977                                          box3d_angle_y_value_changed );
2978         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2979         g_object_set_data( holder, "box3d_angle_y_action", eact );
2980         box3d_angle_y = eact;
2981     }
2983     if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2984         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2985     } else {
2986         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2987     }
2989     /* VP Y state */
2990     {
2991         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2992                                                       // Translators: VP is short for 'vanishing point'
2993                                                       _("State of VP in Y direction"),
2994                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2995                                                       "toggle_vp_y",
2996                                                       Inkscape::ICON_SIZE_DECORATION );
2997         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2998         g_object_set_data( holder, "box3d_vp_y_state_action", act );
2999         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
3000         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
3001         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
3002     }
3004     /* Angle Z */
3005     {
3006         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3007         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3008         eact = create_adjustment_action( "3DBoxAngleZAction",
3009                                          _("Angle in Z direction"), _("Angle Z:"),
3010                                          // Translators: PL is short for 'perspective line'
3011                                          _("Angle of PLs in Z direction"),
3012                                          "tools.shapes.3dbox", "box3d_angle_z", 30,
3013                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3014                                          -360.0, 360.0, 1.0, 10.0,
3015                                          labels, values, G_N_ELEMENTS(labels),
3016                                          box3d_angle_z_value_changed );
3017         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3018         g_object_set_data( holder, "box3d_angle_z_action", eact );
3019         box3d_angle_z = eact;
3020     }
3022     if (!persp3d_VP_is_finite(persp, Proj::Z)) {
3023         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3024     } else {
3025         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3026     }
3028     /* VP Z state */
3029     {
3030         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3031                                                       // Translators: VP is short for 'vanishing point'
3032                                                       _("State of VP in Z direction"),
3033                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3034                                                       "toggle_vp_z",
3035                                                       Inkscape::ICON_SIZE_DECORATION );
3036         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3037         g_object_set_data( holder, "box3d_vp_z_state_action", act );
3038         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3039         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3040         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3041     }
3043     sigc::connection *connection = new sigc::connection(
3044         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3045        );
3046     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3047     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3050 //########################
3051 //##       Spiral       ##
3052 //########################
3054 static void
3055 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
3057     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3059     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3060         prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
3061     }
3063     // quit if run by the attr_changed listener
3064     if (g_object_get_data( tbl, "freeze" )) {
3065         return;
3066     }
3068     // in turn, prevent listener from responding
3069     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3071     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3073     bool modmade = false;
3074     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3075          items != NULL;
3076          items = items->next)
3077     {
3078         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3079             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3080             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3081             SP_OBJECT((SPItem *) items->data)->updateRepr();
3082             modmade = true;
3083         }
3084     }
3086     g_free(namespaced_name);
3088     if (modmade) {
3089         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3090                                    _("Change spiral"));
3091     }
3093     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3096 static void
3097 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3099     sp_spl_tb_value_changed(adj, tbl, "revolution");
3102 static void
3103 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3105     sp_spl_tb_value_changed(adj, tbl, "expansion");
3108 static void
3109 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3111     sp_spl_tb_value_changed(adj, tbl, "t0");
3114 static void
3115 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3117     GtkWidget *tbl = GTK_WIDGET(obj);
3119     GtkAdjustment *adj;
3121     // fixme: make settable
3122     gdouble rev = 5;
3123     gdouble exp = 1.0;
3124     gdouble t0 = 0.0;
3126     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3127     gtk_adjustment_set_value(adj, rev);
3128     gtk_adjustment_value_changed(adj);
3130     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3131     gtk_adjustment_set_value(adj, exp);
3132     gtk_adjustment_value_changed(adj);
3134     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3135     gtk_adjustment_set_value(adj, t0);
3136     gtk_adjustment_value_changed(adj);
3138     spinbutton_defocus(GTK_OBJECT(tbl));
3142 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3143                                          gchar const */*old_value*/, gchar const */*new_value*/,
3144                                          bool /*is_interactive*/, gpointer data)
3146     GtkWidget *tbl = GTK_WIDGET(data);
3148     // quit if run by the _changed callbacks
3149     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3150         return;
3151     }
3153     // in turn, prevent callbacks from responding
3154     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3156     GtkAdjustment *adj;
3157     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3158     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3160     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3161     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3163     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3164     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3166     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3170 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3171     NULL, /* child_added */
3172     NULL, /* child_removed */
3173     spiral_tb_event_attr_changed,
3174     NULL, /* content_changed */
3175     NULL  /* order_changed */
3176 };
3178 static void
3179 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3181     int n_selected = 0;
3182     Inkscape::XML::Node *repr = NULL;
3184     purge_repr_listener( tbl, tbl );
3186     for (GSList const *items = selection->itemList();
3187          items != NULL;
3188          items = items->next)
3189     {
3190         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3191             n_selected++;
3192             repr = SP_OBJECT_REPR((SPItem *) items->data);
3193         }
3194     }
3196     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3198     if (n_selected == 0) {
3199         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3200     } else if (n_selected == 1) {
3201         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3203         if (repr) {
3204             g_object_set_data( tbl, "repr", repr );
3205             Inkscape::GC::anchor(repr);
3206             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3207             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3208         }
3209     } else {
3210         // FIXME: implement averaging of all parameters for multiple selected
3211         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3212         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3213     }
3217 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3219     EgeAdjustmentAction* eact = 0;
3220     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3222     {
3223         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3224         ege_output_action_set_use_markup( act, TRUE );
3225         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3226         g_object_set_data( holder, "mode_action", act );
3227     }
3229     /* Revolution */
3230     {
3231         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3232         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3233         eact = create_adjustment_action( "SpiralRevolutionAction",
3234                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3235                                          "tools.shapes.spiral", "revolution", 3.0,
3236                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3237                                          0.01, 1024.0, 0.1, 1.0,
3238                                          labels, values, G_N_ELEMENTS(labels),
3239                                          sp_spl_tb_revolution_value_changed, 1, 2);
3240         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3241     }
3243     /* Expansion */
3244     {
3245         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3246         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3247         eact = create_adjustment_action( "SpiralExpansionAction",
3248                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3249                                          "tools.shapes.spiral", "expansion", 1.0,
3250                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3251                                          0.0, 1000.0, 0.01, 1.0,
3252                                          labels, values, G_N_ELEMENTS(labels),
3253                                          sp_spl_tb_expansion_value_changed);
3254         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3255     }
3257     /* T0 */
3258     {
3259         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3260         gdouble values[] = {0, 0.5, 0.9};
3261         eact = create_adjustment_action( "SpiralT0Action",
3262                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3263                                          "tools.shapes.spiral", "t0", 0.0,
3264                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3265                                          0.0, 0.999, 0.01, 1.0,
3266                                          labels, values, G_N_ELEMENTS(labels),
3267                                          sp_spl_tb_t0_value_changed);
3268         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3269     }
3271     /* Reset */
3272     {
3273         InkAction* inky = ink_action_new( "SpiralResetAction",
3274                                           _("Defaults"),
3275                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3276                                           GTK_STOCK_CLEAR,
3277                                           secondarySize );
3278         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3279         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3280     }
3283     sigc::connection *connection = new sigc::connection(
3284         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3285         );
3286     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3287     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3290 //########################
3291 //##     Pen/Pencil     ##
3292 //########################
3294 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3295 static char const *
3296 freehand_tool_name(GObject *dataKludge)
3298     SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3299     return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3300              ? "tools.freehand.pen"
3301              : "tools.freehand.pencil" );
3304 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3306     gint mode = ege_select_one_action_get_active(act);
3308     prefs_set_int_attribute(freehand_tool_name(tbl), "freehand-mode", mode);
3310     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3312     // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3313     // preparatory work here
3314     if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3315         SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3316         sp_pen_context_set_polyline_mode(pc);
3317     }
3320 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3322     /* Freehand mode toggle buttons */
3323     {
3324         guint freehandMode = prefs_get_int_attribute(tool_is_pencil ? "tools.freehand.pencil" : "tools.freehand.pen", "freehand-mode", 0);
3325         Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3327         {
3328             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3330             GtkTreeIter iter;
3331             gtk_list_store_append( model, &iter );
3332             gtk_list_store_set( model, &iter,
3333                                 0, _("Bezier"),
3334                                 1, _("Create regular Bezier path"),
3335                                 2, "bezier_mode",
3336                                 -1 );
3338             gtk_list_store_append( model, &iter );
3339             gtk_list_store_set( model, &iter,
3340                                 0, _("Spiro"),
3341                                 1, _("Create Spiro path"),
3342                                 2, "spiro_splines_mode",
3343                                 -1 );
3345             if (!tool_is_pencil) {
3346                 gtk_list_store_append( model, &iter );
3347                 gtk_list_store_set( model, &iter,
3348                                     0, _("Zigzag"),
3349                                     1, _("Create a sequence of straight line segments"),
3350                                     2, "polylines_mode",
3351                                     -1 );
3353                 gtk_list_store_append( model, &iter );
3354                 gtk_list_store_set( model, &iter,
3355                                     0, _("Paraxial"),
3356                                     1, _("Create a sequence of paraxial line segments"),
3357                                     2, "paraxial_lines_mode",
3358                                     -1 );
3359             }
3361             EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3362                                                                 "FreehandModeActionPencil" :
3363                                                                 "FreehandModeActionPen",
3364                                                                 (_("Mode:")), ("Mode"), NULL, GTK_TREE_MODEL(model) );
3365             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3367             ege_select_one_action_set_appearance( act, "full" );
3368             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3369             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3370             ege_select_one_action_set_icon_column( act, 2 );
3371             ege_select_one_action_set_icon_size( act, secondarySize );
3372             ege_select_one_action_set_tooltip_column( act, 1  );
3374             ege_select_one_action_set_active( act, freehandMode);
3375             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3376         }
3377     }
3380 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3381     gint shape = ege_select_one_action_get_active( act );
3382     prefs_set_int_attribute(freehand_tool_name(dataKludge), "shape", shape);
3385 /**
3386  * \brief Generate the list of freehand advanced shape option entries.
3387  */
3388 GList * freehand_shape_dropdown_items_list() {
3389     GList *glist = NULL;
3391     glist = g_list_append (glist, _("None"));
3392     glist = g_list_append (glist, _("Triangle in"));
3393     glist = g_list_append (glist, _("Triangle out"));
3394     glist = g_list_append (glist, _("Ellipse"));
3395     glist = g_list_append (glist, _("From clipboard"));
3397     return glist;
3400 static void
3401 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3402     /*advanced shape options */
3403     {
3404         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3406         GList* items = 0;
3407         gint count = 0;
3408         for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3409         {
3410             GtkTreeIter iter;
3411             gtk_list_store_append( model, &iter );
3412             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3413             count++;
3414         }
3415         g_list_free( items );
3416         items = 0;
3417         EgeSelectOneAction* act1 = ege_select_one_action_new(
3418             tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3419             _("Shape:"), ("Shape"), NULL, GTK_TREE_MODEL(model));
3420         g_object_set( act1, "short_label", _("Shape:"), NULL );
3421         ege_select_one_action_set_appearance( act1, "compact" );
3422         ege_select_one_action_set_active( act1, prefs_get_int_attribute(tool_is_pencil ? "tools.freehand.pencil" : "tools.freehand.pen", "shape", 0) );
3423         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3424         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3425         g_object_set_data( holder, "shape_action", act1 );
3426     }
3429 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3431     sp_add_freehand_mode_toggle(mainActions, holder, false);
3432     freehand_add_advanced_shape_options(mainActions, holder, false);
3436 static void
3437 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3439     GtkWidget *tbl = GTK_WIDGET(obj);
3441     GtkAdjustment *adj;
3443     // fixme: make settable
3444     gdouble tolerance = 4;
3446     adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3447     gtk_adjustment_set_value(adj, tolerance);
3448     gtk_adjustment_value_changed(adj);
3450     spinbutton_defocus(GTK_OBJECT(tbl));
3453 static void
3454 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3457     // quit if run by the attr_changed listener
3458     if (g_object_get_data( tbl, "freeze" )) {
3459         return;
3460     }
3461     // in turn, prevent listener from responding
3462     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3463     prefs_set_double_attribute("tools.freehand.pencil",
3464                                "tolerance", adj->value);
3465     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3471 static void
3472 sp_pencil_tb_tolerance_value_changed_external(Inkscape::XML::Node */*repr*/,
3473                                               const gchar */*key*/,
3474                                               const gchar */*oldval*/,
3475                                               const gchar */*newval*/,
3476                                               bool /*is_interactive*/,
3477                                               void * data)
3479     GObject* tbl = G_OBJECT(data);
3480     if (g_object_get_data( tbl, "freeze" )) {
3481         return;
3482     }
3484     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3486     GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl,
3487                                                             "tolerance");
3489     double v = prefs_get_double_attribute("tools.freehand.pencil",
3490                                             "tolerance", adj->value);
3491     gtk_adjustment_set_value(adj, v);
3492     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3496 static Inkscape::XML::NodeEventVector pencil_node_events =
3498     NULL,
3499     NULL,
3500     sp_pencil_tb_tolerance_value_changed_external,
3501     NULL,
3502     NULL,
3503 };
3506 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3508     sp_add_freehand_mode_toggle(mainActions, holder, true);
3510     EgeAdjustmentAction* eact = 0;
3512     /* Tolerance */
3513     {
3514         gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
3515         gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
3516         eact = create_adjustment_action( "PencilToleranceAction",
3517                                          _("Smoothing:"), _("Smoothing: "),
3518                  _("How much smoothing (simplifying) is applied to the line"),
3519                                          "tools.freehand.pencil", "tolerance",
3520                                          3.0,
3521                                          GTK_WIDGET(desktop->canvas), NULL,
3522                                          holder, TRUE, "altx-pencil",
3523                                          1, 100.0, 0.5, 0,
3524                                          labels, values, G_N_ELEMENTS(labels),
3525                                          sp_pencil_tb_tolerance_value_changed,
3526                                          1, 2);
3527         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3528         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3530         Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE,
3531                                                       "tools.freehand.pencil");
3532         repr->addListener(&pencil_node_events, G_OBJECT(holder));
3533         g_object_set_data(G_OBJECT(holder), "repr", repr);
3535     }
3537     /* advanced shape options */
3538     freehand_add_advanced_shape_options(mainActions, holder, true);
3540     /* Reset */
3541     {
3542         InkAction* inky = ink_action_new( "PencilResetAction",
3543                                           _("Defaults"),
3544                                           _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3545                                           GTK_STOCK_CLEAR,
3546                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3547         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
3548         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3549     }
3551     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3556 //########################
3557 //##       Tweak        ##
3558 //########################
3560 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3562     prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3565 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3567     prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3570 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3572     prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3575 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3577     int mode = ege_select_one_action_get_active( act );
3578     prefs_set_int_attribute("tools.tweak", "mode", mode);
3580     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3581     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3582     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3583     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3584     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3585     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3586     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3587         if (doh) gtk_action_set_sensitive (doh, TRUE);
3588         if (dos) gtk_action_set_sensitive (dos, TRUE);
3589         if (dol) gtk_action_set_sensitive (dol, TRUE);
3590         if (doo) gtk_action_set_sensitive (doo, TRUE);
3591         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3592         if (fid) gtk_action_set_sensitive (fid, FALSE);
3593     } else {
3594         if (doh) gtk_action_set_sensitive (doh, FALSE);
3595         if (dos) gtk_action_set_sensitive (dos, FALSE);
3596         if (dol) gtk_action_set_sensitive (dol, FALSE);
3597         if (doo) gtk_action_set_sensitive (doo, FALSE);
3598         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3599         if (fid) gtk_action_set_sensitive (fid, TRUE);
3600     }
3603 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3605     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3608 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3609     bool show = gtk_toggle_action_get_active( act );
3610     prefs_set_int_attribute ("tools.tweak", "doh",  show ? 1 : 0);
3612 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3613     bool show = gtk_toggle_action_get_active( act );
3614     prefs_set_int_attribute ("tools.tweak", "dos",  show ? 1 : 0);
3616 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3617     bool show = gtk_toggle_action_get_active( act );
3618     prefs_set_int_attribute ("tools.tweak", "dol",  show ? 1 : 0);
3620 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3621     bool show = gtk_toggle_action_get_active( act );
3622     prefs_set_int_attribute ("tools.tweak", "doo",  show ? 1 : 0);
3625 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3627     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3629     {
3630         /* Width */
3631         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3632         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3633         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3634                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3635                                                               "tools.tweak", "width", 15,
3636                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3637                                                               1, 100, 1.0, 0.0,
3638                                                               labels, values, G_N_ELEMENTS(labels),
3639                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
3640         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3641         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3642         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3643     }
3646     {
3647         /* Force */
3648         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3649         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3650         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3651                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
3652                                                               "tools.tweak", "force", 20,
3653                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3654                                                               1, 100, 1.0, 0.0,
3655                                                               labels, values, G_N_ELEMENTS(labels),
3656                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
3657         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3658         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3659         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3660     }
3662     /* Mode */
3663     {
3664         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3666         GtkTreeIter iter;
3667         gtk_list_store_append( model, &iter );
3668         gtk_list_store_set( model, &iter,
3669                             0, _("Push mode"),
3670                             1, _("Push parts of paths in any direction"),
3671                             2, "tweak_push_mode",
3672                             -1 );
3674         gtk_list_store_append( model, &iter );
3675         gtk_list_store_set( model, &iter,
3676                             0, _("Shrink mode"),
3677                             1, _("Shrink (inset) parts of paths"),
3678                             2, "tweak_shrink_mode",
3679                             -1 );
3681         gtk_list_store_append( model, &iter );
3682         gtk_list_store_set( model, &iter,
3683                             0, _("Grow mode"),
3684                             1, _("Grow (outset) parts of paths"),
3685                             2, "tweak_grow_mode",
3686                             -1 );
3688         gtk_list_store_append( model, &iter );
3689         gtk_list_store_set( model, &iter,
3690                             0, _("Attract mode"),
3691                             1, _("Attract parts of paths towards cursor"),
3692                             2, "tweak_attract_mode",
3693                             -1 );
3695         gtk_list_store_append( model, &iter );
3696         gtk_list_store_set( model, &iter,
3697                             0, _("Repel mode"),
3698                             1, _("Repel parts of paths from cursor"),
3699                             2, "tweak_repel_mode",
3700                             -1 );
3702         gtk_list_store_append( model, &iter );
3703         gtk_list_store_set( model, &iter,
3704                             0, _("Roughen mode"),
3705                             1, _("Roughen parts of paths"),
3706                             2, "tweak_roughen_mode",
3707                             -1 );
3709         gtk_list_store_append( model, &iter );
3710         gtk_list_store_set( model, &iter,
3711                             0, _("Color paint mode"),
3712                             1, _("Paint the tool's color upon selected objects"),
3713                             2, "tweak_colorpaint_mode",
3714                             -1 );
3716         gtk_list_store_append( model, &iter );
3717         gtk_list_store_set( model, &iter,
3718                             0, _("Color jitter mode"),
3719                             1, _("Jitter the colors of selected objects"),
3720                             2, "tweak_colorjitter_mode",
3721                             -1 );
3723         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3724         g_object_set( act, "short_label", _("Mode:"), NULL );
3725         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3726         g_object_set_data( holder, "mode_action", act );
3728         ege_select_one_action_set_appearance( act, "full" );
3729         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3730         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3731         ege_select_one_action_set_icon_column( act, 2 );
3732         ege_select_one_action_set_icon_size( act, secondarySize );
3733         ege_select_one_action_set_tooltip_column( act, 1  );
3735         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3736         ege_select_one_action_set_active( act, mode );
3737         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3739         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3740     }
3742     guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3744     {
3745         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3746         ege_output_action_set_use_markup( act, TRUE );
3747         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3748         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3749             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3750         g_object_set_data( holder, "tweak_channels_label", act);
3751     }
3753     {
3754         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3755                                                       _("Hue"),
3756                                                       _("In color mode, act on objects' hue"),
3757                                                       NULL,
3758                                                       Inkscape::ICON_SIZE_DECORATION );
3759         //TRANSLATORS:  "H" here stands for hue
3760         g_object_set( act, "short_label", _("H"), NULL );
3761         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3762         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3763         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3764         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3765             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3766         g_object_set_data( holder, "tweak_doh", act);
3767     }
3768     {
3769         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3770                                                       _("Saturation"),
3771                                                       _("In color mode, act on objects' saturation"),
3772                                                       NULL,
3773                                                       Inkscape::ICON_SIZE_DECORATION );
3774         //TRANSLATORS: "S" here stands for Saturation
3775         g_object_set( act, "short_label", _("S"), NULL );
3776         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3777         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3778         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3779         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3780             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3781         g_object_set_data( holder, "tweak_dos", act );
3782     }
3783     {
3784         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3785                                                       _("Lightness"),
3786                                                       _("In color mode, act on objects' lightness"),
3787                                                       NULL,
3788                                                       Inkscape::ICON_SIZE_DECORATION );
3789         //TRANSLATORS: "L" here stands for Lightness
3790         g_object_set( act, "short_label", _("L"), NULL );
3791         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3792         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3793         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3794         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3795             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3796         g_object_set_data( holder, "tweak_dol", act );
3797     }
3798     {
3799         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3800                                                       _("Opacity"),
3801                                                       _("In color mode, act on objects' opacity"),
3802                                                       NULL,
3803                                                       Inkscape::ICON_SIZE_DECORATION );
3804         //TRANSLATORS: "O" here stands for Opacity
3805         g_object_set( act, "short_label", _("O"), NULL );
3806         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3807         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3808         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3809         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3810             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3811         g_object_set_data( holder, "tweak_doo", act );
3812     }
3814     {   /* Fidelity */
3815         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3816         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3817         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3818                                                               _("Fidelity"), _("Fidelity:"),
3819                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3820                                                               "tools.tweak", "fidelity", 50,
3821                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3822                                                               1, 100, 1.0, 10.0,
3823                                                               labels, values, G_N_ELEMENTS(labels),
3824                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
3825         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3826         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3827         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3828             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3829         g_object_set_data( holder, "tweak_fidelity", eact );
3830     }
3833     /* Use Pressure button */
3834     {
3835         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3836                                                       _("Pressure"),
3837                                                       _("Use the pressure of the input device to alter the force of tweak action"),
3838                                                       "use_pressure",
3839                                                       Inkscape::ICON_SIZE_DECORATION );
3840         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3841         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3842         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3843     }
3848 //########################
3849 //##     Calligraphy    ##
3850 //########################
3851 static void update_presets_list (GObject *tbl)
3853     if (g_object_get_data(tbl, "presets_blocked"))
3854         return;
3856     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3857     if (!sel) {
3858         ege_select_one_action_set_active(sel, 0);
3859         return;
3860     }
3862     int total_prefs = pref_path_number_of_children("tools.calligraphic.preset");
3864     for (int i = 1; i <= total_prefs; i++) {
3865         gchar *preset_path = get_pref_nth_child("tools.calligraphic.preset", i);
3866         Inkscape::XML::Node *preset_repr = inkscape_get_repr(INKSCAPE, preset_path);
3868         bool match = true;
3870         for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
3871               iter; 
3872               ++iter ) {
3873             const gchar *attr_name = g_quark_to_string(iter->key);
3874             if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
3875                 continue;
3876             void *widget = g_object_get_data(tbl, attr_name);
3877             if (widget) {
3878                 if (GTK_IS_ADJUSTMENT(widget)) {
3879                     double v = prefs_get_double_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
3880                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
3881                     //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
3882                     if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
3883                         match = false;
3884                         break;
3885                     }
3886                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
3887                     int v = prefs_get_int_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
3888                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
3889                     //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
3890                     if (gtk_toggle_action_get_active(toggle) != v) {
3891                         match = false;
3892                         break;
3893                     }
3894                 } 
3895             } 
3896         }
3898         if (match) {
3899             // newly added item is at the same index as the 
3900             // save command, so we need to change twice for it to take effect
3901             ege_select_one_action_set_active(sel, 0);
3902             ege_select_one_action_set_active(sel, i);
3903             return;
3904         }
3905     }
3907     // no match found
3908     ege_select_one_action_set_active(sel, 0);
3911 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3913     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value * 0.01 );
3914     update_presets_list(tbl);
3917 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3919     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value * 0.01 );
3920     update_presets_list(tbl);
3923 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3925     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3926     update_presets_list(tbl);
3929 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3931     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3932     update_presets_list(tbl);
3935 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3937     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value * 0.01 );
3938     update_presets_list(tbl);
3941 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3943     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value * 0.01);
3944     update_presets_list(tbl);
3947 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3949     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value * 0.01 );
3950     update_presets_list(tbl);
3953 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3955     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3956     update_presets_list(tbl);
3959 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
3961     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3962     update_presets_list(tbl);
3965 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
3967     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3968     update_presets_list(tbl);
3971 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
3973     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
3974     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3975     update_presets_list(tbl);
3976     if (calligraphy_angle )
3977         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3981 static gchar const *const widget_names[] = {
3982     "width",
3983     "mass",
3984     "wiggle",
3985     "angle",
3986     "thinning",
3987     "tremor",
3988     "flatness",
3989     "cap_rounding",
3990     "usepressure",
3991     "tracebackground",
3992     "usetilt"
3993 };
3996 static void sp_dcc_build_presets_list(GObject *tbl) 
3998     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4000     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4001     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4002     gtk_list_store_clear (model);
4004     {
4005         GtkTreeIter iter;
4006         gtk_list_store_append( model, &iter );
4007         gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4008     }
4010     //TODO: switch back to prefs API
4011     Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
4012     Inkscape::XML::Node *child_repr = sp_repr_children(repr);
4013     int ii=1;
4014     while (child_repr) {
4015         GtkTreeIter iter;
4016         char *preset_name = (char *) child_repr->attribute("name");
4017         gtk_list_store_append( model, &iter );
4018         gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );
4019         child_repr = sp_repr_next(child_repr);
4020     }
4022     {
4023         GtkTreeIter iter;
4024         gtk_list_store_append( model, &iter );
4025         gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4026         g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4027     }
4029     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4031     update_presets_list (tbl);
4034 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4036     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4037     if (! desktop) return;
4039     if (g_object_get_data(tbl, "presets_blocked")) 
4040         return;
4042     Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
4043     if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) {
4044         // dialog cancelled
4045         update_presets_list (tbl);
4046         return;
4047     }
4048     Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
4050     if (!profile_name.c_str() || *profile_name.c_str() == 0) {
4051         // empty name entered
4052         update_presets_list (tbl);
4053         return;
4054     }
4056     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4058     int new_index = -1;
4059     gchar *pref_path = NULL;
4060     int total_prefs = pref_path_number_of_children("tools.calligraphic.preset");
4062     for (int i = 1; i <= total_prefs; i++) {
4063         gchar *path = get_pref_nth_child("tools.calligraphic.preset", i);
4064         const gchar *name = prefs_get_string_attribute(path, "name");
4065         if (name && !strcmp(name, profile_name.c_str())) {
4066             // we already have preset with this name, replace its values
4067             new_index = i;
4068             pref_path = g_strdup(path);
4069             break;
4070         }
4071     }
4073     if (new_index == -1) {
4074         // no preset with this name, create 
4075         new_index = total_prefs + 1;
4076         gchar *profile_id = g_strdup_printf("dcc%d", new_index);
4077         pref_path = create_pref("tools.calligraphic.preset", profile_id);
4078         g_free(profile_id);
4079     }
4081     for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4082         gchar const *const widget_name = widget_names[i];
4083         void *widget = g_object_get_data(tbl, widget_name);
4084         if (widget) {
4085             if (GTK_IS_ADJUSTMENT(widget)) {
4086                 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4087                 double v = gtk_adjustment_get_value(adj);
4088                 prefs_set_double_attribute(pref_path, widget_name, v);
4089                 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4090             } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4091                 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4092                 int v = gtk_toggle_action_get_active(toggle);
4093                 prefs_set_int_attribute(pref_path, widget_name, v);
4094                 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4095             } else {
4096                g_warning("Unknown widget type for preset: %s\n", widget_name);
4097             }
4098         } else {
4099             g_warning("Bad key when writing preset: %s\n", widget_name);
4100         }
4101     }
4102     prefs_set_string_attribute(pref_path, "name", profile_name.c_str());
4104     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4106     sp_dcc_build_presets_list (tbl);
4108     g_free(pref_path);
4112 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4114     gint preset_index = ege_select_one_action_get_active( act );
4115     gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4117     if (preset_index == save_presets_index) {
4118         // this is the Save command
4119         sp_dcc_save_profile(NULL, tbl);
4120         return;
4121     }
4123     if (g_object_get_data(tbl, "presets_blocked")) 
4124         return;
4126     gchar *const preset_path = get_pref_nth_child("tools.calligraphic.preset", preset_index);
4128     if (preset_path) {
4129         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
4131         Inkscape::XML::Node *preset_repr = inkscape_get_repr(INKSCAPE, preset_path);
4133         for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
4134               iter; 
4135               ++iter ) {
4136             const gchar *attr_name = g_quark_to_string(iter->key);
4137             if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
4138                 continue;
4139             void *widget = g_object_get_data(tbl, attr_name);
4140             if (widget) {
4141                 if (GTK_IS_ADJUSTMENT(widget)) {
4142                     double v = prefs_get_double_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4143                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4144                     gtk_adjustment_set_value(adj, v);
4145                     //std::cout << "set adj " << attr_name << " to " << v << "\n";
4146                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4147                     int v = prefs_get_int_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4148                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4149                     gtk_toggle_action_set_active(toggle, v);
4150                     //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4151                 } else {
4152                     g_warning("Unknown widget type for preset: %s\n", attr_name);
4153                 }
4154             } else {
4155                 g_warning("Bad key found in a preset record: %s\n", attr_name);
4156             }
4157         }
4158         g_free(preset_path);
4159         g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4160     }
4165 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4167     {
4168         g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4170         EgeAdjustmentAction* calligraphy_angle = 0;
4172         {
4173         /* Width */
4174         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4175         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4176         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4177                                                               _("Pen Width"), _("Width:"),
4178                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
4179                                                               "tools.calligraphic", "width", 15,
4180                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4181                                                               1, 100, 1.0, 0.0,
4182                                                               labels, values, G_N_ELEMENTS(labels),
4183                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
4184         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4185         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4186         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4187         }
4189         {
4190         /* Thinning */
4191             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4192             gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4193         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4194                                                               _("Stroke Thinning"), _("Thinning:"),
4195                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4196                                                               "tools.calligraphic", "thinning", 10,
4197                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4198                                                               -100, 100, 1, 0.1,
4199                                                               labels, values, G_N_ELEMENTS(labels),
4200                                                               sp_ddc_velthin_value_changed, 0.01, 0, 100);
4201         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4202         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4203         }
4205         {
4206         /* Angle */
4207         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4208         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4209         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4210                                                               _("Pen Angle"), _("Angle:"),
4211                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4212                                                               "tools.calligraphic", "angle", 30,
4213                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4214                                                               -90.0, 90.0, 1.0, 10.0,
4215                                                               labels, values, G_N_ELEMENTS(labels),
4216                                                               sp_ddc_angle_value_changed, 1, 0 );
4217         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4218         g_object_set_data( holder, "angle_action", eact );
4219         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4220         calligraphy_angle = eact;
4221         }
4223         {
4224         /* Fixation */
4225             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4226         gdouble values[] = {0, 20, 40, 60, 90, 100};
4227         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4228                                                               _("Fixation"), _("Fixation:"),
4229                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
4230                                                               "tools.calligraphic", "flatness", 90,
4231                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4232                                                               0.0, 100, 1.0, 10.0,
4233                                                               labels, values, G_N_ELEMENTS(labels),
4234                                                               sp_ddc_flatness_value_changed, 0.01, 0, 100 );
4235         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4236         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4237         }
4239         {
4240         /* Cap Rounding */
4241             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4242         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4243         // TRANSLATORS: "cap" means "end" (both start and finish) here
4244         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4245                                                               _("Cap rounding"), _("Caps:"),
4246                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4247                                                               "tools.calligraphic", "cap_rounding", 0.0,
4248                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4249                                                               0.0, 5.0, 0.01, 0.1,
4250                                                               labels, values, G_N_ELEMENTS(labels),
4251                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4252         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4253         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4254         }
4256         {
4257         /* Tremor */
4258             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4259         gdouble values[] = {0, 10, 20, 40, 60, 100};
4260         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4261                                                               _("Stroke Tremor"), _("Tremor:"),
4262                                                               _("Increase to make strokes rugged and trembling"),
4263                                                               "tools.calligraphic", "tremor", 0.0,
4264                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4265                                                               0.0, 100, 1, 0.0,
4266                                                               labels, values, G_N_ELEMENTS(labels),
4267                                                               sp_ddc_tremor_value_changed, 0.01, 0, 100 );
4269         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4270         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4271         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4272         }
4274         {
4275         /* Wiggle */
4276         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4277         gdouble values[] = {0, 20, 40, 60, 100};
4278         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4279                                                               _("Pen Wiggle"), _("Wiggle:"),
4280                                                               _("Increase to make the pen waver and wiggle"),
4281                                                               "tools.calligraphic", "wiggle", 0.0,
4282                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4283                                                               0.0, 100, 1, 0.0,
4284                                                               labels, values, G_N_ELEMENTS(labels),
4285                                                               sp_ddc_wiggle_value_changed, 0.01, 0, 100 );
4286         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4287         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4288         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4289         }
4291         {
4292         /* Mass */
4293             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4294         gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4295         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4296                                                               _("Pen Mass"), _("Mass:"),
4297                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
4298                                                               "tools.calligraphic", "mass", 2.0,
4299                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4300                                                               0.0, 100, 1, 0.0,
4301                                                               labels, values, G_N_ELEMENTS(labels),
4302                                                               sp_ddc_mass_value_changed, 0.01, 0, 100 );
4303         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4304         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4305         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4306         }
4309         /* Trace Background button */
4310         {
4311             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4312                                                           _("Trace Background"),
4313                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4314                                                           "trace_background",
4315                                                           Inkscape::ICON_SIZE_DECORATION );
4316             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4317             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4318             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
4319             g_object_set_data( holder, "tracebackground", act );
4320         }
4322         /* Use Pressure button */
4323         {
4324             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4325                                                           _("Pressure"),
4326                                                           _("Use the pressure of the input device to alter the width of the pen"),
4327                                                           "use_pressure",
4328                                                           Inkscape::ICON_SIZE_DECORATION );
4329             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4330             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4331             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
4332             g_object_set_data( holder, "usepressure", act );
4333         }
4335         /* Use Tilt button */
4336         {
4337             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4338                                                           _("Tilt"),
4339                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
4340                                                           "use_tilt",
4341                                                           Inkscape::ICON_SIZE_DECORATION );
4342             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4343             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4344             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4345             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4346             g_object_set_data( holder, "usetilt", act );
4347         }
4349         /*calligraphic profile */
4350         {
4351             GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
4352             EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
4353             ege_select_one_action_set_appearance (act1, "compact");
4354             g_object_set_data (holder, "profile_selector", act1 );
4356             g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
4358             sp_dcc_build_presets_list (holder);
4360             g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
4361             gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
4362         }
4363     }
4367 //########################
4368 //##    Circle / Arc    ##
4369 //########################
4371 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4373     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4374     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4376     if (v1 == 0 && v2 == 0) {
4377         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4378             gtk_action_set_sensitive( ocb, FALSE );
4379             gtk_action_set_sensitive( make_whole, FALSE );
4380         }
4381     } else {
4382         gtk_action_set_sensitive( ocb, TRUE );
4383         gtk_action_set_sensitive( make_whole, TRUE );
4384     }
4387 static void
4388 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4390     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4392     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4393         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
4394     }
4396     // quit if run by the attr_changed listener
4397     if (g_object_get_data( tbl, "freeze" )) {
4398         return;
4399     }
4401     // in turn, prevent listener from responding
4402     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4404     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4406     bool modmade = false;
4407     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4408          items != NULL;
4409          items = items->next)
4410     {
4411         SPItem *item = SP_ITEM(items->data);
4413         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4415             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4416             SPArc *arc = SP_ARC(item);
4418             if (!strcmp(value_name, "start"))
4419                 ge->start = (adj->value * M_PI)/ 180;
4420             else
4421                 ge->end = (adj->value * M_PI)/ 180;
4423             sp_genericellipse_normalize(ge);
4424             ((SPObject *)arc)->updateRepr();
4425             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4427             modmade = true;
4428         }
4429     }
4431     g_free(namespaced_name);
4433     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4435     sp_arctb_sensitivize( tbl, adj->value, other->value );
4437     if (modmade) {
4438         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4439                                    _("Arc: Change start/end"));
4440     }
4442     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4446 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
4448     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
4451 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4453     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
4457 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4459     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4460     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4461         if ( ege_select_one_action_get_active( act ) != 0 ) {
4462             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
4463         } else {
4464             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
4465         }
4466     }
4468     // quit if run by the attr_changed listener
4469     if (g_object_get_data( tbl, "freeze" )) {
4470         return;
4471     }
4473     // in turn, prevent listener from responding
4474     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4476     bool modmade = false;
4478     if ( ege_select_one_action_get_active(act) != 0 ) {
4479         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4480              items != NULL;
4481              items = items->next)
4482         {
4483             if (SP_IS_ARC((SPItem *) items->data)) {
4484                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4485                 repr->setAttribute("sodipodi:open", "true");
4486                 SP_OBJECT((SPItem *) items->data)->updateRepr();
4487                 modmade = true;
4488             }
4489         }
4490     } else {
4491         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4492              items != NULL;
4493              items = items->next)
4494         {
4495             if (SP_IS_ARC((SPItem *) items->data))    {
4496                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4497                 repr->setAttribute("sodipodi:open", NULL);
4498                 SP_OBJECT((SPItem *) items->data)->updateRepr();
4499                 modmade = true;
4500             }
4501         }
4502     }
4504     if (modmade) {
4505         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4506                                    _("Arc: Change open/closed"));
4507     }
4509     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4512 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4514     GtkAdjustment *adj;
4515     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
4516     gtk_adjustment_set_value(adj, 0.0);
4517     gtk_adjustment_value_changed(adj);
4519     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
4520     gtk_adjustment_set_value(adj, 0.0);
4521     gtk_adjustment_value_changed(adj);
4523     spinbutton_defocus( GTK_OBJECT(obj) );
4526 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
4527                                       gchar const */*old_value*/, gchar const */*new_value*/,
4528                                       bool /*is_interactive*/, gpointer data)
4530     GObject *tbl = G_OBJECT(data);
4532     // quit if run by the _changed callbacks
4533     if (g_object_get_data( tbl, "freeze" )) {
4534         return;
4535     }
4537     // in turn, prevent callbacks from responding
4538     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4540     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4541     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4543     GtkAdjustment *adj1,*adj2;
4544     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4545     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4546     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4547     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4549     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4551     char const *openstr = NULL;
4552     openstr = repr->attribute("sodipodi:open");
4553     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4555     if (openstr) {
4556         ege_select_one_action_set_active( ocb, 1 );
4557     } else {
4558         ege_select_one_action_set_active( ocb, 0 );
4559     }
4561     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4564 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4565     NULL, /* child_added */
4566     NULL, /* child_removed */
4567     arc_tb_event_attr_changed,
4568     NULL, /* content_changed */
4569     NULL  /* order_changed */
4570 };
4573 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4575     int n_selected = 0;
4576     Inkscape::XML::Node *repr = NULL;
4578     purge_repr_listener( tbl, tbl );
4580     for (GSList const *items = selection->itemList();
4581          items != NULL;
4582          items = items->next)
4583     {
4584         if (SP_IS_ARC((SPItem *) items->data)) {
4585             n_selected++;
4586             repr = SP_OBJECT_REPR((SPItem *) items->data);
4587         }
4588     }
4590     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4592     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4593     if (n_selected == 0) {
4594         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4595     } else if (n_selected == 1) {
4596         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4597         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4599         if (repr) {
4600             g_object_set_data( tbl, "repr", repr );
4601             Inkscape::GC::anchor(repr);
4602             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4603             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4604         }
4605     } else {
4606         // FIXME: implement averaging of all parameters for multiple selected
4607         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4608         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4609         sp_arctb_sensitivize( tbl, 1, 0 );
4610     }
4614 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4616     EgeAdjustmentAction* eact = 0;
4617     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
4620     {
4621         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4622         ege_output_action_set_use_markup( act, TRUE );
4623         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4624         g_object_set_data( holder, "mode_action", act );
4625     }
4627     /* Start */
4628     {
4629         eact = create_adjustment_action( "ArcStartAction",
4630                                          _("Start"), _("Start:"),
4631                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
4632                                          "tools.shapes.arc", "start", 0.0,
4633                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4634                                          -360.0, 360.0, 1.0, 10.0,
4635                                          0, 0, 0,
4636                                          sp_arctb_start_value_changed);
4637         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4638     }
4640     /* End */
4641     {
4642         eact = create_adjustment_action( "ArcEndAction",
4643                                          _("End"), _("End:"),
4644                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
4645                                          "tools.shapes.arc", "end", 0.0,
4646                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4647                                          -360.0, 360.0, 1.0, 10.0,
4648                                          0, 0, 0,
4649                                          sp_arctb_end_value_changed);
4650         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4651     }
4653     /* Segments / Pie checkbox */
4654     {
4655         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4657         GtkTreeIter iter;
4658         gtk_list_store_append( model, &iter );
4659         gtk_list_store_set( model, &iter,
4660                             0, _("Closed arc"),
4661                             1, _("Switch to segment (closed shape with two radii)"),
4662                             2, "circle_closed_arc",
4663                             -1 );
4665         gtk_list_store_append( model, &iter );
4666         gtk_list_store_set( model, &iter,
4667                             0, _("Open Arc"),
4668                             1, _("Switch to arc (unclosed shape)"),
4669                             2, "circle_open_arc",
4670                             -1 );
4672         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4673         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4674         g_object_set_data( holder, "open_action", act );
4676         ege_select_one_action_set_appearance( act, "full" );
4677         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4678         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4679         ege_select_one_action_set_icon_column( act, 2 );
4680         ege_select_one_action_set_icon_size( act, secondarySize );
4681         ege_select_one_action_set_tooltip_column( act, 1  );
4683         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4684         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4685         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4686         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4687     }
4689     /* Make Whole */
4690     {
4691         InkAction* inky = ink_action_new( "ArcResetAction",
4692                                           _("Make whole"),
4693                                           _("Make the shape a whole ellipse, not arc or segment"),
4694                                           "reset_circle",
4695                                           secondarySize );
4696         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4697         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4698         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4699         g_object_set_data( holder, "make_whole", inky );
4700     }
4702     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4703     // sensitivize make whole and open checkbox
4704     {
4705         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4706         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4707         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4708     }
4711     sigc::connection *connection = new sigc::connection(
4712         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4713         );
4714     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4715     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4721 // toggle button callbacks and updaters
4723 //########################
4724 //##      Dropper       ##
4725 //########################
4727 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4728     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4729     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4730     if ( set_action ) {
4731         if ( gtk_toggle_action_get_active( act ) ) {
4732             gtk_action_set_sensitive( set_action, TRUE );
4733         } else {
4734             gtk_action_set_sensitive( set_action, FALSE );
4735         }
4736     }
4738     spinbutton_defocus(GTK_OBJECT(tbl));
4741 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4742     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4743     spinbutton_defocus(GTK_OBJECT(tbl));
4747 /**
4748  * Dropper auxiliary toolbar construction and setup.
4749  *
4750  * TODO: Would like to add swatch of current color.
4751  * TODO: Add queue of last 5 or so colors selected with new swatches so that
4752  *       can drag and drop places. Will provide a nice mixing palette.
4753  */
4754 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4756     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4758     {
4759         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4760         ege_output_action_set_use_markup( act, TRUE );
4761         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4762     }
4764     {
4765         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4766                                                       _("Pick opacity"),
4767                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4768                                                       NULL,
4769                                                       Inkscape::ICON_SIZE_DECORATION );
4770         g_object_set( act, "short_label", _("Pick"), NULL );
4771         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4772         g_object_set_data( holder, "pick_action", act );
4773         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4774         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4775     }
4777     {
4778         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4779                                                       _("Assign opacity"),
4780                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4781                                                       NULL,
4782                                                       Inkscape::ICON_SIZE_DECORATION );
4783         g_object_set( act, "short_label", _("Assign"), NULL );
4784         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4785         g_object_set_data( holder, "set_action", act );
4786         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4787         // make sure it's disabled if we're not picking alpha
4788         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4789         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4790     }
4794 //########################
4795 //##      LPETool       ##
4796 //########################
4798 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
4800 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
4801 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
4803     using namespace Inkscape::LivePathEffect;
4805     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
4806     SPEventContext *ec = desktop->event_context;
4807     if (!SP_IS_LPETOOL_CONTEXT(ec)) {
4808         return;
4809     }
4811     // only take action if run by the attr_changed listener
4812     if (!g_object_get_data(tbl, "freeze")) {
4813         // in turn, prevent listener from responding
4814         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
4816         gint mode = ege_select_one_action_get_active(act);
4817         EffectType type = lpesubtools[mode];
4819         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4820         bool success = lpetool_try_construction(lc, type);
4821         if (success) {
4822             // since the construction was already performed, we set the state back to inactive
4823             ege_select_one_action_set_active(act, 0);
4824             mode = 0;
4825         } else {
4826             // switch to the chosen subtool
4827             SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
4828         }
4830         if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4831             prefs_set_int_attribute( "tools.lpetool", "mode", mode );
4832         }
4834         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
4835     }
4838 void
4839 sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4841     SPEventContext *ec = selection->desktop()->event_context;
4842     if (!SP_IS_LPETOOL_CONTEXT(ec))
4843         return;
4845     lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
4848 void
4849 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
4851     using namespace Inkscape::LivePathEffect;
4852     SPEventContext *ec = selection->desktop()->event_context;
4853     if (!SP_IS_LPETOOL_CONTEXT(ec))
4854         return;
4855     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
4857     lpetool_delete_measuring_items(lc);
4858     lpetool_create_measuring_items(lc, selection);
4860     // activate line segment combo box if a single item with LPELineSegment is selected
4861     GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
4862     SPItem *item = selection->singleItem();
4863     if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
4864         SPLPEItem *lpeitem = SP_LPE_ITEM(item);
4865         Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
4866         if (lpe && lpe->effectType() == LINE_SEGMENT) {
4867             LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
4868             g_object_set_data(tbl, "currentlpe", lpe);
4869             g_object_set_data(tbl, "currentlpeitem", lpeitem);
4870             gtk_action_set_sensitive(w, TRUE);
4871             ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
4872         } else {
4873             g_object_set_data(tbl, "currentlpe", NULL);
4874             g_object_set_data(tbl, "currentlpeitem", NULL);
4875             gtk_action_set_sensitive(w, FALSE);
4876         }
4877     } else {
4878         g_object_set_data(tbl, "currentlpe", NULL);
4879         g_object_set_data(tbl, "currentlpeitem", NULL);
4880         gtk_action_set_sensitive(w, FALSE);
4881     }
4884 static void
4885 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
4886     SPDesktop *desktop = static_cast<SPDesktop *>(data);
4888     bool show = gtk_toggle_action_get_active( act );
4889     prefs_set_int_attribute ("tools.lpetool", "show_bbox",  show ? 1 : 0);
4891     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
4892         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4893         lpetool_context_reset_limiting_bbox(lc);
4894     }
4897 static void
4898 lpetool_toggle_show_measuring_info (GtkToggleAction *act, GObject *tbl) {
4899     SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
4900     if (!tools_isactive(desktop, TOOLS_LPETOOL))
4901         return;
4903     GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
4904     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4905     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
4906         bool show = gtk_toggle_action_get_active( act );
4907         prefs_set_int_attribute ("tools.lpetool", "show_measuring_info",  show ? 1 : 0);
4908         lpetool_show_measuring_info(lc, show);
4909         gtk_action_set_sensitive(GTK_ACTION(unitact), show);
4910     }
4913 static void
4914 lpetool_unit_changed(GtkAction* act, GObject* tbl) {
4915     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
4916     SPUnit const *unit = tracker->getActiveUnit();
4917     prefs_set_int_attribute("tools.lpetool", "unitid", unit->unit_id);
4919     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4920     if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
4921         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4922         lpetool_delete_measuring_items(lc);
4923         lpetool_create_measuring_items(lc);
4924     }
4927 static void
4928 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
4929     SPDesktop *desktop = static_cast<SPDesktop *>(data);
4930     Inkscape::Selection *selection = desktop->selection;
4932     boost::optional<NR::Rect> bbox = selection->bounds();
4934     if (bbox) {
4935         Geom::Point A(bbox->min());
4936         Geom::Point B(bbox->max());
4938         A *= desktop->doc2dt();
4939         B *= desktop->doc2dt();
4941         // TODO: should we provide a way to store points in prefs?
4942         prefs_set_double_attribute ("tools.lpetool", "bbox_upperleftx", A[Geom::X]);
4943         prefs_set_double_attribute ("tools.lpetool", "bbox_upperlefty", A[Geom::Y]);
4944         prefs_set_double_attribute ("tools.lpetool", "bbox_lowerrightx", B[Geom::X]);
4945         prefs_set_double_attribute ("tools.lpetool", "bbox_lowerrighty", B[Geom::Y]);
4947         lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
4948     }
4950     gtk_toggle_action_set_active(act, false);
4953 static void
4954 sp_line_segment_build_list(GObject *tbl) 
4956     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
4958     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
4959     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4960     gtk_list_store_clear (model);
4962     // TODO: we add the entries of rht combo box manually; later this should be done automatically
4963     {
4964         GtkTreeIter iter;
4965         gtk_list_store_append( model, &iter );
4966         gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
4967         gtk_list_store_append( model, &iter );
4968         gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
4969         gtk_list_store_append( model, &iter );
4970         gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
4971         gtk_list_store_append( model, &iter );
4972         gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
4973     }
4975     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
4978 static void
4979 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
4980     using namespace Inkscape::LivePathEffect;
4982     // quit if run by the attr_changed listener
4983     if (g_object_get_data(tbl, "freeze")) {
4984         return;
4985     }
4987     // in turn, prevent listener from responding
4988     g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
4990     LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
4991     SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
4992     if (lpeitem) {
4993         SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
4994         lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
4995         sp_lpe_item_update_patheffect(lpeitem, true, true);
4996     }
4998     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5001 static void
5002 lpetool_open_lpe_dialog (GtkToggleAction *act, gpointer data) {
5003     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5005     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5006         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5007         sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5008     }
5009     gtk_toggle_action_set_active(act, false);
5012 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5014     UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5015     tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5016     g_object_set_data(holder, "tracker", tracker);
5017     SPUnit const *unit = tracker->getActiveUnit();
5018     prefs_set_int_attribute("tools.lpetool", "unitid", unit->unit_id);
5020     /** Automatically create a list of LPEs that get added to the toolbar **/
5021     {
5022         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5024         GtkTreeIter iter;
5026         // the first toggle button represents the state that no subtool is active (remove this when
5027         // this can be modeled by EgeSelectOneAction or some other action)
5028         gtk_list_store_append( model, &iter );
5029         gtk_list_store_set( model, &iter,
5030                             0, _("All inactive"),
5031                             1, _("No geometric tool is active"),
5032                             2, _("all_inactive"),
5033                             -1 );
5035         Inkscape::LivePathEffect::EffectType type;
5036         for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5037             type =  lpesubtools[i];
5038             gtk_list_store_append( model, &iter );
5039             gtk_list_store_set( model, &iter,
5040                                 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5041                                 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5042                                 2, Inkscape::LivePathEffect::LPETypeConverter.get_key(type).c_str(),
5043                                 -1 );
5044         }
5046         EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5047         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5048         g_object_set_data( holder, "lpetool_mode_action", act );
5050         ege_select_one_action_set_appearance( act, "full" );
5051         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5052         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5053         ege_select_one_action_set_icon_column( act, 2 );
5054         ege_select_one_action_set_tooltip_column( act, 1  );
5056         gint lpeToolMode = prefs_get_int_attribute("tools.lpetool", "mode", 0);
5057         ege_select_one_action_set_active( act, lpeToolMode );
5058         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5059     }
5061     /* Show limiting bounding box */
5062     {
5063         InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5064                                                       _("Show limiting bounding box"),
5065                                                       _("Show bounding box (used to cut infinite lines)"),
5066                                                       "lpetool_show_bbox",
5067                                                       Inkscape::ICON_SIZE_DECORATION );
5068         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5069         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5070         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.lpetool", "show_bbox", 1 ) );
5071     }
5073     /* Set limiting bounding box to bbox of current selection */
5074     {
5075         InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5076                                                       _("Get limiting bounding box from selection"),
5077                                                       _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5078                                                       "lpetool_set_bbox",
5079                                                       Inkscape::ICON_SIZE_DECORATION );
5080         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5081         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5082         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5083     }
5086     /* Combo box to choose line segment type */
5087     {
5088         GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5089         EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5090         ege_select_one_action_set_appearance (act, "compact");
5091         g_object_set_data (holder, "lpetool_line_segment_action", act );
5093         g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5095         sp_line_segment_build_list (holder);
5097         g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5098         gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5099         gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5100     }
5102     /* Display measuring info for selected items */
5103     {
5104         InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5105                                                       _("Display measuring info"),
5106                                                       _("Display measuring info for selected items"),
5107                                                       "lpetool_measuring_info",
5108                                                       Inkscape::ICON_SIZE_DECORATION );
5109         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5110         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
5111         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.lpetool", "show_measuring_info", 1 ) );
5112     }
5114     // add the units menu
5115     {
5116         GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5117         gtk_action_group_add_action( mainActions, act );
5118         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5119         g_object_set_data(holder, "lpetool_units_action", act);
5120         gtk_action_set_sensitive(act, prefs_get_int_attribute ("tools.lpetool", "show_measuring_info", 1));
5121     }
5123     /* Open LPE dialog (to adapt parameters numerically) */
5124     {
5125         InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
5126                                                       _("Open LPE dialog"),
5127                                                       _("Open LPE dialog (to adapt parameters numerically)"),
5128                                                       "lpetool_open_lpe_dialog",
5129                                                       Inkscape::ICON_SIZE_DECORATION );
5130         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5131         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
5132         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5133     }
5135     //watch selection
5136     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5138     sigc::connection *c_selection_modified =
5139         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5140                               (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
5141     pool->add_connection ("selection-modified", c_selection_modified);
5143     sigc::connection *c_selection_changed =
5144         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5145                               (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5146     pool->add_connection ("selection-changed", c_selection_changed);
5149 //########################
5150 //##       Eraser       ##
5151 //########################
5153 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5155     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5156     gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
5157     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5158         prefs_set_int_attribute( "tools.eraser", "mode", eraserMode );
5159     }
5161     // only take action if run by the attr_changed listener
5162     if (!g_object_get_data( tbl, "freeze" )) {
5163         // in turn, prevent listener from responding
5164         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5166         if ( eraserMode != 0 ) {
5167         } else {
5168         }
5169         // TODO finish implementation
5171         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5172     }
5175 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5177     {
5178         /* Width */
5179         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5180         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5181         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5182                                                               _("Pen Width"), _("Width:"),
5183                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
5184                                                               "tools.eraser", "width", 15,
5185                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5186                                                               1, 100, 1.0, 0.0,
5187                                                               labels, values, G_N_ELEMENTS(labels),
5188                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
5189         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5190         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5191         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5192     }
5194     {
5195         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5197         GtkTreeIter iter;
5198         gtk_list_store_append( model, &iter );
5199         gtk_list_store_set( model, &iter,
5200                             0, _("Delete"),
5201                             1, _("Delete objects touched by the eraser"),
5202                             2, "delete_object",
5203                             -1 );
5205         gtk_list_store_append( model, &iter );
5206         gtk_list_store_set( model, &iter,
5207                             0, _("Cut"),
5208                             1, _("Cut out from objects"),
5209                             2, "difference",
5210                             -1 );
5212         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5213         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5214         g_object_set_data( holder, "eraser_mode_action", act );
5216         ege_select_one_action_set_appearance( act, "full" );
5217         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5218         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5219         ege_select_one_action_set_icon_column( act, 2 );
5220         ege_select_one_action_set_tooltip_column( act, 1  );
5222         gint eraserMode = (prefs_get_int_attribute("tools.eraser", "mode", 0) != 0) ? 1 : 0;
5223         ege_select_one_action_set_active( act, eraserMode );
5224         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
5225     }
5229 //########################
5230 //##    Text Toolbox    ##
5231 //########################
5232 /*
5233 static void
5234 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
5236     //Call back for letter sizing spinbutton
5239 static void
5240 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
5242     //Call back for line height spinbutton
5245 static void
5246 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5248     //Call back for horizontal kerning spinbutton
5251 static void
5252 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5254     //Call back for vertical kerning spinbutton
5257 static void
5258 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
5260     //Call back for letter rotation spinbutton
5261 }*/
5263 namespace {
5265 bool popdown_visible = false;
5266 bool popdown_hasfocus = false;
5268 void
5269 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
5271     SPStyle *query =
5272         sp_style_new (SP_ACTIVE_DOCUMENT);
5274 //    int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5276     int result_family =
5277         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5279     int result_style =
5280         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5282     int result_numbers =
5283         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5285     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5287     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5288     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
5289         // there are no texts in selection, read from prefs
5290  
5291         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
5292         if (repr) {
5293             sp_style_read_from_repr (query, repr);
5294             if (g_object_get_data(tbl, "text_style_from_prefs")) {
5295                 // do not reset the toolbar style from prefs if we already did it last time
5296                 sp_style_unref(query);
5297                 return;
5298             }
5299             g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
5300         } else {
5301             sp_style_unref(query);
5302             return;
5303         }
5304     } else {
5305         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
5306     }
5308     if (query->text)
5309     {
5310         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
5311             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5312             gtk_entry_set_text (GTK_ENTRY (entry), "");
5314         } else if (query->text->font_specification.value || query->text->font_family.value) {
5316             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5318             // Get the font that corresponds
5319             Glib::ustring familyName;
5321             font_instance * font = font_factory::Default()->FaceFromStyle(query);
5322             if (font) {
5323                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
5324                 font->Unref();
5325                 font = NULL;
5326             }
5328             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
5330             Gtk::TreePath path;
5331             try {
5332                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
5333             } catch (...) {
5334                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
5335                 sp_style_unref(query);
5336                 return;
5337             }
5339             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5340             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5342             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
5344             gtk_tree_selection_select_path (tselection, path.gobj());
5345             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5347             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
5348         }
5350         //Size
5351         {
5352             GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
5353             gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
5354             g_object_set_data(tbl, "size-block", gpointer(1));
5355             gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
5356             g_object_set_data(tbl, "size-block", gpointer(0));
5357             g_free(str);
5358         }
5360         //Anchor
5361         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
5362         {
5363             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
5364             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5365             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5366             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5367         }
5368         else
5369         {
5370             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
5371             {
5372                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
5373                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5374                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5375                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5376             }
5377             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
5378             {
5379                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
5380                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5381                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5382                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5383             }
5384             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
5385             {
5386                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
5387                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5388                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5389                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5390             }
5391         }
5393         //Style
5394         {
5395             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
5397             gboolean active = gtk_toggle_button_get_active (button);
5398             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
5400             if (active != check)
5401             {
5402                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5403                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5404                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5405             }
5406         }
5408         {
5409             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
5411             gboolean active = gtk_toggle_button_get_active (button);
5412             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
5414             if (active != check)
5415             {
5416                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5417                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5418                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5419             }
5420         }
5422         //Orientation
5423         //locking both buttons, changing one affect all group (both)
5424         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
5425         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5427         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
5428         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
5430         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
5431         {
5432             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5433         }
5434         else
5435         {
5436             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
5437         }
5438         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5439         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
5440     }
5442     sp_style_unref(query);
5445 void
5446 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
5448     sp_text_toolbox_selection_changed (selection, tbl);
5451 void
5452 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
5454     sp_text_toolbox_selection_changed (NULL, tbl);
5457 void
5458 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
5459                                 GObject             *tbl)
5461     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
5462     GtkTreeModel *model = 0;
5463     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5464     GtkTreeIter   iter;
5465     char         *family = 0;
5467     gdk_pointer_ungrab (GDK_CURRENT_TIME);
5468     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5470     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
5471         return;
5472     }
5474     gtk_tree_model_get (model, &iter, 0, &family, -1);
5476     if (g_object_get_data (G_OBJECT (selection), "block"))
5477     {
5478         gtk_entry_set_text (GTK_ENTRY (entry), family);
5479         return;
5480     }
5482     gtk_entry_set_text (GTK_ENTRY (entry), family);
5484     SPStyle *query =
5485         sp_style_new (SP_ACTIVE_DOCUMENT);
5487     int result_fontspec =
5488         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5490     //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5492     SPCSSAttr *css = sp_repr_css_attr_new ();
5495     // First try to get the font spec from the stored value
5496     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
5498     if (fontSpec.empty()) {
5499         // Construct a new font specification if it does not yet exist
5500         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5501         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5502         fontFromStyle->Unref();
5503     }
5505     if (!fontSpec.empty()) {
5506         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
5507         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
5508             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
5509             if (font) {
5510                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5512                 // Set all the these just in case they were altered when finding the best
5513                 // match for the new family and old style...
5515                 gchar c[256];
5517                 font->Family(c, 256);
5518                 sp_repr_css_set_property (css, "font-family", c);
5520                 font->Attribute( "weight", c, 256);
5521                 sp_repr_css_set_property (css, "font-weight", c);
5523                 font->Attribute("style", c, 256);
5524                 sp_repr_css_set_property (css, "font-style", c);
5526                 font->Attribute("stretch", c, 256);
5527                 sp_repr_css_set_property (css, "font-stretch", c);
5529                 font->Attribute("variant", c, 256);
5530                 sp_repr_css_set_property (css, "font-variant", c);
5532                 font->Unref();
5533             }
5534         }
5535     }
5537     // If querying returned nothing, set the default style of the tool (for new texts)
5538     if (result_fontspec == QUERY_STYLE_NOTHING)
5539     {
5540         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5541         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
5542     }
5543     else
5544     {
5545         sp_desktop_set_style (desktop, css, true, true);
5546     }
5548     sp_style_unref(query);
5550     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5551                                    _("Text: Change font family"));
5552     sp_repr_css_attr_unref (css);
5553     g_free(family);
5554     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5556     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5559 /* This is where execution comes when the contents of the font family box have been completed
5560    by the press of the return key */
5561 void
5562 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
5563                                        GObject      *tbl)
5565     const char *family = gtk_entry_get_text (entry);   // Fetch the requested font family
5567 // Try to match that to a known font. If not, then leave current font alone and remain focused on text box
5568     try {
5569         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
5570         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5571         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5572         gtk_tree_selection_select_path (selection, path.gobj());
5573         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5574         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5575     } catch (...) {
5576         if (family && strlen (family))
5577         {
5578             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5579         }
5580     }
5583 void
5584 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
5585                                    gpointer          data)
5587     if (g_object_get_data (G_OBJECT (button), "block")) return;
5588     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
5589     int prop = GPOINTER_TO_INT(data);
5591     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5592     SPCSSAttr *css = sp_repr_css_attr_new ();
5594     switch (prop)
5595     {
5596         case 0:
5597         {
5598             sp_repr_css_set_property (css, "text-anchor", "start");
5599             sp_repr_css_set_property (css, "text-align", "start");
5600             break;
5601         }
5602         case 1:
5603         {
5604             sp_repr_css_set_property (css, "text-anchor", "middle");
5605             sp_repr_css_set_property (css, "text-align", "center");
5606             break;
5607         }
5609         case 2:
5610         {
5611             sp_repr_css_set_property (css, "text-anchor", "end");
5612             sp_repr_css_set_property (css, "text-align", "end");
5613             break;
5614         }
5616         case 3:
5617         {
5618             sp_repr_css_set_property (css, "text-anchor", "start");
5619             sp_repr_css_set_property (css, "text-align", "justify");
5620             break;
5621         }
5622     }
5624     SPStyle *query =
5625         sp_style_new (SP_ACTIVE_DOCUMENT);
5626     int result_numbers =
5627         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5629     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5630     if (result_numbers == QUERY_STYLE_NOTHING)
5631     {
5632         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5633     }
5635     sp_style_unref(query);
5637     sp_desktop_set_style (desktop, css, true, true);
5638     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5639                                    _("Text: Change alignment"));
5640     sp_repr_css_attr_unref (css);
5642     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5645 void
5646 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
5647                                gpointer          data)
5649     if (g_object_get_data (G_OBJECT (button), "block")) return;
5651     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5652     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5653     int          prop       = GPOINTER_TO_INT(data);
5654     bool         active     = gtk_toggle_button_get_active (button);
5656     SPStyle *query =
5657         sp_style_new (SP_ACTIVE_DOCUMENT);
5659     int result_fontspec =
5660         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5662     //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5663     //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5664     //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5666     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
5667     Glib::ustring newFontSpec = "";
5669     if (fontSpec.empty()) {
5670         // Construct a new font specification if it does not yet exist
5671         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5672         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5673         fontFromStyle->Unref();
5674     }
5676     switch (prop)
5677     {
5678         case 0:
5679         {
5680             if (!fontSpec.empty()) {
5681                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
5682             }
5683             if (fontSpec != newFontSpec) {
5684                 // Don't even set the bold if the font didn't exist on the system
5685                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
5686             }
5687             break;
5688         }
5690         case 1:
5691         {
5692             if (!fontSpec.empty()) {
5693                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
5694             }
5695             if (fontSpec != newFontSpec) {
5696                 // Don't even set the italic if the font didn't exist on the system
5697                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
5698             }
5699             break;
5700         }
5701     }
5703     if (!newFontSpec.empty()) {
5704         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5705     }
5707     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5708     if (result_fontspec == QUERY_STYLE_NOTHING)
5709     {
5710         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5711     }
5713     sp_style_unref(query);
5715     sp_desktop_set_style (desktop, css, true, true);
5716     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5717                                    _("Text: Change font style"));
5718     sp_repr_css_attr_unref (css);
5720     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5723 void
5724 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
5725                                      gpointer         data)
5727     if (g_object_get_data (G_OBJECT (button), "block")) {
5728         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5729         return;
5730     }
5732     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5733     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5734     int          prop       = GPOINTER_TO_INT(data);
5736     switch (prop)
5737     {
5738         case 0:
5739         {
5740             sp_repr_css_set_property (css, "writing-mode", "lr");
5741             break;
5742         }
5744         case 1:
5745         {
5746             sp_repr_css_set_property (css, "writing-mode", "tb");
5747             break;
5748         }
5749     }
5751     SPStyle *query =
5752         sp_style_new (SP_ACTIVE_DOCUMENT);
5753     int result_numbers =
5754         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5756     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5757     if (result_numbers == QUERY_STYLE_NOTHING)
5758     {
5759         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5760     }
5762     sp_desktop_set_style (desktop, css, true, true);
5763     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5764                                    _("Text: Change orientation"));
5765     sp_repr_css_attr_unref (css);
5767     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5770 gboolean
5771 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5773     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5774     if (!desktop) return FALSE;
5776     switch (get_group0_keyval (event)) {
5777         case GDK_Escape: // defocus
5778             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5779             sp_text_toolbox_selection_changed (NULL, tbl); // update
5780             return TRUE; // I consumed the event
5781             break;
5782     }
5783     return FALSE;
5786 gboolean
5787 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5789     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5790     if (!desktop) return FALSE;
5792     switch (get_group0_keyval (event)) {
5793         case GDK_KP_Enter:
5794         case GDK_Return:
5795         case GDK_Escape: // defocus
5796             gtk_widget_hide (w);
5797             popdown_visible = false;
5798             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5799             return TRUE; // I consumed the event
5800             break;
5801         case GDK_w:
5802         case GDK_W:
5803             if (event->state & GDK_CONTROL_MASK) {
5804                 gtk_widget_hide (w);
5805                 popdown_visible = false;
5806                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5807                 return TRUE; // I consumed the event
5808             }
5809             break;
5810     }
5811     return FALSE;
5815 void
5816 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
5817                                GObject     *tbl)
5819     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5821     if (g_object_get_data (tbl, "size-block")) return;
5823     // If this is not from selecting a size in the list (in which case get_active will give the
5824     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5825     // process this event. This fixes GTK's stupid insistence on sending an activate change every
5826     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5827     if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5828         return;
5830     gdouble value = -1;
5831     {
5832         gchar *endptr;
5833         gchar *const text = gtk_combo_box_get_active_text(cbox);
5834         if (text) {
5835             value = g_strtod(text, &endptr);
5836             if (endptr == text) {  // Conversion failed, non-numeric input.
5837                 value = -1;
5838             }
5839             g_free(text);
5840         }
5841     }
5842     if (value <= 0) {
5843         return; // could not parse value
5844     }
5846     SPCSSAttr *css = sp_repr_css_attr_new ();
5847     Inkscape::CSSOStringStream osfs;
5848     osfs << value;
5849     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5851     SPStyle *query =
5852         sp_style_new (SP_ACTIVE_DOCUMENT);
5853     int result_numbers =
5854         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5856     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5857     if (result_numbers == QUERY_STYLE_NOTHING)
5858     {
5859         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5860     }
5862     sp_style_unref(query);
5864     sp_desktop_set_style (desktop, css, true, true);
5865     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5866                                    _("Text: Change font size"));
5867     sp_repr_css_attr_unref (css);
5869     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5872 gboolean
5873 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5875     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5876     if (!desktop) return FALSE;
5878     if (!g_object_get_data (tbl, "esc-pressed")) {
5879         g_object_set_data (tbl, "enter-pressed", gpointer(1));
5880         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5881         sp_text_toolbox_size_changed (cbox, tbl);
5882         g_object_set_data (tbl, "enter-pressed", gpointer(0));
5883     }
5884     return FALSE; // I consumed the event
5888 gboolean
5889 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5891     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5892     if (!desktop) return FALSE;
5894     switch (get_group0_keyval (event)) {
5895         case GDK_Escape: // defocus
5896             g_object_set_data (tbl, "esc-pressed", gpointer(1));
5897             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5898             g_object_set_data (tbl, "esc-pressed", gpointer(0));
5899             return TRUE; // I consumed the event
5900             break;
5901         case GDK_Return: // defocus
5902         case GDK_KP_Enter:
5903             g_object_set_data (tbl, "enter-pressed", gpointer(1));
5904             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5905             sp_text_toolbox_size_changed (cbox, tbl);
5906             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5907             g_object_set_data (tbl, "enter-pressed", gpointer(0));
5908             return TRUE; // I consumed the event
5909             break;
5910     }
5911     return FALSE;
5914 void
5915 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
5916                                          GObject            *tbl)
5918     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
5919     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5920     int x, y;
5922     if (!popdown_visible)
5923     {
5924         gdk_window_get_origin (widget->window, &x, &y);
5925         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
5926         gtk_widget_show_all (popdown);
5927         //sp_transientize (popdown);
5929         gdk_pointer_grab (widget->window, TRUE,
5930                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
5931                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
5932                                         GDK_POINTER_MOTION_MASK),
5933                           NULL, NULL, GDK_CURRENT_TIME);
5935         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
5937         popdown_visible = true;
5938     }
5939     else
5940     {
5941         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5942         gdk_pointer_ungrab (GDK_CURRENT_TIME);
5943         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5944         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5945         gtk_widget_hide (popdown);
5946         popdown_visible = false;
5947     }
5950 gboolean
5951 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
5952                                  GdkEventFocus    */*event*/,
5953                                  GObject          */*tbl*/)
5955     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
5956     return FALSE;
5959 gboolean
5960 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
5961                                    GdkEventFocus    */*event*/,
5962                                    GObject          */*tbl*/)
5964     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5966     if (popdown_hasfocus) {
5967         gtk_widget_hide (popdown);
5968         popdown_hasfocus = false;
5969         popdown_visible = false;
5970         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5971         return TRUE;
5972     }
5973     return FALSE;
5976 gboolean
5977 sp_text_toolbox_popdown_focus_in (GtkWidget        */*popdown*/,
5978                                    GdkEventFocus    */*event*/,
5979                                    GObject          */*tbl*/)
5981     popdown_hasfocus = true;
5982     return TRUE;
5986 void
5987 cell_data_func  (GtkTreeViewColumn */*column*/,
5988                  GtkCellRenderer   *cell,
5989                  GtkTreeModel      *tree_model,
5990                  GtkTreeIter       *iter,
5991                  gpointer           /*data*/)
5993     gchar *family;
5994     gtk_tree_model_get(tree_model, iter, 0, &family, -1);
5995     gchar *const family_escaped = g_markup_escape_text(family, -1);
5997     static char const *const sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
5998     gchar *const sample_escaped = g_markup_escape_text(sample, -1);
6000     std::stringstream markup;
6001     markup << family_escaped << "  <span foreground='darkgray' font_family='"
6002            << family_escaped << "'>" << sample_escaped << "</span>";
6003     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6005     g_free(family);
6006     g_free(family_escaped);
6007     g_free(sample_escaped);
6010 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
6011     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
6012     if (completion) {
6013         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
6014         g_object_unref (completion);
6015     }
6018 GtkWidget*
6019 sp_text_toolbox_new (SPDesktop *desktop)
6021     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
6022     GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
6024     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
6025     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
6027     GtkTooltips *tt = gtk_tooltips_new();
6028     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
6030     ////////////Family
6031     //Window
6032     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
6033     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
6035     //Entry
6036     GtkWidget           *entry = gtk_entry_new ();
6037     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
6038     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
6039     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
6040     gtk_entry_completion_set_text_column (completion, 0);
6041     gtk_entry_completion_set_minimum_key_length (completion, 1);
6042     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
6043     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
6044     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
6045     gtk_toolbar_append_widget( tbl, entry, "", "" );
6046     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
6048     //Button
6049     GtkWidget   *button = gtk_button_new ();
6050     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
6051     gtk_toolbar_append_widget( tbl, button, "", "");
6053     //Popdown
6054     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
6055     GtkWidget           *treeview = gtk_tree_view_new ();
6057     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
6058     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
6059     gtk_tree_view_column_pack_start (column, cell, FALSE);
6060     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
6061     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
6062     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
6064     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
6065     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
6066     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
6068     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
6070     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
6071     gtk_container_add (GTK_CONTAINER (sw), treeview);
6073     gtk_container_add (GTK_CONTAINER (window), sw);
6074     gtk_widget_set_size_request (window, 300, 450);
6076     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
6077     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
6078     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
6080     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
6082     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
6083     g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
6084     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
6086     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
6087     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6089     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
6090     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
6091     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
6092     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
6093     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
6095     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
6096     GtkWidget *box = gtk_event_box_new ();
6097     gtk_container_add (GTK_CONTAINER (box), image);
6098     gtk_toolbar_append_widget( tbl, box, "", "");
6099     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
6100     GtkTooltips *tooltips = gtk_tooltips_new ();
6101     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
6102     gtk_widget_hide (GTK_WIDGET (box));
6103     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
6105     ////////////Size
6106     gchar const *const sizes[] = {
6107         "4", "6", "8", "9", "10", "11", "12", "13", "14",
6108         "16", "18", "20", "22", "24", "28",
6109         "32", "36", "40", "48", "56", "64", "72", "144"
6110     };
6112     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
6113     for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
6114         gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
6115     }
6116     gtk_widget_set_size_request (cbox, 80, -1);
6117     gtk_toolbar_append_widget( tbl, cbox, "", "");
6118     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
6119     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
6120     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
6121     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
6123     ////////////Text anchor
6124     GtkWidget *group   = gtk_radio_button_new (NULL);
6125     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
6126     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
6128     // left
6129     GtkWidget *rbutton = group;
6130     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6131     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
6132     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6134     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6135     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
6136     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
6137     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
6139     // center
6140     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6141     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6142     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
6143     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6145     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6146     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
6147     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
6148     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
6150     // right
6151     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6152     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6153     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
6154     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6156     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6157     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
6158     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
6159     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
6161     // fill
6162     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6163     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6164     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
6165     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6167     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6168     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
6169     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
6170     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
6172     gtk_toolbar_append_widget( tbl, row, "", "");
6174     //spacer
6175     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6177     ////////////Text style
6178     row = gtk_hbox_new (FALSE, 4);
6180     // bold
6181     rbutton = gtk_toggle_button_new ();
6182     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6183     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
6184     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6185     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
6187     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6188     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
6189     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
6191     // italic
6192     rbutton = gtk_toggle_button_new ();
6193     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6194     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
6195     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6196     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
6198     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6199     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
6200     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
6202     gtk_toolbar_append_widget( tbl, row, "", "");
6204     //spacer
6205     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6207     ////////////Text orientation
6208     group   = gtk_radio_button_new (NULL);
6209     row     = gtk_hbox_new (FALSE, 4);
6210     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
6212     // horizontal
6213     rbutton = group;
6214     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6215     gtk_container_add           (GTK_CONTAINER (rbutton),
6216                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
6217     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6218     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
6220     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6221     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
6222     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
6224     // vertical
6225     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6226     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6227     gtk_container_add           (GTK_CONTAINER (rbutton),
6228                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
6229     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6230     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
6232     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6233     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
6234     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
6235     gtk_toolbar_append_widget( tbl, row, "", "" );
6238     //watch selection
6239     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
6241     sigc::connection *c_selection_changed =
6242         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
6243                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
6244     pool->add_connection ("selection-changed", c_selection_changed);
6246     sigc::connection *c_selection_modified =
6247         new sigc::connection (sp_desktop_selection (desktop)->connectModified
6248                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
6249     pool->add_connection ("selection-modified", c_selection_modified);
6251     sigc::connection *c_subselection_changed =
6252         new sigc::connection (desktop->connectToolSubselectionChanged
6253                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
6254     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
6256     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
6259     gtk_widget_show_all( GTK_WIDGET(tbl) );
6261     return GTK_WIDGET(tbl);
6262 } // end of sp_text_toolbox_new()
6264 }//<unnamed> namespace
6267 //#########################
6268 //##      Connector      ##
6269 //#########################
6271 static void sp_connector_path_set_avoid(void)
6273     cc_selection_set_avoid(true);
6277 static void sp_connector_path_set_ignore(void)
6279     cc_selection_set_avoid(false);
6284 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
6286     // quit if run by the _changed callbacks
6287     if (g_object_get_data( tbl, "freeze" )) {
6288         return;
6289     }
6291     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
6292     SPDocument *doc = sp_desktop_document(desktop);
6294     if (!sp_document_get_undo_sensitive(doc))
6295     {
6296         return;
6297     }
6299     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6301     if ( repr->attribute("inkscape:connector-spacing") ) {
6302         gdouble priorValue = gtk_adjustment_get_value(adj);
6303         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
6304         if ( priorValue == gtk_adjustment_get_value(adj) ) {
6305             return;
6306         }
6307     } else if ( adj->value == defaultConnSpacing ) {
6308         return;
6309     }
6311     // in turn, prevent callbacks from responding
6312     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6314     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
6315     SP_OBJECT(desktop->namedview)->updateRepr();
6317     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
6318     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
6319         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
6320         NR::Matrix m = NR::identity();
6321         avoid_item_move(&m, item);
6322     }
6324     if (items) {
6325         g_slist_free(items);
6326     }
6328     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
6329             _("Change connector spacing"));
6331     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6333     spinbutton_defocus(GTK_OBJECT(tbl));
6336 static void sp_connector_graph_layout(void)
6338     if (!SP_ACTIVE_DESKTOP) return;
6340     // hack for clones, see comment in align-and-distribute.cpp
6341     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
6342     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
6344     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
6346     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
6348     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
6351 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6353     if ( gtk_toggle_action_get_active( act ) ) {
6354         prefs_set_string_attribute("tools.connector", "directedlayout",
6355                 "true");
6356     } else {
6357         prefs_set_string_attribute("tools.connector", "directedlayout",
6358                 "false");
6359     }
6362 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6364     if ( gtk_toggle_action_get_active( act ) ) {
6365         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
6366                 "true");
6367     } else {
6368         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
6369                 "false");
6370     }
6374 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
6376     prefs_set_double_attribute("tools.connector", "length", adj->value);
6377     spinbutton_defocus(GTK_OBJECT(tbl));
6380 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
6381                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
6382                                             bool /*is_interactive*/, gpointer data)
6384     GtkWidget *tbl = GTK_WIDGET(data);
6386     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6387         return;
6388     }
6389     if (strcmp(name, "inkscape:connector-spacing") != 0) {
6390         return;
6391     }
6393     GtkAdjustment *adj = (GtkAdjustment*)
6394             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
6395     gdouble spacing = defaultConnSpacing;
6396     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
6398     gtk_adjustment_set_value(adj, spacing);
6402 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
6403     NULL, /* child_added */
6404     NULL, /* child_removed */
6405     connector_tb_event_attr_changed,
6406     NULL, /* content_changed */
6407     NULL  /* order_changed */
6408 };
6411 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
6413     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
6415     {
6416         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
6417                                           _("Avoid"),
6418                                           _("Make connectors avoid selected objects"),
6419                                           "connector_avoid",
6420                                           secondarySize );
6421         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
6422         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6423     }
6425     {
6426         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
6427                                           _("Ignore"),
6428                                           _("Make connectors ignore selected objects"),
6429                                           "connector_ignore",
6430                                           secondarySize );
6431         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
6432         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6433     }
6435     EgeAdjustmentAction* eact = 0;
6437     // Spacing spinbox
6438     eact = create_adjustment_action( "ConnectorSpacingAction",
6439                                      _("Connector Spacing"), _("Spacing:"),
6440                                      _("The amount of space left around objects by auto-routing connectors"),
6441                                      "tools.connector", "spacing", defaultConnSpacing,
6442                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
6443                                      0, 100, 1.0, 10.0,
6444                                      0, 0, 0,
6445                                      connector_spacing_changed, 1, 0 );
6446     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6448     // Graph (connector network) layout
6449     {
6450         InkAction* inky = ink_action_new( "ConnectorGraphAction",
6451                                           _("Graph"),
6452                                           _("Nicely arrange selected connector network"),
6453                                           "graph_layout",
6454                                           secondarySize );
6455         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
6456         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6457     }
6459     // Default connector length spinbox
6460     eact = create_adjustment_action( "ConnectorLengthAction",
6461                                      _("Connector Length"), _("Length:"),
6462                                      _("Ideal length for connectors when layout is applied"),
6463                                      "tools.connector", "length", 100,
6464                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
6465                                      10, 1000, 10.0, 100.0,
6466                                      0, 0, 0,
6467                                      connector_length_changed, 1, 0 );
6468     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6471     // Directed edges toggle button
6472     {
6473         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
6474                                                       _("Downwards"),
6475                                                       _("Make connectors with end-markers (arrows) point downwards"),
6476                                                       "directed_graph",
6477                                                       Inkscape::ICON_SIZE_DECORATION );
6478         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6480         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
6481         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6482                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6484         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
6485     }
6487     // Avoid overlaps toggle button
6488     {
6489         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
6490                                                       _("Remove overlaps"),
6491                                                       _("Do not allow overlapping shapes"),
6492                                                       "remove_overlaps",
6493                                                       Inkscape::ICON_SIZE_DECORATION );
6494         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6496         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
6497         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6498                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6500         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
6501     }
6503     // Code to watch for changes to the connector-spacing attribute in
6504     // the XML.
6505     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6506     g_assert(repr != NULL);
6508     purge_repr_listener( holder, holder );
6510     if (repr) {
6511         g_object_set_data( holder, "repr", repr );
6512         Inkscape::GC::anchor(repr);
6513         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
6514         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
6515     }
6516 } // end of sp_connector_toolbox_prep()
6519 //#########################
6520 //##     Paintbucket     ##
6521 //#########################
6523 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
6525     gint channels = ege_select_one_action_get_active( act );
6526     flood_channels_set_channels( channels );
6529 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
6531     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
6534 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
6536     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
6539 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
6541     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
6542     SPUnit const *unit = tracker->getActiveUnit();
6544     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
6546     prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
6549 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
6551     // FIXME: make defaults settable via Inkscape Options
6552     struct KeyValue {
6553         char const *key;
6554         double value;
6555     } const key_values[] = {
6556         {"threshold", 15},
6557         {"offset", 0.0}
6558     };
6560     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
6561         KeyValue const &kv = key_values[i];
6562         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
6563         if ( adj ) {
6564             gtk_adjustment_set_value(adj, kv.value);
6565         }
6566     }
6568     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
6569     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
6570     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
6571     ege_select_one_action_set_active( autogap_action, 0 );
6574 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6576     EgeAdjustmentAction* eact = 0;
6578     {
6579         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6581         GList* items = 0;
6582         gint count = 0;
6583         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
6584         {
6585             GtkTreeIter iter;
6586             gtk_list_store_append( model, &iter );
6587             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6588             count++;
6589         }
6590         g_list_free( items );
6591         items = 0;
6592         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
6593         g_object_set( act1, "short_label", _("Fill by:"), NULL );
6594         ege_select_one_action_set_appearance( act1, "compact" );
6595         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
6596         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
6597         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
6598         g_object_set_data( holder, "channels_action", act1 );
6599     }
6601     // Spacing spinbox
6602     {
6603         eact = create_adjustment_action(
6604             "ThresholdAction",
6605             _("Fill Threshold"), _("Threshold:"),
6606             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
6607             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
6608             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
6609             0, 0, 0,
6610             paintbucket_threshold_changed, 1, 0 );
6612         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6613         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6614     }
6616     // Create the units menu.
6617     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
6618     const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
6619     if (stored_unit)
6620         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
6621     g_object_set_data( holder, "tracker", tracker );
6622     {
6623         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
6624         gtk_action_group_add_action( mainActions, act );
6625     }
6627     // Offset spinbox
6628     {
6629         eact = create_adjustment_action(
6630             "OffsetAction",
6631             _("Grow/shrink by"), _("Grow/shrink by:"),
6632             _("The amount to grow (positive) or shrink (negative) the created fill path"),
6633             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
6634             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
6635             0, 0, 0,
6636             paintbucket_offset_changed, 1, 2);
6637         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
6639         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6640     }
6642     /* Auto Gap */
6643     {
6644         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6646         GList* items = 0;
6647         gint count = 0;
6648         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
6649         {
6650             GtkTreeIter iter;
6651             gtk_list_store_append( model, &iter );
6652             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6653             count++;
6654         }
6655         g_list_free( items );
6656         items = 0;
6657         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
6658         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
6659         ege_select_one_action_set_appearance( act2, "compact" );
6660         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
6661         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
6662         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
6663         g_object_set_data( holder, "autogap_action", act2 );
6664     }
6666     /* Reset */
6667     {
6668         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
6669                                           _("Defaults"),
6670                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
6671                                           GTK_STOCK_CLEAR );
6672         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
6673         gtk_action_group_add_action( mainActions, act );
6674         gtk_action_set_sensitive( act, TRUE );
6675     }
6679 /*
6680   Local Variables:
6681   mode:c++
6682   c-file-style:"stroustrup"
6683   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
6684   indent-tabs-mode:nil
6685   fill-column:99
6686   End:
6687 */
6688 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :