Code

Option to display measuring info in geometry tool
[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         "  </toolbar>"
457         "  <toolbar name='DropperToolbar'>"
458         "    <toolitem action='DropperOpacityAction' />"
459         "    <toolitem action='DropperPickAlphaAction' />"
460         "    <toolitem action='DropperSetAlphaAction' />"
461         "  </toolbar>"
463         "  <toolbar name='ConnectorToolbar'>"
464         "    <toolitem action='ConnectorAvoidAction' />"
465         "    <toolitem action='ConnectorIgnoreAction' />"
466         "    <toolitem action='ConnectorSpacingAction' />"
467         "    <toolitem action='ConnectorGraphAction' />"
468         "    <toolitem action='ConnectorLengthAction' />"
469         "    <toolitem action='ConnectorDirectedAction' />"
470         "    <toolitem action='ConnectorOverlapAction' />"
471         "  </toolbar>"
473         "</ui>"
476 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
478 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
480 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
481 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
483 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
484 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
486 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
487 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
490 GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick( GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
491                                                               Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
492                                                               Inkscape::UI::View::View *view, GtkTooltips *tt);
494 class VerbAction : public Gtk::Action {
495 public:
496     static Glib::RefPtr<VerbAction> create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
498     virtual ~VerbAction();
499     virtual void set_active(bool active = true);
501 protected:
502     virtual Gtk::Widget* create_menu_item_vfunc();
503     virtual Gtk::Widget* create_tool_item_vfunc();
505     virtual void connect_proxy_vfunc(Gtk::Widget* proxy);
506     virtual void disconnect_proxy_vfunc(Gtk::Widget* proxy);
508     virtual void on_activate();
510 private:
511     Inkscape::Verb* verb;
512     Inkscape::Verb* verb2;
513     Inkscape::UI::View::View *view;
514     GtkTooltips *tooltips;
515     bool active;
517     VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
518 };
521 Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips)
523     Glib::RefPtr<VerbAction> result;
524     SPAction *action = verb->get_action(view);
525     if ( action ) {
526         //SPAction* action2 = verb2 ? verb2->get_action(view) : 0;
527         result = Glib::RefPtr<VerbAction>(new VerbAction(verb, verb2, view, tooltips));
528     }
530     return result;
533 VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips) :
534     Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(verb->get_image()), Glib::ustring(_(verb->get_name())), Glib::ustring(_(verb->get_tip()))),
535     verb(verb),
536     verb2(verb2),
537     view(view),
538     tooltips(tooltips),
539     active(false)
543 VerbAction::~VerbAction()
547 Gtk::Widget* VerbAction::create_menu_item_vfunc()
549 // First call in to get the icon rendered if present in SVG
550     Gtk::Widget *widget = sp_icon_get_icon( property_stock_id().get_value().get_string(), Inkscape::ICON_SIZE_MENU );
551     delete widget;
552     widget = 0;
554     Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
555 //     g_message("create_menu_item_vfunc() = %p  for '%s'", widg, verb->get_id());
556     return widg;
559 Gtk::Widget* VerbAction::create_tool_item_vfunc()
561 //     Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
562     Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
563     GtkWidget* toolbox = 0;
564     GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
565                                                                           SP_BUTTON_TYPE_TOGGLE,
566                                                                           verb,
567                                                                           verb2,
568                                                                           view,
569                                                                           tooltips );
570     if ( active ) {
571         sp_button_toggle_set_down( SP_BUTTON(button), active);
572     }
573     gtk_widget_show_all( button );
574     Gtk::Widget* wrapped = Glib::wrap(button);
575     Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
576     holder->add(*wrapped);
578 //     g_message("create_tool_item_vfunc() = %p  for '%s'", holder, verb->get_id());
579     return holder;
582 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
584 //     g_message("connect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
585     Gtk::Action::connect_proxy_vfunc(proxy);
588 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
590 //     g_message("disconnect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
591     Gtk::Action::disconnect_proxy_vfunc(proxy);
594 void VerbAction::set_active(bool active)
596     this->active = active;
597     Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
598     for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
599         Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
600         if (ti) {
601             // *should* have one child that is the SPButton
602             Gtk::Widget* child = ti->get_child();
603             if ( child && SP_IS_BUTTON(child->gobj()) ) {
604                 SPButton* button = SP_BUTTON(child->gobj());
605                 sp_button_toggle_set_down( button, active );
606             }
607         }
608     }
611 void VerbAction::on_activate()
613     if ( verb ) {
614         SPAction *action = verb->get_action(view);
615         if ( action ) {
616             sp_action_perform(action, 0);
617         }
618     }
621 /* Global text entry widgets necessary for update */
622 /* GtkWidget *dropper_rgb_entry,
623           *dropper_opacity_entry ; */
624 // should be made a private member once this is converted to class
626 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
627     connection->disconnect();
628     delete connection;
631 static void purge_repr_listener( GObject* obj, GObject* tbl )
633     (void)obj;
634     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
635     if (oldrepr) { // remove old listener
636         sp_repr_remove_listener_by_data(oldrepr, tbl);
637         Inkscape::GC::release(oldrepr);
638         oldrepr = 0;
639         g_object_set_data( tbl, "repr", NULL );
640     }
643 GtkWidget *
644 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
645                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
646                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
648     SPAction *action = verb->get_action(view);
649     if (!action) return NULL;
651     SPAction *doubleclick_action;
652     if (doubleclick_verb)
653         doubleclick_action = doubleclick_verb->get_action(view);
654     else
655         doubleclick_action = NULL;
657     /* fixme: Handle sensitive/unsensitive */
658     /* fixme: Implement sp_button_new_from_action */
659     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
660     gtk_widget_show(b);
663     unsigned int shortcut = sp_shortcut_get_primary(verb);
664     if (shortcut) {
665         gchar key[256];
666         sp_ui_shortcut_string(shortcut, key);
667         gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
668         if ( t ) {
669             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
670         }
671         g_free(tip);
672     } else {
673         if ( t ) {
674             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
675         }
676     }
678     return b;
682 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
684     SPAction* targetAction = SP_ACTION(user_data);
685     if ( targetAction ) {
686         sp_action_perform( targetAction, NULL );
687     }
690 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
692     if ( data ) {
693         GtkAction* act = GTK_ACTION(data);
694         gtk_action_set_sensitive( act, sensitive );
695     }
698 static SPActionEventVector action_event_vector = {
699     {NULL},
700     NULL,
701     NULL,
702     sp_action_action_set_sensitive,
703     NULL,
704     NULL
705 };
707 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
709     GtkAction* act = 0;
711     SPAction* targetAction = verb->get_action(view);
712     InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size  );
713     act = GTK_ACTION(inky);
714     gtk_action_set_sensitive( act, targetAction->sensitive );
716     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
718     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
719     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
721     return act;
724 Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
726     Inkscape::UI::View::View *view = desktop;
727     gint verbsToUse[] = {
728         // disabled until we have icons for them:
729         //find
730         //SP_VERB_EDIT_TILE,
731         //SP_VERB_EDIT_UNTILE,
732         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
733         SP_VERB_DIALOG_DISPLAY,
734         SP_VERB_DIALOG_FILL_STROKE,
735         SP_VERB_DIALOG_NAMEDVIEW,
736         SP_VERB_DIALOG_TEXT,
737         SP_VERB_DIALOG_XML_EDITOR,
738         SP_VERB_EDIT_CLONE,
739         SP_VERB_EDIT_COPY,
740         SP_VERB_EDIT_CUT,
741         SP_VERB_EDIT_DUPLICATE,
742         SP_VERB_EDIT_PASTE,
743         SP_VERB_EDIT_REDO,
744         SP_VERB_EDIT_UNDO,
745         SP_VERB_EDIT_UNLINK_CLONE,
746         SP_VERB_FILE_EXPORT,
747         SP_VERB_FILE_IMPORT,
748         SP_VERB_FILE_NEW,
749         SP_VERB_FILE_OPEN,
750         SP_VERB_FILE_PRINT,
751         SP_VERB_FILE_SAVE,
752         SP_VERB_OBJECT_TO_CURVE,
753         SP_VERB_SELECTION_GROUP,
754         SP_VERB_SELECTION_OUTLINE,
755         SP_VERB_SELECTION_UNGROUP,
756         SP_VERB_ZOOM_1_1,
757         SP_VERB_ZOOM_1_2,
758         SP_VERB_ZOOM_2_1,
759         SP_VERB_ZOOM_DRAWING,
760         SP_VERB_ZOOM_IN,
761         SP_VERB_ZOOM_NEXT,
762         SP_VERB_ZOOM_OUT,
763         SP_VERB_ZOOM_PAGE,
764         SP_VERB_ZOOM_PAGE_WIDTH,
765         SP_VERB_ZOOM_PREV,
766         SP_VERB_ZOOM_SELECTION,
767     };
769     Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
771     static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
772     Glib::RefPtr<Gtk::ActionGroup> mainActions;
773     if ( groups.find(desktop) != groups.end() ) {
774         mainActions = groups[desktop];
775     }
777     if ( !mainActions ) {
778         mainActions = Gtk::ActionGroup::create("main");
779         groups[desktop] = mainActions;
780     }
782     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
783         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
784         if ( verb ) {
785             if (!mainActions->get_action(verb->get_id())) {
786                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
787                 mainActions->add(Glib::wrap(act));
788             }
789         }
790     }
792     if ( !mainActions->get_action("ToolZoom") ) {
793         GtkTooltips *tt = gtk_tooltips_new();
794         for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
795             Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
796             if ( va ) {
797                 mainActions->add(va);
798                 if ( i == 0 ) {
799                     va->set_active(true);
800                 }
801             }
802         }
803     }
806     return mainActions;
810 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
812     gtk_widget_set_size_request( widget,
813                                  widget->allocation.width,
814                                  widget->allocation.height );
817 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
819     gtk_widget_set_size_request( widget, -1, -1 );
824 GtkWidget *
825 sp_tool_toolbox_new()
827     GtkTooltips *tt = gtk_tooltips_new();
828     GtkWidget* tb = gtk_toolbar_new();
829     gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
830     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
832     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
833     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
835     gtk_widget_set_sensitive(tb, FALSE);
837     GtkWidget *hb = gtk_handle_box_new();
838     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
839     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
840     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
842     gtk_container_add(GTK_CONTAINER(hb), tb);
843     gtk_widget_show(GTK_WIDGET(tb));
845     sigc::connection* conn = new sigc::connection;
846     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
848     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
849     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
851     return hb;
854 GtkWidget *
855 sp_aux_toolbox_new()
857     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
859     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
861     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
863     gtk_widget_set_sensitive(tb, FALSE);
865     GtkWidget *hb = gtk_handle_box_new();
866     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
867     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
868     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
870     gtk_container_add(GTK_CONTAINER(hb), tb);
871     gtk_widget_show(GTK_WIDGET(tb));
873     sigc::connection* conn = new sigc::connection;
874     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
876     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
877     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
879     return hb;
882 //####################################
883 //# Commands Bar
884 //####################################
886 GtkWidget *
887 sp_commands_toolbox_new()
889     GtkWidget *tb = gtk_toolbar_new();
891     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
892     gtk_widget_set_sensitive(tb, FALSE);
894     GtkWidget *hb = gtk_handle_box_new();
895     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
896     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
897     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
899     gtk_container_add(GTK_CONTAINER(hb), tb);
900     gtk_widget_show(GTK_WIDGET(tb));
902     sigc::connection* conn = new sigc::connection;
903     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
905     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
906     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
908     return hb;
912 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
913                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
914                                                        gchar const *path, gchar const *data, gdouble def,
915                                                        GtkWidget *focusTarget,
916                                                        GtkWidget *us,
917                                                        GObject *dataKludge,
918                                                        gboolean altx, gchar const *altx_mark,
919                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
920                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
921                                                        void (*callback)(GtkAdjustment *, GObject *),
922                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
924     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
925                                                              lower, upper, step, page, page ) );
926     if (us) {
927         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
928     }
930     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
932     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
933     if ( shortLabel ) {
934         g_object_set( act, "short_label", shortLabel, NULL );
935     }
937     if ( (descrCount > 0) && descrLabels && descrValues ) {
938         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
939     }
941     if ( focusTarget ) {
942         ege_adjustment_action_set_focuswidget( act, focusTarget );
943     }
945     if ( altx && altx_mark ) {
946         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
947     }
949     if ( dataKludge ) {
950         g_object_set_data( dataKludge, data, adj );
951     }
953     // Using a cast just to make sure we pass in the right kind of function pointer
954     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
956     return act;
960 //####################################
961 //# node editing callbacks
962 //####################################
964 /**
965  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
966  */
967 static ShapeEditor *get_current_shape_editor()
969     if (!SP_ACTIVE_DESKTOP) {
970         return NULL;
971     }
973     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
975     if (!SP_IS_NODE_CONTEXT(event_context)) {
976         return NULL;
977     }
979     return SP_NODE_CONTEXT(event_context)->shape_editor;
983 void
984 sp_node_path_edit_add(void)
986     ShapeEditor *shape_editor = get_current_shape_editor();
987     if (shape_editor) shape_editor->add_node();
990 void
991 sp_node_path_edit_delete(void)
993     ShapeEditor *shape_editor = get_current_shape_editor();
994     if (shape_editor) shape_editor->delete_nodes_preserving_shape();
997 void
998 sp_node_path_edit_delete_segment(void)
1000     ShapeEditor *shape_editor = get_current_shape_editor();
1001     if (shape_editor) shape_editor->delete_segment();
1004 void
1005 sp_node_path_edit_break(void)
1007     ShapeEditor *shape_editor = get_current_shape_editor();
1008     if (shape_editor) shape_editor->break_at_nodes();
1011 void
1012 sp_node_path_edit_join(void)
1014     ShapeEditor *shape_editor = get_current_shape_editor();
1015     if (shape_editor) shape_editor->join_nodes();
1018 void
1019 sp_node_path_edit_join_segment(void)
1021     ShapeEditor *shape_editor = get_current_shape_editor();
1022     if (shape_editor) shape_editor->join_segments();
1025 void
1026 sp_node_path_edit_toline(void)
1028     ShapeEditor *shape_editor = get_current_shape_editor();
1029     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1032 void
1033 sp_node_path_edit_tocurve(void)
1035     ShapeEditor *shape_editor = get_current_shape_editor();
1036     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1039 void
1040 sp_node_path_edit_cusp(void)
1042     ShapeEditor *shape_editor = get_current_shape_editor();
1043     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1046 void
1047 sp_node_path_edit_smooth(void)
1049     ShapeEditor *shape_editor = get_current_shape_editor();
1050     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1053 void
1054 sp_node_path_edit_symmetrical(void)
1056     ShapeEditor *shape_editor = get_current_shape_editor();
1057     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1060 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1061     bool show = gtk_toggle_action_get_active( act );
1062     prefs_set_int_attribute ("tools.nodes", "show_handles",  show ? 1 : 0);
1063     ShapeEditor *shape_editor = get_current_shape_editor();
1064     if (shape_editor) shape_editor->show_handles(show);
1067 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1068     bool show = gtk_toggle_action_get_active( act );
1069     prefs_set_int_attribute ("tools.nodes", "show_helperpath",  show ? 1 : 0);
1070     ShapeEditor *shape_editor = get_current_shape_editor();
1071     if (shape_editor) shape_editor->show_helperpath(show);
1074 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1075     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1078 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1079     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1082 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1083     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1086 /* is called when the node selection is modified */
1087 static void
1088 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1090     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1091     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1092     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1093     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1095     // quit if run by the attr_changed listener
1096     if (g_object_get_data( tbl, "freeze" )) {
1097         return;
1098     }
1100     // in turn, prevent listener from responding
1101     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1103     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1104     SPUnit const *unit = tracker->getActiveUnit();
1106     ShapeEditor *shape_editor = get_current_shape_editor();
1107     if (shape_editor && shape_editor->has_nodepath()) {
1108         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1109         int n_selected = 0;
1110         if (nodepath) {
1111             n_selected = nodepath->numSelected();
1112         }
1114         if (n_selected == 0) {
1115             gtk_action_set_sensitive(xact, FALSE);
1116             gtk_action_set_sensitive(yact, FALSE);
1117         } else {
1118             gtk_action_set_sensitive(xact, TRUE);
1119             gtk_action_set_sensitive(yact, TRUE);
1120             Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1121             Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1123             if (n_selected == 1) {
1124                 Geom::Point sel_node = nodepath->singleSelectedCoords();
1125                 if (oldx != sel_node[Geom::X] || oldy != sel_node[Geom::Y]) {
1126                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[Geom::X], *unit));
1127                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[Geom::Y], *unit));
1128                 }
1129             } else {
1130                 boost::optional<Geom::Coord> x = sp_node_selected_common_coord(nodepath, Geom::X);
1131                 boost::optional<Geom::Coord> y = sp_node_selected_common_coord(nodepath, Geom::Y);
1132                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1133                     /* Note: Currently x and y will always have a value, even if the coordinates of the
1134                        selected nodes don't coincide (in this case we use the coordinates of the center
1135                        of the bounding box). So the entries are never set to zero. */
1136                     // FIXME: Maybe we should clear the entry if several nodes are selected
1137                     //        instead of providing a kind of average value
1138                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1139                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1140                 }
1141             }
1142         }
1143     } else {
1144         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1145         gtk_action_set_sensitive(xact, FALSE);
1146         gtk_action_set_sensitive(yact, FALSE);
1147     }
1149     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1152 static void
1153 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1155     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1157     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1158     SPUnit const *unit = tracker->getActiveUnit();
1160     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1161         prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
1162     }
1164     // quit if run by the attr_changed listener
1165     if (g_object_get_data( tbl, "freeze" )) {
1166         return;
1167     }
1169     // in turn, prevent listener from responding
1170     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1172     ShapeEditor *shape_editor = get_current_shape_editor();
1173     if (shape_editor && shape_editor->has_nodepath()) {
1174         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1175         if (!strcmp(value_name, "x")) {
1176             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::X);
1177         }
1178         if (!strcmp(value_name, "y")) {
1179             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::Y);
1180         }
1181     }
1183     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1186 static void
1187 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1189     sp_node_path_value_changed(adj, tbl, "x");
1192 static void
1193 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1195     sp_node_path_value_changed(adj, tbl, "y");
1198 void
1199 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1201     {
1202     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1203     SPItem *item = selection->singleItem();
1204     if (item && SP_IS_LPE_ITEM(item)) {
1205        if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1206            gtk_action_set_sensitive(w, TRUE);
1207        } else {
1208            gtk_action_set_sensitive(w, FALSE);
1209        }
1210     } else {
1211        gtk_action_set_sensitive(w, FALSE);
1212     }
1213     }
1215     {
1216     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1217     SPItem *item = selection->singleItem();
1218     if (item && item->clip_ref && item->clip_ref->getObject()) {
1219        gtk_action_set_sensitive(w, TRUE);
1220     } else {
1221        gtk_action_set_sensitive(w, FALSE);
1222     }
1223     }
1225     {
1226     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1227     SPItem *item = selection->singleItem();
1228     if (item && item->mask_ref && item->mask_ref->getObject()) {
1229        gtk_action_set_sensitive(w, TRUE);
1230     } else {
1231        gtk_action_set_sensitive(w, FALSE);
1232     }
1233     }
1236 void
1237 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1239     sp_node_toolbox_sel_changed (selection, tbl);
1244 //################################
1245 //##    Node Editing Toolbox    ##
1246 //################################
1248 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1250     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1251     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1252     g_object_set_data( holder, "tracker", tracker );
1254     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
1256     {
1257         InkAction* inky = ink_action_new( "NodeInsertAction",
1258                                           _("Insert node"),
1259                                           _("Insert new nodes into selected segments"),
1260                                           "node_insert",
1261                                           secondarySize );
1262         g_object_set( inky, "short_label", _("Insert"), NULL );
1263         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1264         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1265     }
1267     {
1268         InkAction* inky = ink_action_new( "NodeDeleteAction",
1269                                           _("Delete node"),
1270                                           _("Delete selected nodes"),
1271                                           "node_delete",
1272                                           secondarySize );
1273         g_object_set( inky, "short_label", _("Delete"), NULL );
1274         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1275         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1276     }
1278     {
1279         InkAction* inky = ink_action_new( "NodeJoinAction",
1280                                           _("Join endnodes"),
1281                                           _("Join selected endnodes"),
1282                                           "node_join",
1283                                           secondarySize );
1284         g_object_set( inky, "short_label", _("Join"), NULL );
1285         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1286         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1287     }
1289     {
1290         InkAction* inky = ink_action_new( "NodeBreakAction",
1291                                           _("Break nodes"),
1292                                           _("Break path at selected nodes"),
1293                                           "node_break",
1294                                           secondarySize );
1295         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1296         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1297     }
1300     {
1301         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1302                                           _("Join with segment"),
1303                                           _("Join selected endnodes with a new segment"),
1304                                           "node_join_segment",
1305                                           secondarySize );
1306         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1307         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1308     }
1310     {
1311         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1312                                           _("Delete segment"),
1313                                           _("Delete segment between two non-endpoint nodes"),
1314                                           "node_delete_segment",
1315                                           secondarySize );
1316         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1317         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1318     }
1320     {
1321         InkAction* inky = ink_action_new( "NodeCuspAction",
1322                                           _("Node Cusp"),
1323                                           _("Make selected nodes corner"),
1324                                           "node_cusp",
1325                                           secondarySize );
1326         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1327         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1328     }
1330     {
1331         InkAction* inky = ink_action_new( "NodeSmoothAction",
1332                                           _("Node Smooth"),
1333                                           _("Make selected nodes smooth"),
1334                                           "node_smooth",
1335                                           secondarySize );
1336         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1337         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1338     }
1340     {
1341         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1342                                           _("Node Symmetric"),
1343                                           _("Make selected nodes symmetric"),
1344                                           "node_symmetric",
1345                                           secondarySize );
1346         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1347         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1348     }
1350     {
1351         InkAction* inky = ink_action_new( "NodeLineAction",
1352                                           _("Node Line"),
1353                                           _("Make selected segments lines"),
1354                                           "node_line",
1355                                           secondarySize );
1356         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1357         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1358     }
1360     {
1361         InkAction* inky = ink_action_new( "NodeCurveAction",
1362                                           _("Node Curve"),
1363                                           _("Make selected segments curves"),
1364                                           "node_curve",
1365                                           secondarySize );
1366         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1367         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1368     }
1370     {
1371         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1372                                                       _("Show Handles"),
1373                                                       _("Show the Bezier handles of selected nodes"),
1374                                                       "nodes_show_handles",
1375                                                       Inkscape::ICON_SIZE_DECORATION );
1376         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1377         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1378         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1379     }
1381     {
1382         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1383                                                       _("Show Outline"),
1384                                                       _("Show the outline of the path"),
1385                                                       "nodes_show_helperpath",
1386                                                       Inkscape::ICON_SIZE_DECORATION );
1387         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1388         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1389         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_helperpath", 0 ) );
1390     }
1392     {
1393         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1394                                           _("Next path effect parameter"),
1395                                           _("Show next path effect parameter for editing"),
1396                                           "edit_next_parameter",
1397                                           Inkscape::ICON_SIZE_DECORATION );
1398         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1399         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1400         g_object_set_data( holder, "nodes_lpeedit", inky);
1401     }
1403     {
1404         InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1405                                           _("Edit clipping path"),
1406                                           _("Edit the clipping path of the object"),
1407                                           "nodeedit-clippath",
1408                                           Inkscape::ICON_SIZE_DECORATION );
1409         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1410         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1411         g_object_set_data( holder, "nodes_clippathedit", inky);
1412     }
1414     {
1415         InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1416                                           _("Edit mask path"),
1417                                           _("Edit the mask of the object"),
1418                                           "nodeedit-mask",
1419                                           Inkscape::ICON_SIZE_DECORATION );
1420         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1421         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1422         g_object_set_data( holder, "nodes_maskedit", inky);
1423     }
1425     /* X coord of selected node(s) */
1426     {
1427         EgeAdjustmentAction* eact = 0;
1428         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1429         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1430         eact = create_adjustment_action( "NodeXAction",
1431                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1432                                          "tools.nodes", "Xcoord", 0,
1433                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1434                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1435                                          labels, values, G_N_ELEMENTS(labels),
1436                                          sp_node_path_x_value_changed );
1437         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1438         g_object_set_data( holder, "nodes_x_action", eact );
1439         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1440         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1441     }
1443     /* Y coord of selected node(s) */
1444     {
1445         EgeAdjustmentAction* eact = 0;
1446         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1447         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1448         eact = create_adjustment_action( "NodeYAction",
1449                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1450                                          "tools.nodes", "Ycoord", 0,
1451                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1452                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1453                                          labels, values, G_N_ELEMENTS(labels),
1454                                          sp_node_path_y_value_changed );
1455         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1456         g_object_set_data( holder, "nodes_y_action", eact );
1457         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1458         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1459     }
1461     // add the units menu
1462     {
1463         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1464         gtk_action_group_add_action( mainActions, act );
1465     }
1468     sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1470     //watch selection
1471     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1473     sigc::connection *c_selection_changed =
1474         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1475                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1476     pool->add_connection ("selection-changed", c_selection_changed);
1478     sigc::connection *c_selection_modified =
1479         new sigc::connection (sp_desktop_selection (desktop)->connectModified
1480                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1481     pool->add_connection ("selection-modified", c_selection_modified);
1483     sigc::connection *c_subselection_changed =
1484         new sigc::connection (desktop->connectToolSubselectionChanged
1485                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1486     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1488     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1490     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1491 } // end of sp_node_toolbox_prep()
1494 //########################
1495 //##    Zoom Toolbox    ##
1496 //########################
1498 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1500     // no custom GtkAction setup needed
1501 } // end of sp_zoom_toolbox_prep()
1503 void
1504 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1506     toolbox_set_desktop(toolbox,
1507                         desktop,
1508                         setup_tool_toolbox,
1509                         update_tool_toolbox,
1510                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1511                                                                          "event_context_connection")));
1515 void
1516 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1518     toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1519                         desktop,
1520                         setup_aux_toolbox,
1521                         update_aux_toolbox,
1522                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1523                                                                          "event_context_connection")));
1526 void
1527 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1529     toolbox_set_desktop(toolbox,
1530                         desktop,
1531                         setup_commands_toolbox,
1532                         update_commands_toolbox,
1533                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1534                                                                          "event_context_connection")));
1537 static void
1538 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1540     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1541     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1543     if (old_desktop) {
1544         GList *children, *iter;
1546         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1547         for ( iter = children ; iter ; iter = iter->next ) {
1548             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1549         }
1550         g_list_free(children);
1551     }
1553     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1555     if (desktop) {
1556         gtk_widget_set_sensitive(toolbox, TRUE);
1557         setup_func(toolbox, desktop);
1558         update_func(desktop, desktop->event_context, toolbox);
1559         *conn = desktop->connectEventContextChanged
1560             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1561     } else {
1562         gtk_widget_set_sensitive(toolbox, FALSE);
1563     }
1565 } // end of toolbox_set_desktop()
1568 static void
1569 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1571     gchar const * descr =
1572         "<ui>"
1573         "  <toolbar name='ToolToolbar'>"
1574         "    <toolitem action='ToolSelector' />"
1575         "    <toolitem action='ToolNode' />"
1576         "    <toolitem action='ToolTweak' />"
1577         "    <toolitem action='ToolZoom' />"
1578         "    <toolitem action='ToolRect' />"
1579         "    <toolitem action='Tool3DBox' />"
1580         "    <toolitem action='ToolArc' />"
1581         "    <toolitem action='ToolStar' />"
1582         "    <toolitem action='ToolSpiral' />"
1583         "    <toolitem action='ToolPencil' />"
1584         "    <toolitem action='ToolPen' />"
1585         "    <toolitem action='ToolCalligraphic' />"
1586         "    <toolitem action='ToolEraser' />"
1587 //        "    <toolitem action='ToolLPETool' />"
1588         "    <toolitem action='ToolPaintBucket' />"
1589         "    <toolitem action='ToolText' />"
1590         "    <toolitem action='ToolConnector' />"
1591         "    <toolitem action='ToolGradient' />"
1592         "    <toolitem action='ToolDropper' />"
1593         "  </toolbar>"
1594         "</ui>";
1595     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1596     GtkUIManager* mgr = gtk_ui_manager_new();
1597     GError* errVal = 0;
1599     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1600     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1602     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" );
1603     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1604         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1605     }
1606     Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1607     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1609     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
1610     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1612     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1614     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1615     if ( child ) {
1616         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1617     }
1619     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1620 //     Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1624 static void
1625 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1627     gchar const *const tname = ( eventcontext
1628                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1629                                  : NULL );
1630     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1632     for (int i = 0 ; tools[i].type_name ; i++ ) {
1633         Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1634         if ( act ) {
1635             bool setActive = tname && !strcmp(tname, tools[i].type_name);
1636             Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1637             if ( verbAct ) {
1638                 verbAct->set_active(setActive);
1639             }
1640         }
1641     }
1644 static void
1645 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1647     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1648     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1649     GtkUIManager* mgr = gtk_ui_manager_new();
1650     GError* errVal = 0;
1651     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1652     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1654     std::map<std::string, GtkWidget*> dataHolders;
1656     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1657         if ( aux_toolboxes[i].prep_func ) {
1658             // converted to GtkActions and UIManager
1660             GtkWidget* kludge = gtk_toolbar_new();
1661             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1662             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1663             dataHolders[aux_toolboxes[i].type_name] = kludge;
1664             aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1665         } else {
1667             GtkWidget *sub_toolbox = 0;
1668             if (aux_toolboxes[i].create_func == NULL)
1669                 sub_toolbox = sp_empty_toolbox_new(desktop);
1670             else {
1671                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1672             }
1674             gtk_size_group_add_widget( grouper, sub_toolbox );
1676             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1677             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1679         }
1680     }
1682     // Second pass to create toolbars *after* all GtkActions are created
1683     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1684         if ( aux_toolboxes[i].prep_func ) {
1685             // converted to GtkActions and UIManager
1687             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1689             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1690             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1692             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1693             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1694             g_free( tmp );
1695             tmp = 0;
1697             Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1698             if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1699                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1700             }
1701             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1704             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1706             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1707                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1708                 swatch->setDesktop( desktop );
1709                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1710                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1711                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1712                 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 );
1713             }
1715             gtk_widget_show_all( holder );
1716             sp_set_font_size_smaller( holder );
1718             gtk_size_group_add_widget( grouper, holder );
1720             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1721             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1722         }
1723     }
1725     g_object_unref( G_OBJECT(grouper) );
1728 static void
1729 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1731     gchar const *tname = ( eventcontext
1732                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1733                            : NULL );
1734     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1735         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1736         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1737             gtk_widget_show_all(sub_toolbox);
1738             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1739         } else {
1740             gtk_widget_hide(sub_toolbox);
1741         }
1742     }
1745 static void
1746 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1748     gchar const * descr =
1749         "<ui>"
1750         "  <toolbar name='CommandsToolbar'>"
1751         "    <toolitem action='FileNew' />"
1752         "    <toolitem action='FileOpen' />"
1753         "    <toolitem action='FileSave' />"
1754         "    <toolitem action='FilePrint' />"
1755         "    <separator />"
1756         "    <toolitem action='FileImport' />"
1757         "    <toolitem action='FileExport' />"
1758         "    <separator />"
1759         "    <toolitem action='EditUndo' />"
1760         "    <toolitem action='EditRedo' />"
1761         "    <separator />"
1762         "    <toolitem action='EditCopy' />"
1763         "    <toolitem action='EditCut' />"
1764         "    <toolitem action='EditPaste' />"
1765         "    <separator />"
1766         "    <toolitem action='ZoomSelection' />"
1767         "    <toolitem action='ZoomDrawing' />"
1768         "    <toolitem action='ZoomPage' />"
1769         "    <separator />"
1770         "    <toolitem action='EditDuplicate' />"
1771         "    <toolitem action='EditClone' />"
1772         "    <toolitem action='EditUnlinkClone' />"
1773         "    <separator />"
1774         "    <toolitem action='SelectionGroup' />"
1775         "    <toolitem action='SelectionUnGroup' />"
1776         "    <separator />"
1777         "    <toolitem action='DialogFillStroke' />"
1778         "    <toolitem action='DialogText' />"
1779         "    <toolitem action='DialogXMLEditor' />"
1780         "    <toolitem action='DialogAlignDistribute' />"
1781         "    <separator />"
1782         "    <toolitem action='DialogPreferences' />"
1783         "    <toolitem action='DialogDocumentProperties' />"
1784         "  </toolbar>"
1785         "</ui>";
1786     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1789     GtkUIManager* mgr = gtk_ui_manager_new();
1790     GError* errVal = 0;
1792     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1793     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1795     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1796     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1797         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1798     }
1800     Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1801     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1803     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1804     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1807     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1809     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1810     if ( child ) {
1811         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1812     }
1814     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1817 static void
1818 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1822 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1824     gtk_widget_show(toolbox_toplevel);
1825     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1827     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1828     if (!shown_toolbox) {
1829         return;
1830     }
1831     gtk_widget_show(toolbox);
1833     gtk_widget_show_all(shown_toolbox);
1836 static GtkWidget *
1837 sp_empty_toolbox_new(SPDesktop *desktop)
1839     GtkWidget *tbl = gtk_toolbar_new();
1840     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1841     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1843     gtk_widget_show_all(tbl);
1844     sp_set_font_size_smaller (tbl);
1846     return tbl;
1849 #define MODE_LABEL_WIDTH 70
1851 //########################
1852 //##       Star         ##
1853 //########################
1855 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1857     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1859     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1860         // do not remember prefs if this call is initiated by an undo change, because undoing object
1861         // creation sets bogus values to its attributes before it is deleted
1862         prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1863     }
1865     // quit if run by the attr_changed listener
1866     if (g_object_get_data( dataKludge, "freeze" )) {
1867         return;
1868     }
1870     // in turn, prevent listener from responding
1871     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1873     bool modmade = false;
1875     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1876     GSList const *items = selection->itemList();
1877     for (; items != NULL; items = items->next) {
1878         if (SP_IS_STAR((SPItem *) items->data)) {
1879             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1880             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1881             sp_repr_set_svg_double(repr, "sodipodi:arg2",
1882                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1883                                     + M_PI / (gint)adj->value));
1884             SP_OBJECT((SPItem *) items->data)->updateRepr();
1885             modmade = true;
1886         }
1887     }
1888     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1889                                    _("Star: Change number of corners"));
1891     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1894 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1896     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1898     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1899         prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1900     }
1902     // quit if run by the attr_changed listener
1903     if (g_object_get_data( dataKludge, "freeze" )) {
1904         return;
1905     }
1907     // in turn, prevent listener from responding
1908     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1910     bool modmade = false;
1911     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1912     GSList const *items = selection->itemList();
1913     for (; items != NULL; items = items->next) {
1914         if (SP_IS_STAR((SPItem *) items->data)) {
1915             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1917             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1918             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1919             if (r2 < r1) {
1920                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1921             } else {
1922                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1923             }
1925             SP_OBJECT((SPItem *) items->data)->updateRepr();
1926             modmade = true;
1927         }
1928     }
1930     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1931                                    _("Star: Change spoke ratio"));
1933     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1936 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1938     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1939     bool flat = ege_select_one_action_get_active( act ) == 0;
1941     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1942         prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1943                                     flat ? "true" : "false" );
1944     }
1946     // quit if run by the attr_changed listener
1947     if (g_object_get_data( dataKludge, "freeze" )) {
1948         return;
1949     }
1951     // in turn, prevent listener from responding
1952     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1954     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1955     GSList const *items = selection->itemList();
1956     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1957     bool modmade = false;
1959     if ( prop_action ) {
1960         gtk_action_set_sensitive( prop_action, !flat );
1961     }
1963     for (; items != NULL; items = items->next) {
1964         if (SP_IS_STAR((SPItem *) items->data)) {
1965             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1966             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1967             SP_OBJECT((SPItem *) items->data)->updateRepr();
1968             modmade = true;
1969         }
1970     }
1972     if (modmade) {
1973         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1974                          flat ? _("Make polygon") : _("Make star"));
1975     }
1977     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1980 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1982     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1984     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1985         prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1986     }
1988     // quit if run by the attr_changed listener
1989     if (g_object_get_data( dataKludge, "freeze" )) {
1990         return;
1991     }
1993     // in turn, prevent listener from responding
1994     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1996     bool modmade = false;
1998     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1999     GSList const *items = selection->itemList();
2000     for (; items != NULL; items = items->next) {
2001         if (SP_IS_STAR((SPItem *) items->data)) {
2002             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2003             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2004             SP_OBJECT(items->data)->updateRepr();
2005             modmade = true;
2006         }
2007     }
2008     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2009                                    _("Star: Change rounding"));
2011     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2014 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2016     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2018     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2019         prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
2020     }
2022     // quit if run by the attr_changed listener
2023     if (g_object_get_data( dataKludge, "freeze" )) {
2024         return;
2025     }
2027     // in turn, prevent listener from responding
2028     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2030     bool modmade = false;
2032     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2033     GSList const *items = selection->itemList();
2034     for (; items != NULL; items = items->next) {
2035         if (SP_IS_STAR((SPItem *) items->data)) {
2036             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2037             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2038             SP_OBJECT(items->data)->updateRepr();
2039             modmade = true;
2040         }
2041     }
2042     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2043                                    _("Star: Change randomization"));
2045     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2049 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2050                                        gchar const */*old_value*/, gchar const */*new_value*/,
2051                                        bool /*is_interactive*/, gpointer data)
2053     GtkWidget *tbl = GTK_WIDGET(data);
2055     // quit if run by the _changed callbacks
2056     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2057         return;
2058     }
2060     // in turn, prevent callbacks from responding
2061     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2063     GtkAdjustment *adj = 0;
2065     gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2066     bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2068     if (!strcmp(name, "inkscape:randomized")) {
2069         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2070         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2071     } else if (!strcmp(name, "inkscape:rounded")) {
2072         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2073         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2074     } else if (!strcmp(name, "inkscape:flatsided")) {
2075         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2076         char const *flatsides = repr->attribute("inkscape:flatsided");
2077         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2078         if ( flatsides && !strcmp(flatsides,"false") ) {
2079             ege_select_one_action_set_active( flat_action, 1 );
2080             gtk_action_set_sensitive( prop_action, TRUE );
2081         } else {
2082             ege_select_one_action_set_active( flat_action, 0 );
2083             gtk_action_set_sensitive( prop_action, FALSE );
2084         }
2085     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2086         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2087         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2088         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2089         if (r2 < r1) {
2090             gtk_adjustment_set_value(adj, r2/r1);
2091         } else {
2092             gtk_adjustment_set_value(adj, r1/r2);
2093         }
2094     } else if (!strcmp(name, "sodipodi:sides")) {
2095         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2096         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2097     }
2099     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2103 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2105     NULL, /* child_added */
2106     NULL, /* child_removed */
2107     star_tb_event_attr_changed,
2108     NULL, /* content_changed */
2109     NULL  /* order_changed */
2110 };
2113 /**
2114  *  \param selection Should not be NULL.
2115  */
2116 static void
2117 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2119     int n_selected = 0;
2120     Inkscape::XML::Node *repr = NULL;
2122     purge_repr_listener( tbl, tbl );
2124     for (GSList const *items = selection->itemList();
2125          items != NULL;
2126          items = items->next)
2127     {
2128         if (SP_IS_STAR((SPItem *) items->data)) {
2129             n_selected++;
2130             repr = SP_OBJECT_REPR((SPItem *) items->data);
2131         }
2132     }
2134     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2136     if (n_selected == 0) {
2137         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2138     } else if (n_selected == 1) {
2139         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2141         if (repr) {
2142             g_object_set_data( tbl, "repr", repr );
2143             Inkscape::GC::anchor(repr);
2144             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2145             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2146         }
2147     } else {
2148         // FIXME: implement averaging of all parameters for multiple selected stars
2149         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2150         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2151     }
2155 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2157     // FIXME: in this and all other _default functions, set some flag telling the value_changed
2158     // callbacks to lump all the changes for all selected objects in one undo step
2160     GtkAdjustment *adj = 0;
2162     // fixme: make settable in prefs!
2163     gint mag = 5;
2164     gdouble prop = 0.5;
2165     gboolean flat = FALSE;
2166     gdouble randomized = 0;
2167     gdouble rounded = 0;
2169     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2170     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2172     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2173     gtk_action_set_sensitive( sb2, !flat );
2175     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2176     gtk_adjustment_set_value(adj, mag);
2177     gtk_adjustment_value_changed(adj);
2179     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2180     gtk_adjustment_set_value(adj, prop);
2181     gtk_adjustment_value_changed(adj);
2183     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2184     gtk_adjustment_set_value(adj, rounded);
2185     gtk_adjustment_value_changed(adj);
2187     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2188     gtk_adjustment_set_value(adj, randomized);
2189     gtk_adjustment_value_changed(adj);
2193 void
2194 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2196     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2197     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2198     GtkWidget *l = gtk_label_new(NULL);
2199     gtk_label_set_markup(GTK_LABEL(l), title);
2200     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2201     if ( GTK_IS_TOOLBAR(tbl) ) {
2202         gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2203     } else {
2204         gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2205     }
2206     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2210 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2212     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2214     {
2215         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2216         ege_output_action_set_use_markup( act, TRUE );
2217         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2218         g_object_set_data( holder, "mode_action", act );
2219     }
2221     {
2222         EgeAdjustmentAction* eact = 0;
2223         gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2224         bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2226         /* Flatsided checkbox */
2227         {
2228             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2230             GtkTreeIter iter;
2231             gtk_list_store_append( model, &iter );
2232             gtk_list_store_set( model, &iter,
2233                                 0, _("Polygon"),
2234                                 1, _("Regular polygon (with one handle) instead of a star"),
2235                                 2, "star_flat",
2236                                 -1 );
2238             gtk_list_store_append( model, &iter );
2239             gtk_list_store_set( model, &iter,
2240                                 0, _("Star"),
2241                                 1, _("Star instead of a regular polygon (with one handle)"),
2242                                 2, "star_angled",
2243                                 -1 );
2245             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2246             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2247             g_object_set_data( holder, "flat_action", act );
2249             ege_select_one_action_set_appearance( act, "full" );
2250             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2251             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2252             ege_select_one_action_set_icon_column( act, 2 );
2253             ege_select_one_action_set_icon_size( act, secondarySize );
2254             ege_select_one_action_set_tooltip_column( act, 1  );
2256             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2257             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2258         }
2260         /* Magnitude */
2261         {
2262         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2263         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2264         eact = create_adjustment_action( "MagnitudeAction",
2265                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2266                                          "tools.shapes.star", "magnitude", 3,
2267                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2268                                          3, 1024, 1, 5,
2269                                          labels, values, G_N_ELEMENTS(labels),
2270                                          sp_stb_magnitude_value_changed,
2271                                          1.0, 0 );
2272         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2273         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2274         }
2276         /* Spoke ratio */
2277         {
2278         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2279         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2280         eact = create_adjustment_action( "SpokeAction",
2281                                          _("Spoke ratio"), _("Spoke ratio:"),
2282                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2283                                          // Base radius is the same for the closest handle.
2284                                          _("Base radius to tip radius ratio"),
2285                                          "tools.shapes.star", "proportion", 0.5,
2286                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2287                                          0.01, 1.0, 0.01, 0.1,
2288                                          labels, values, G_N_ELEMENTS(labels),
2289                                          sp_stb_proportion_value_changed );
2290         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2291         g_object_set_data( holder, "prop_action", eact );
2292         }
2294         if ( !isFlatSided ) {
2295             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2296         } else {
2297             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2298         }
2300         /* Roundedness */
2301         {
2302         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2303         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2304         eact = create_adjustment_action( "RoundednessAction",
2305                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2306                                          "tools.shapes.star", "rounded", 0.0,
2307                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2308                                          -10.0, 10.0, 0.01, 0.1,
2309                                          labels, values, G_N_ELEMENTS(labels),
2310                                          sp_stb_rounded_value_changed );
2311         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2312         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2313         }
2315         /* Randomization */
2316         {
2317         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2318         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2319         eact = create_adjustment_action( "RandomizationAction",
2320                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2321                                          "tools.shapes.star", "randomized", 0.0,
2322                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2323                                          -10.0, 10.0, 0.001, 0.01,
2324                                          labels, values, G_N_ELEMENTS(labels),
2325                                          sp_stb_randomized_value_changed, 0.1, 3 );
2326         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2327         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2328         }
2329     }
2331     {
2332         /* Reset */
2333         {
2334             GtkAction* act = gtk_action_new( "StarResetAction",
2335                                              _("Defaults"),
2336                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2337                                              GTK_STOCK_CLEAR );
2338             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2339             gtk_action_group_add_action( mainActions, act );
2340             gtk_action_set_sensitive( act, TRUE );
2341         }
2342     }
2344     sigc::connection *connection = new sigc::connection(
2345         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2346         );
2347     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2348     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2352 //########################
2353 //##       Rect         ##
2354 //########################
2356 static void sp_rtb_sensitivize( GObject *tbl )
2358     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2359     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2360     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2362     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2363         gtk_action_set_sensitive( not_rounded, FALSE );
2364     } else {
2365         gtk_action_set_sensitive( not_rounded, TRUE );
2366     }
2370 static void
2371 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2372                           void (*setter)(SPRect *, gdouble))
2374     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2376     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2377     SPUnit const *unit = tracker->getActiveUnit();
2379     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2380         prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2381     }
2383     // quit if run by the attr_changed listener
2384     if (g_object_get_data( tbl, "freeze" )) {
2385         return;
2386     }
2388     // in turn, prevent listener from responding
2389     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2391     bool modmade = false;
2392     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2393     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2394         if (SP_IS_RECT(items->data)) {
2395             if (adj->value != 0) {
2396                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2397             } else {
2398                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2399             }
2400             modmade = true;
2401         }
2402     }
2404     sp_rtb_sensitivize( tbl );
2406     if (modmade) {
2407         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2408                                    _("Change rectangle"));
2409     }
2411     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2414 static void
2415 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2417     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2420 static void
2421 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2423     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2426 static void
2427 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2429     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2432 static void
2433 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2435     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2440 static void
2441 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2443     GtkAdjustment *adj = 0;
2445     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2446     gtk_adjustment_set_value(adj, 0.0);
2447     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2448     gtk_adjustment_value_changed(adj);
2450     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2451     gtk_adjustment_set_value(adj, 0.0);
2452     gtk_adjustment_value_changed(adj);
2454     sp_rtb_sensitivize( obj );
2457 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2458                                        gchar const */*old_value*/, gchar const */*new_value*/,
2459                                        bool /*is_interactive*/, gpointer data)
2461     GObject *tbl = G_OBJECT(data);
2463     // quit if run by the _changed callbacks
2464     if (g_object_get_data( tbl, "freeze" )) {
2465         return;
2466     }
2468     // in turn, prevent callbacks from responding
2469     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2471     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2472     SPUnit const *unit = tracker->getActiveUnit();
2474     gpointer item = g_object_get_data( tbl, "item" );
2475     if (item && SP_IS_RECT(item)) {
2476         {
2477             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2478             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2479             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2480         }
2482         {
2483             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2484             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2485             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2486         }
2488         {
2489             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2490             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2491             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2492         }
2494         {
2495             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2496             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2497             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2498         }
2499     }
2501     sp_rtb_sensitivize( tbl );
2503     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2507 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2508     NULL, /* child_added */
2509     NULL, /* child_removed */
2510     rect_tb_event_attr_changed,
2511     NULL, /* content_changed */
2512     NULL  /* order_changed */
2513 };
2515 /**
2516  *  \param selection should not be NULL.
2517  */
2518 static void
2519 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2521     int n_selected = 0;
2522     Inkscape::XML::Node *repr = NULL;
2523     SPItem *item = NULL;
2525     if ( g_object_get_data( tbl, "repr" ) ) {
2526         g_object_set_data( tbl, "item", NULL );
2527     }
2528     purge_repr_listener( tbl, tbl );
2530     for (GSList const *items = selection->itemList();
2531          items != NULL;
2532          items = items->next) {
2533         if (SP_IS_RECT((SPItem *) items->data)) {
2534             n_selected++;
2535             item = (SPItem *) items->data;
2536             repr = SP_OBJECT_REPR(item);
2537         }
2538     }
2540     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2542     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2544     if (n_selected == 0) {
2545         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2547         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2548         gtk_action_set_sensitive(w, FALSE);
2549         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2550         gtk_action_set_sensitive(h, FALSE);
2552     } else if (n_selected == 1) {
2553         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2554         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2556         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2557         gtk_action_set_sensitive(w, TRUE);
2558         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2559         gtk_action_set_sensitive(h, TRUE);
2561         if (repr) {
2562             g_object_set_data( tbl, "repr", repr );
2563             g_object_set_data( tbl, "item", item );
2564             Inkscape::GC::anchor(repr);
2565             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2566             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2567         }
2568     } else {
2569         // FIXME: implement averaging of all parameters for multiple selected
2570         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2571         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2572         sp_rtb_sensitivize( tbl );
2573     }
2577 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2579     EgeAdjustmentAction* eact = 0;
2580     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2582     {
2583         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2584         ege_output_action_set_use_markup( act, TRUE );
2585         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2586         g_object_set_data( holder, "mode_action", act );
2587     }
2589     // rx/ry units menu: create
2590     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2591     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2592     // fixme: add % meaning per cent of the width/height
2593     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2594     g_object_set_data( holder, "tracker", tracker );
2596     /* W */
2597     {
2598         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2599         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2600         eact = create_adjustment_action( "RectWidthAction",
2601                                          _("Width"), _("W:"), _("Width of rectangle"),
2602                                          "tools.shapes.rect", "width", 0,
2603                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2604                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2605                                          labels, values, G_N_ELEMENTS(labels),
2606                                          sp_rtb_width_value_changed );
2607         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2608         g_object_set_data( holder, "width_action", eact );
2609         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2610         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2611     }
2613     /* H */
2614     {
2615         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2616         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2617         eact = create_adjustment_action( "RectHeightAction",
2618                                          _("Height"), _("H:"), _("Height of rectangle"),
2619                                          "tools.shapes.rect", "height", 0,
2620                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2621                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2622                                          labels, values, G_N_ELEMENTS(labels),
2623                                          sp_rtb_height_value_changed );
2624         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2625         g_object_set_data( holder, "height_action", eact );
2626         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2627         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2628     }
2630     /* rx */
2631     {
2632         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2633         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2634         eact = create_adjustment_action( "RadiusXAction",
2635                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2636                                          "tools.shapes.rect", "rx", 0,
2637                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2638                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2639                                          labels, values, G_N_ELEMENTS(labels),
2640                                          sp_rtb_rx_value_changed);
2641         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2642         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2643     }
2645     /* ry */
2646     {
2647         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2648         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2649         eact = create_adjustment_action( "RadiusYAction",
2650                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2651                                          "tools.shapes.rect", "ry", 0,
2652                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2653                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2654                                          labels, values, G_N_ELEMENTS(labels),
2655                                          sp_rtb_ry_value_changed);
2656         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2657         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2658     }
2660     // add the units menu
2661     {
2662         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2663         gtk_action_group_add_action( mainActions, act );
2664     }
2666     /* Reset */
2667     {
2668         InkAction* inky = ink_action_new( "RectResetAction",
2669                                           _("Not rounded"),
2670                                           _("Make corners sharp"),
2671                                           "squared_corner",
2672                                           secondarySize );
2673         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2674         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2675         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2676         g_object_set_data( holder, "not_rounded", inky );
2677     }
2679     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2680     sp_rtb_sensitivize( holder );
2682     sigc::connection *connection = new sigc::connection(
2683         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2684         );
2685     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2686     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2689 //########################
2690 //##       3D Box       ##
2691 //########################
2693 // normalize angle so that it lies in the interval [0,360]
2694 static double box3d_normalize_angle (double a) {
2695     double angle = a + ((int) (a/360.0))*360;
2696     if (angle < 0) {
2697         angle += 360.0;
2698     }
2699     return angle;
2702 static void
2703 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2704                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2705     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2706     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2707     //       are reset).
2708     bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2710     if (is_infinite) {
2711         gtk_toggle_action_set_active(tact, TRUE);
2712         gtk_action_set_sensitive(act, TRUE);
2714         double angle = persp3d_get_infinite_angle(persp, axis);
2715         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2716             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2717         }
2718     } else {
2719         gtk_toggle_action_set_active(tact, FALSE);
2720         gtk_action_set_sensitive(act, FALSE);
2721     }
2724 static void
2725 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2726     if (!persp_repr) {
2727         g_print ("No perspective given to box3d_resync_toolbar().\n");
2728         return;
2729     }
2731     GtkWidget *tbl = GTK_WIDGET(data);
2732     GtkAdjustment *adj = 0;
2733     GtkAction *act = 0;
2734     GtkToggleAction *tact = 0;
2735     Persp3D *persp = persp3d_get_from_repr(persp_repr);
2736     {
2737         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2738         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2739         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2741         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2742     }
2743     {
2744         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2745         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2746         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2748         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2749     }
2750     {
2751         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2752         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2753         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2755         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2756     }
2759 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2760                                                   gchar const */*old_value*/, gchar const */*new_value*/,
2761                                                   bool /*is_interactive*/, gpointer data)
2763     GtkWidget *tbl = GTK_WIDGET(data);
2765     // quit if run by the attr_changed listener
2766     // note: it used to work without the differently called freeze_ attributes (here and in
2767     //       box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2768     if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2769         return;
2770     }
2772     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2773     // sp_document_maybe_done() when the document is undo insensitive)
2774     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2776     // TODO: Only update the appropriate part of the toolbar
2777 //    if (!strcmp(name, "inkscape:vp_z")) {
2778         box3d_resync_toolbar(repr, G_OBJECT(tbl));
2779 //    }
2781     Persp3D *persp = persp3d_get_from_repr(repr);
2782     persp3d_update_box_reprs(persp);
2784     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2787 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2789     NULL, /* child_added */
2790     NULL, /* child_removed */
2791     box3d_persp_tb_event_attr_changed,
2792     NULL, /* content_changed */
2793     NULL  /* order_changed */
2794 };
2796 /**
2797  *  \param selection Should not be NULL.
2798  */
2799 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2800 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2801 static void
2802 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2804     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2805     // disable the angle entry fields for this direction (otherwise entering a value in them should only
2806     // update the perspectives with infinite VPs and leave the other ones untouched).
2808     Inkscape::XML::Node *persp_repr = NULL;
2809     purge_repr_listener(tbl, tbl);
2811     SPItem *item = selection->singleItem();
2812     if (item && SP_IS_BOX3D(item)) {
2813         // FIXME: Also deal with multiple selected boxes
2814         SPBox3D *box = SP_BOX3D(item);
2815         Persp3D *persp = box3d_get_perspective(box);
2816         persp_repr = SP_OBJECT_REPR(persp);
2817         if (persp_repr) {
2818             g_object_set_data(tbl, "repr", persp_repr);
2819             Inkscape::GC::anchor(persp_repr);
2820             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2821             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2822         }
2824         inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2825         prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2827         box3d_resync_toolbar(persp_repr, tbl);
2828     }
2831 static void
2832 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2834     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2835     SPDocument *document = sp_desktop_document(desktop);
2837     // quit if run by the attr_changed listener
2838     // note: it used to work without the differently called freeze_ attributes (here and in
2839     //       box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2840     if (g_object_get_data( dataKludge, "freeze_attr" )) {
2841         return;
2842     }
2844     // in turn, prevent listener from responding
2845     g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2847     //Persp3D *persp = document->current_persp3d;
2848     std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
2849     if (sel_persps.empty()) {
2850         // this can happen when the document is created; we silently ignore it
2851         return;
2852     }
2853     Persp3D *persp = sel_persps.front();
2855     persp->tmat.set_infinite_direction (axis, adj->value);
2856     SP_OBJECT(persp)->updateRepr();
2858     // TODO: use the correct axis here, too
2859     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2861     g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2865 static void
2866 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2868     box3d_angle_value_changed(adj, dataKludge, Proj::X);
2871 static void
2872 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2874     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2877 static void
2878 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2880     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2884 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2886     // TODO: Take all selected perspectives into account
2887     std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
2888     if (sel_persps.empty()) {
2889         // this can happen when the document is created; we silently ignore it
2890         return;
2891     }
2892     Persp3D *persp = sel_persps.front();
2894     bool set_infinite = gtk_toggle_action_get_active(act);
2895     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2898 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2900     box3d_vp_state_changed(act, box3d_angle, Proj::X);
2903 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2905     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2908 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2910     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2913 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2915     EgeAdjustmentAction* eact = 0;
2916     SPDocument *document = sp_desktop_document (desktop);
2917     Persp3D *persp = document->current_persp3d;
2919     EgeAdjustmentAction* box3d_angle_x = 0;
2920     EgeAdjustmentAction* box3d_angle_y = 0;
2921     EgeAdjustmentAction* box3d_angle_z = 0;
2923     /* Angle X */
2924     {
2925         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2926         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2927         eact = create_adjustment_action( "3DBoxAngleXAction",
2928                                          _("Angle in X direction"), _("Angle X:"),
2929                                          // Translators: PL is short for 'perspective line'
2930                                          _("Angle of PLs in X direction"),
2931                                          "tools.shapes.3dbox", "box3d_angle_x", 30,
2932                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2933                                          -360.0, 360.0, 1.0, 10.0,
2934                                          labels, values, G_N_ELEMENTS(labels),
2935                                          box3d_angle_x_value_changed );
2936         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2937         g_object_set_data( holder, "box3d_angle_x_action", eact );
2938         box3d_angle_x = eact;
2939     }
2941     if (!persp3d_VP_is_finite(persp, Proj::X)) {
2942         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2943     } else {
2944         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2945     }
2948     /* VP X state */
2949     {
2950         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2951                                                       // Translators: VP is short for 'vanishing point'
2952                                                       _("State of VP in X direction"),
2953                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2954                                                       "toggle_vp_x",
2955                                                       Inkscape::ICON_SIZE_DECORATION );
2956         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2957         g_object_set_data( holder, "box3d_vp_x_state_action", act );
2958         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2959         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2960         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2961     }
2963     /* Angle Y */
2964     {
2965         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2966         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2967         eact = create_adjustment_action( "3DBoxAngleYAction",
2968                                          _("Angle in Y direction"), _("Angle Y:"),
2969                                          // Translators: PL is short for 'perspective line'
2970                                          _("Angle of PLs in Y direction"),
2971                                          "tools.shapes.3dbox", "box3d_angle_y", 30,
2972                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2973                                          -360.0, 360.0, 1.0, 10.0,
2974                                          labels, values, G_N_ELEMENTS(labels),
2975                                          box3d_angle_y_value_changed );
2976         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2977         g_object_set_data( holder, "box3d_angle_y_action", eact );
2978         box3d_angle_y = eact;
2979     }
2981     if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2982         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2983     } else {
2984         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2985     }
2987     /* VP Y state */
2988     {
2989         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2990                                                       // Translators: VP is short for 'vanishing point'
2991                                                       _("State of VP in Y direction"),
2992                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2993                                                       "toggle_vp_y",
2994                                                       Inkscape::ICON_SIZE_DECORATION );
2995         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2996         g_object_set_data( holder, "box3d_vp_y_state_action", act );
2997         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2998         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2999         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
3000     }
3002     /* Angle Z */
3003     {
3004         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3005         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3006         eact = create_adjustment_action( "3DBoxAngleZAction",
3007                                          _("Angle in Z direction"), _("Angle Z:"),
3008                                          // Translators: PL is short for 'perspective line'
3009                                          _("Angle of PLs in Z direction"),
3010                                          "tools.shapes.3dbox", "box3d_angle_z", 30,
3011                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3012                                          -360.0, 360.0, 1.0, 10.0,
3013                                          labels, values, G_N_ELEMENTS(labels),
3014                                          box3d_angle_z_value_changed );
3015         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3016         g_object_set_data( holder, "box3d_angle_z_action", eact );
3017         box3d_angle_z = eact;
3018     }
3020     if (!persp3d_VP_is_finite(persp, Proj::Z)) {
3021         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3022     } else {
3023         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3024     }
3026     /* VP Z state */
3027     {
3028         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3029                                                       // Translators: VP is short for 'vanishing point'
3030                                                       _("State of VP in Z direction"),
3031                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3032                                                       "toggle_vp_z",
3033                                                       Inkscape::ICON_SIZE_DECORATION );
3034         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3035         g_object_set_data( holder, "box3d_vp_z_state_action", act );
3036         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3037         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3038         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3039     }
3041     sigc::connection *connection = new sigc::connection(
3042         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3043        );
3044     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3045     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3048 //########################
3049 //##       Spiral       ##
3050 //########################
3052 static void
3053 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
3055     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3057     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3058         prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
3059     }
3061     // quit if run by the attr_changed listener
3062     if (g_object_get_data( tbl, "freeze" )) {
3063         return;
3064     }
3066     // in turn, prevent listener from responding
3067     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3069     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3071     bool modmade = false;
3072     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3073          items != NULL;
3074          items = items->next)
3075     {
3076         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3077             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3078             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3079             SP_OBJECT((SPItem *) items->data)->updateRepr();
3080             modmade = true;
3081         }
3082     }
3084     g_free(namespaced_name);
3086     if (modmade) {
3087         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3088                                    _("Change spiral"));
3089     }
3091     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3094 static void
3095 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3097     sp_spl_tb_value_changed(adj, tbl, "revolution");
3100 static void
3101 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3103     sp_spl_tb_value_changed(adj, tbl, "expansion");
3106 static void
3107 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3109     sp_spl_tb_value_changed(adj, tbl, "t0");
3112 static void
3113 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3115     GtkWidget *tbl = GTK_WIDGET(obj);
3117     GtkAdjustment *adj;
3119     // fixme: make settable
3120     gdouble rev = 5;
3121     gdouble exp = 1.0;
3122     gdouble t0 = 0.0;
3124     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3125     gtk_adjustment_set_value(adj, rev);
3126     gtk_adjustment_value_changed(adj);
3128     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3129     gtk_adjustment_set_value(adj, exp);
3130     gtk_adjustment_value_changed(adj);
3132     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3133     gtk_adjustment_set_value(adj, t0);
3134     gtk_adjustment_value_changed(adj);
3136     spinbutton_defocus(GTK_OBJECT(tbl));
3140 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3141                                          gchar const */*old_value*/, gchar const */*new_value*/,
3142                                          bool /*is_interactive*/, gpointer data)
3144     GtkWidget *tbl = GTK_WIDGET(data);
3146     // quit if run by the _changed callbacks
3147     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3148         return;
3149     }
3151     // in turn, prevent callbacks from responding
3152     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3154     GtkAdjustment *adj;
3155     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3156     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3158     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3159     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3161     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3162     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3164     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3168 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3169     NULL, /* child_added */
3170     NULL, /* child_removed */
3171     spiral_tb_event_attr_changed,
3172     NULL, /* content_changed */
3173     NULL  /* order_changed */
3174 };
3176 static void
3177 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3179     int n_selected = 0;
3180     Inkscape::XML::Node *repr = NULL;
3182     purge_repr_listener( tbl, tbl );
3184     for (GSList const *items = selection->itemList();
3185          items != NULL;
3186          items = items->next)
3187     {
3188         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3189             n_selected++;
3190             repr = SP_OBJECT_REPR((SPItem *) items->data);
3191         }
3192     }
3194     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3196     if (n_selected == 0) {
3197         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3198     } else if (n_selected == 1) {
3199         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3201         if (repr) {
3202             g_object_set_data( tbl, "repr", repr );
3203             Inkscape::GC::anchor(repr);
3204             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3205             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3206         }
3207     } else {
3208         // FIXME: implement averaging of all parameters for multiple selected
3209         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3210         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3211     }
3215 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3217     EgeAdjustmentAction* eact = 0;
3218     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3220     {
3221         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3222         ege_output_action_set_use_markup( act, TRUE );
3223         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3224         g_object_set_data( holder, "mode_action", act );
3225     }
3227     /* Revolution */
3228     {
3229         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3230         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3231         eact = create_adjustment_action( "SpiralRevolutionAction",
3232                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3233                                          "tools.shapes.spiral", "revolution", 3.0,
3234                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3235                                          0.01, 1024.0, 0.1, 1.0,
3236                                          labels, values, G_N_ELEMENTS(labels),
3237                                          sp_spl_tb_revolution_value_changed, 1, 2);
3238         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3239     }
3241     /* Expansion */
3242     {
3243         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3244         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3245         eact = create_adjustment_action( "SpiralExpansionAction",
3246                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3247                                          "tools.shapes.spiral", "expansion", 1.0,
3248                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3249                                          0.0, 1000.0, 0.01, 1.0,
3250                                          labels, values, G_N_ELEMENTS(labels),
3251                                          sp_spl_tb_expansion_value_changed);
3252         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3253     }
3255     /* T0 */
3256     {
3257         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3258         gdouble values[] = {0, 0.5, 0.9};
3259         eact = create_adjustment_action( "SpiralT0Action",
3260                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3261                                          "tools.shapes.spiral", "t0", 0.0,
3262                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3263                                          0.0, 0.999, 0.01, 1.0,
3264                                          labels, values, G_N_ELEMENTS(labels),
3265                                          sp_spl_tb_t0_value_changed);
3266         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3267     }
3269     /* Reset */
3270     {
3271         InkAction* inky = ink_action_new( "SpiralResetAction",
3272                                           _("Defaults"),
3273                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3274                                           GTK_STOCK_CLEAR,
3275                                           secondarySize );
3276         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3277         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3278     }
3281     sigc::connection *connection = new sigc::connection(
3282         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3283         );
3284     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3285     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3288 //########################
3289 //##     Pen/Pencil     ##
3290 //########################
3292 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3293 static char const *
3294 freehand_tool_name(GObject *dataKludge)
3296     SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3297     return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3298              ? "tools.freehand.pen"
3299              : "tools.freehand.pencil" );
3302 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3304     gint mode = ege_select_one_action_get_active(act);
3306     prefs_set_int_attribute(freehand_tool_name(tbl), "freehand-mode", mode);
3308     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3310     // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3311     // preparatory work here
3312     if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3313         SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3314         sp_pen_context_set_polyline_mode(pc);
3315     }
3318 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3320     /* Freehand mode toggle buttons */
3321     {
3322         guint freehandMode = prefs_get_int_attribute(tool_is_pencil ? "tools.freehand.pencil" : "tools.freehand.pen", "freehand-mode", 0);
3323         Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3325         {
3326             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3328             GtkTreeIter iter;
3329             gtk_list_store_append( model, &iter );
3330             gtk_list_store_set( model, &iter,
3331                                 0, _("Bezier"),
3332                                 1, _("Create regular Bezier path"),
3333                                 2, "bezier_mode",
3334                                 -1 );
3336             gtk_list_store_append( model, &iter );
3337             gtk_list_store_set( model, &iter,
3338                                 0, _("Spiro"),
3339                                 1, _("Create Spiro path"),
3340                                 2, "spiro_splines_mode",
3341                                 -1 );
3343             if (!tool_is_pencil) {
3344                 gtk_list_store_append( model, &iter );
3345                 gtk_list_store_set( model, &iter,
3346                                     0, _("Zigzag"),
3347                                     1, _("Create a sequence of straight line segments"),
3348                                     2, "polylines_mode",
3349                                     -1 );
3351                 gtk_list_store_append( model, &iter );
3352                 gtk_list_store_set( model, &iter,
3353                                     0, _("Paraxial"),
3354                                     1, _("Create a sequence of paraxial line segments"),
3355                                     2, "paraxial_lines_mode",
3356                                     -1 );
3357             }
3359             EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3360                                                                 "FreehandModeActionPencil" :
3361                                                                 "FreehandModeActionPen",
3362                                                                 (_("Mode:")), ("Mode"), NULL, GTK_TREE_MODEL(model) );
3363             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3365             ege_select_one_action_set_appearance( act, "full" );
3366             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3367             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3368             ege_select_one_action_set_icon_column( act, 2 );
3369             ege_select_one_action_set_icon_size( act, secondarySize );
3370             ege_select_one_action_set_tooltip_column( act, 1  );
3372             ege_select_one_action_set_active( act, freehandMode);
3373             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3374         }
3375     }
3378 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3379     gint shape = ege_select_one_action_get_active( act );
3380     prefs_set_int_attribute(freehand_tool_name(dataKludge), "shape", shape);
3383 /**
3384  * \brief Generate the list of freehand advanced shape option entries.
3385  */
3386 GList * freehand_shape_dropdown_items_list() {
3387     GList *glist = NULL;
3389     glist = g_list_append (glist, _("None"));
3390     glist = g_list_append (glist, _("Triangle in"));
3391     glist = g_list_append (glist, _("Triangle out"));
3392     glist = g_list_append (glist, _("Ellipse"));
3393     glist = g_list_append (glist, _("From clipboard"));
3395     return glist;
3398 static void
3399 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3400     /*advanced shape options */
3401     {
3402         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3404         GList* items = 0;
3405         gint count = 0;
3406         for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3407         {
3408             GtkTreeIter iter;
3409             gtk_list_store_append( model, &iter );
3410             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3411             count++;
3412         }
3413         g_list_free( items );
3414         items = 0;
3415         EgeSelectOneAction* act1 = ege_select_one_action_new(
3416             tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3417             _("Shape:"), ("Shape"), NULL, GTK_TREE_MODEL(model));
3418         g_object_set( act1, "short_label", _("Shape:"), NULL );
3419         ege_select_one_action_set_appearance( act1, "compact" );
3420         ege_select_one_action_set_active( act1, prefs_get_int_attribute(tool_is_pencil ? "tools.freehand.pencil" : "tools.freehand.pen", "shape", 0) );
3421         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3422         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3423         g_object_set_data( holder, "shape_action", act1 );
3424     }
3427 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3429     sp_add_freehand_mode_toggle(mainActions, holder, false);
3430     freehand_add_advanced_shape_options(mainActions, holder, false);
3434 static void
3435 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3437     GtkWidget *tbl = GTK_WIDGET(obj);
3439     GtkAdjustment *adj;
3441     // fixme: make settable
3442     gdouble tolerance = 4;
3444     adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3445     gtk_adjustment_set_value(adj, tolerance);
3446     gtk_adjustment_value_changed(adj);
3448     spinbutton_defocus(GTK_OBJECT(tbl));
3451 static void
3452 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3455     // quit if run by the attr_changed listener
3456     if (g_object_get_data( tbl, "freeze" )) {
3457         return;
3458     }
3459     // in turn, prevent listener from responding
3460     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3461     prefs_set_double_attribute("tools.freehand.pencil",
3462                                "tolerance", adj->value);
3463     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3469 static void
3470 sp_pencil_tb_tolerance_value_changed_external(Inkscape::XML::Node */*repr*/,
3471                                               const gchar */*key*/,
3472                                               const gchar */*oldval*/,
3473                                               const gchar */*newval*/,
3474                                               bool /*is_interactive*/,
3475                                               void * data)
3477     GObject* tbl = G_OBJECT(data);
3478     if (g_object_get_data( tbl, "freeze" )) {
3479         return;
3480     }
3482     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3484     GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl,
3485                                                             "tolerance");
3487     double v = prefs_get_double_attribute("tools.freehand.pencil",
3488                                             "tolerance", adj->value);
3489     gtk_adjustment_set_value(adj, v);
3490     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3494 static Inkscape::XML::NodeEventVector pencil_node_events =
3496     NULL,
3497     NULL,
3498     sp_pencil_tb_tolerance_value_changed_external,
3499     NULL,
3500     NULL,
3501 };
3504 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3506     sp_add_freehand_mode_toggle(mainActions, holder, true);
3508     EgeAdjustmentAction* eact = 0;
3510     /* Tolerance */
3511     {
3512         gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
3513         gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
3514         eact = create_adjustment_action( "PencilToleranceAction",
3515                                          _("Smoothing:"), _("Smoothing: "),
3516                  _("How much smoothing (simplifying) is applied to the line"),
3517                                          "tools.freehand.pencil", "tolerance",
3518                                          3.0,
3519                                          GTK_WIDGET(desktop->canvas), NULL,
3520                                          holder, TRUE, "altx-pencil",
3521                                          1, 100.0, 0.5, 0,
3522                                          labels, values, G_N_ELEMENTS(labels),
3523                                          sp_pencil_tb_tolerance_value_changed,
3524                                          1, 2);
3525         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3526         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3528         Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE,
3529                                                       "tools.freehand.pencil");
3530         repr->addListener(&pencil_node_events, G_OBJECT(holder));
3531         g_object_set_data(G_OBJECT(holder), "repr", repr);
3533     }
3535     /* advanced shape options */
3536     freehand_add_advanced_shape_options(mainActions, holder, true);
3538     /* Reset */
3539     {
3540         InkAction* inky = ink_action_new( "PencilResetAction",
3541                                           _("Defaults"),
3542                                           _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3543                                           GTK_STOCK_CLEAR,
3544                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3545         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
3546         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3547     }
3549     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3554 //########################
3555 //##       Tweak        ##
3556 //########################
3558 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3560     prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3563 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3565     prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3568 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3570     prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3573 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3575     int mode = ege_select_one_action_get_active( act );
3576     prefs_set_int_attribute("tools.tweak", "mode", mode);
3578     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3579     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3580     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3581     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3582     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3583     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3584     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3585         if (doh) gtk_action_set_sensitive (doh, TRUE);
3586         if (dos) gtk_action_set_sensitive (dos, TRUE);
3587         if (dol) gtk_action_set_sensitive (dol, TRUE);
3588         if (doo) gtk_action_set_sensitive (doo, TRUE);
3589         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3590         if (fid) gtk_action_set_sensitive (fid, FALSE);
3591     } else {
3592         if (doh) gtk_action_set_sensitive (doh, FALSE);
3593         if (dos) gtk_action_set_sensitive (dos, FALSE);
3594         if (dol) gtk_action_set_sensitive (dol, FALSE);
3595         if (doo) gtk_action_set_sensitive (doo, FALSE);
3596         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3597         if (fid) gtk_action_set_sensitive (fid, TRUE);
3598     }
3601 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3603     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3606 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3607     bool show = gtk_toggle_action_get_active( act );
3608     prefs_set_int_attribute ("tools.tweak", "doh",  show ? 1 : 0);
3610 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3611     bool show = gtk_toggle_action_get_active( act );
3612     prefs_set_int_attribute ("tools.tweak", "dos",  show ? 1 : 0);
3614 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3615     bool show = gtk_toggle_action_get_active( act );
3616     prefs_set_int_attribute ("tools.tweak", "dol",  show ? 1 : 0);
3618 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3619     bool show = gtk_toggle_action_get_active( act );
3620     prefs_set_int_attribute ("tools.tweak", "doo",  show ? 1 : 0);
3623 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3625     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3627     {
3628         /* Width */
3629         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3630         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3631         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3632                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3633                                                               "tools.tweak", "width", 15,
3634                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3635                                                               1, 100, 1.0, 0.0,
3636                                                               labels, values, G_N_ELEMENTS(labels),
3637                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
3638         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3639         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3640         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3641     }
3644     {
3645         /* Force */
3646         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3647         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3648         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3649                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
3650                                                               "tools.tweak", "force", 20,
3651                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3652                                                               1, 100, 1.0, 0.0,
3653                                                               labels, values, G_N_ELEMENTS(labels),
3654                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
3655         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3656         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3657         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3658     }
3660     /* Mode */
3661     {
3662         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3664         GtkTreeIter iter;
3665         gtk_list_store_append( model, &iter );
3666         gtk_list_store_set( model, &iter,
3667                             0, _("Push mode"),
3668                             1, _("Push parts of paths in any direction"),
3669                             2, "tweak_push_mode",
3670                             -1 );
3672         gtk_list_store_append( model, &iter );
3673         gtk_list_store_set( model, &iter,
3674                             0, _("Shrink mode"),
3675                             1, _("Shrink (inset) parts of paths"),
3676                             2, "tweak_shrink_mode",
3677                             -1 );
3679         gtk_list_store_append( model, &iter );
3680         gtk_list_store_set( model, &iter,
3681                             0, _("Grow mode"),
3682                             1, _("Grow (outset) parts of paths"),
3683                             2, "tweak_grow_mode",
3684                             -1 );
3686         gtk_list_store_append( model, &iter );
3687         gtk_list_store_set( model, &iter,
3688                             0, _("Attract mode"),
3689                             1, _("Attract parts of paths towards cursor"),
3690                             2, "tweak_attract_mode",
3691                             -1 );
3693         gtk_list_store_append( model, &iter );
3694         gtk_list_store_set( model, &iter,
3695                             0, _("Repel mode"),
3696                             1, _("Repel parts of paths from cursor"),
3697                             2, "tweak_repel_mode",
3698                             -1 );
3700         gtk_list_store_append( model, &iter );
3701         gtk_list_store_set( model, &iter,
3702                             0, _("Roughen mode"),
3703                             1, _("Roughen parts of paths"),
3704                             2, "tweak_roughen_mode",
3705                             -1 );
3707         gtk_list_store_append( model, &iter );
3708         gtk_list_store_set( model, &iter,
3709                             0, _("Color paint mode"),
3710                             1, _("Paint the tool's color upon selected objects"),
3711                             2, "tweak_colorpaint_mode",
3712                             -1 );
3714         gtk_list_store_append( model, &iter );
3715         gtk_list_store_set( model, &iter,
3716                             0, _("Color jitter mode"),
3717                             1, _("Jitter the colors of selected objects"),
3718                             2, "tweak_colorjitter_mode",
3719                             -1 );
3721         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3722         g_object_set( act, "short_label", _("Mode:"), NULL );
3723         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3724         g_object_set_data( holder, "mode_action", act );
3726         ege_select_one_action_set_appearance( act, "full" );
3727         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3728         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3729         ege_select_one_action_set_icon_column( act, 2 );
3730         ege_select_one_action_set_icon_size( act, secondarySize );
3731         ege_select_one_action_set_tooltip_column( act, 1  );
3733         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3734         ege_select_one_action_set_active( act, mode );
3735         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3737         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3738     }
3740     guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3742     {
3743         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3744         ege_output_action_set_use_markup( act, TRUE );
3745         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3746         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3747             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3748         g_object_set_data( holder, "tweak_channels_label", act);
3749     }
3751     {
3752         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3753                                                       _("Hue"),
3754                                                       _("In color mode, act on objects' hue"),
3755                                                       NULL,
3756                                                       Inkscape::ICON_SIZE_DECORATION );
3757         //TRANSLATORS:  "H" here stands for hue
3758         g_object_set( act, "short_label", _("H"), NULL );
3759         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3760         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3761         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3762         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3763             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3764         g_object_set_data( holder, "tweak_doh", act);
3765     }
3766     {
3767         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3768                                                       _("Saturation"),
3769                                                       _("In color mode, act on objects' saturation"),
3770                                                       NULL,
3771                                                       Inkscape::ICON_SIZE_DECORATION );
3772         //TRANSLATORS: "S" here stands for Saturation
3773         g_object_set( act, "short_label", _("S"), NULL );
3774         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3775         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3776         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3777         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3778             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3779         g_object_set_data( holder, "tweak_dos", act );
3780     }
3781     {
3782         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3783                                                       _("Lightness"),
3784                                                       _("In color mode, act on objects' lightness"),
3785                                                       NULL,
3786                                                       Inkscape::ICON_SIZE_DECORATION );
3787         //TRANSLATORS: "L" here stands for Lightness
3788         g_object_set( act, "short_label", _("L"), NULL );
3789         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3790         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3791         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3792         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3793             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3794         g_object_set_data( holder, "tweak_dol", act );
3795     }
3796     {
3797         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3798                                                       _("Opacity"),
3799                                                       _("In color mode, act on objects' opacity"),
3800                                                       NULL,
3801                                                       Inkscape::ICON_SIZE_DECORATION );
3802         //TRANSLATORS: "O" here stands for Opacity
3803         g_object_set( act, "short_label", _("O"), NULL );
3804         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3805         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3806         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3807         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3808             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3809         g_object_set_data( holder, "tweak_doo", act );
3810     }
3812     {   /* Fidelity */
3813         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3814         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3815         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3816                                                               _("Fidelity"), _("Fidelity:"),
3817                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3818                                                               "tools.tweak", "fidelity", 50,
3819                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3820                                                               1, 100, 1.0, 10.0,
3821                                                               labels, values, G_N_ELEMENTS(labels),
3822                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
3823         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3824         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3825         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3826             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3827         g_object_set_data( holder, "tweak_fidelity", eact );
3828     }
3831     /* Use Pressure button */
3832     {
3833         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3834                                                       _("Pressure"),
3835                                                       _("Use the pressure of the input device to alter the force of tweak action"),
3836                                                       "use_pressure",
3837                                                       Inkscape::ICON_SIZE_DECORATION );
3838         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3839         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3840         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3841     }
3846 //########################
3847 //##     Calligraphy    ##
3848 //########################
3849 static void update_presets_list (GObject *tbl)
3851     if (g_object_get_data(tbl, "presets_blocked"))
3852         return;
3854     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3855     if (!sel) {
3856         ege_select_one_action_set_active(sel, 0);
3857         return;
3858     }
3860     int total_prefs = pref_path_number_of_children("tools.calligraphic.preset");
3862     for (int i = 1; i <= total_prefs; i++) {
3863         gchar *preset_path = get_pref_nth_child("tools.calligraphic.preset", i);
3864         Inkscape::XML::Node *preset_repr = inkscape_get_repr(INKSCAPE, preset_path);
3866         bool match = true;
3868         for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
3869               iter; 
3870               ++iter ) {
3871             const gchar *attr_name = g_quark_to_string(iter->key);
3872             if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
3873                 continue;
3874             void *widget = g_object_get_data(tbl, attr_name);
3875             if (widget) {
3876                 if (GTK_IS_ADJUSTMENT(widget)) {
3877                     double v = prefs_get_double_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
3878                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
3879                     //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
3880                     if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
3881                         match = false;
3882                         break;
3883                     }
3884                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
3885                     int v = prefs_get_int_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
3886                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
3887                     //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
3888                     if (gtk_toggle_action_get_active(toggle) != v) {
3889                         match = false;
3890                         break;
3891                     }
3892                 } 
3893             } 
3894         }
3896         if (match) {
3897             // newly added item is at the same index as the 
3898             // save command, so we need to change twice for it to take effect
3899             ege_select_one_action_set_active(sel, 0);
3900             ege_select_one_action_set_active(sel, i);
3901             return;
3902         }
3903     }
3905     // no match found
3906     ege_select_one_action_set_active(sel, 0);
3909 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3911     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value * 0.01 );
3912     update_presets_list(tbl);
3915 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3917     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value * 0.01 );
3918     update_presets_list(tbl);
3921 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3923     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3924     update_presets_list(tbl);
3927 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3929     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3930     update_presets_list(tbl);
3933 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3935     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value * 0.01 );
3936     update_presets_list(tbl);
3939 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3941     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value * 0.01);
3942     update_presets_list(tbl);
3945 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3947     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value * 0.01 );
3948     update_presets_list(tbl);
3951 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3953     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3954     update_presets_list(tbl);
3957 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
3959     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3960     update_presets_list(tbl);
3963 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
3965     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3966     update_presets_list(tbl);
3969 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
3971     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
3972     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3973     update_presets_list(tbl);
3974     if (calligraphy_angle )
3975         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3979 static gchar const *const widget_names[] = {
3980     "width",
3981     "mass",
3982     "wiggle",
3983     "angle",
3984     "thinning",
3985     "tremor",
3986     "flatness",
3987     "cap_rounding",
3988     "usepressure",
3989     "tracebackground",
3990     "usetilt"
3991 };
3994 static void sp_dcc_build_presets_list(GObject *tbl) 
3996     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
3998     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3999     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4000     gtk_list_store_clear (model);
4002     {
4003         GtkTreeIter iter;
4004         gtk_list_store_append( model, &iter );
4005         gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4006     }
4008     //TODO: switch back to prefs API
4009     Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
4010     Inkscape::XML::Node *child_repr = sp_repr_children(repr);
4011     int ii=1;
4012     while (child_repr) {
4013         GtkTreeIter iter;
4014         char *preset_name = (char *) child_repr->attribute("name");
4015         gtk_list_store_append( model, &iter );
4016         gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );
4017         child_repr = sp_repr_next(child_repr);
4018     }
4020     {
4021         GtkTreeIter iter;
4022         gtk_list_store_append( model, &iter );
4023         gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4024         g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4025     }
4027     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4029     update_presets_list (tbl);
4032 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4034     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4035     if (! desktop) return;
4037     if (g_object_get_data(tbl, "presets_blocked")) 
4038         return;
4040     Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
4041     if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) {
4042         // dialog cancelled
4043         update_presets_list (tbl);
4044         return;
4045     }
4046     Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
4048     if (!profile_name.c_str() || *profile_name.c_str() == 0) {
4049         // empty name entered
4050         update_presets_list (tbl);
4051         return;
4052     }
4054     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4056     int new_index = -1;
4057     gchar *pref_path = NULL;
4058     int total_prefs = pref_path_number_of_children("tools.calligraphic.preset");
4060     for (int i = 1; i <= total_prefs; i++) {
4061         gchar *path = get_pref_nth_child("tools.calligraphic.preset", i);
4062         const gchar *name = prefs_get_string_attribute(path, "name");
4063         if (name && !strcmp(name, profile_name.c_str())) {
4064             // we already have preset with this name, replace its values
4065             new_index = i;
4066             pref_path = g_strdup(path);
4067             break;
4068         }
4069     }
4071     if (new_index == -1) {
4072         // no preset with this name, create 
4073         new_index = total_prefs + 1;
4074         gchar *profile_id = g_strdup_printf("dcc%d", new_index);
4075         pref_path = create_pref("tools.calligraphic.preset", profile_id);
4076         g_free(profile_id);
4077     }
4079     for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4080         gchar const *const widget_name = widget_names[i];
4081         void *widget = g_object_get_data(tbl, widget_name);
4082         if (widget) {
4083             if (GTK_IS_ADJUSTMENT(widget)) {
4084                 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4085                 double v = gtk_adjustment_get_value(adj);
4086                 prefs_set_double_attribute(pref_path, widget_name, v);
4087                 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4088             } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4089                 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4090                 int v = gtk_toggle_action_get_active(toggle);
4091                 prefs_set_int_attribute(pref_path, widget_name, v);
4092                 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4093             } else {
4094                g_warning("Unknown widget type for preset: %s\n", widget_name);
4095             }
4096         } else {
4097             g_warning("Bad key when writing preset: %s\n", widget_name);
4098         }
4099     }
4100     prefs_set_string_attribute(pref_path, "name", profile_name.c_str());
4102     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4104     sp_dcc_build_presets_list (tbl);
4106     g_free(pref_path);
4110 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4112     gint preset_index = ege_select_one_action_get_active( act );
4113     gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4115     if (preset_index == save_presets_index) {
4116         // this is the Save command
4117         sp_dcc_save_profile(NULL, tbl);
4118         return;
4119     }
4121     if (g_object_get_data(tbl, "presets_blocked")) 
4122         return;
4124     gchar *const preset_path = get_pref_nth_child("tools.calligraphic.preset", preset_index);
4126     if (preset_path) {
4127         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
4129         Inkscape::XML::Node *preset_repr = inkscape_get_repr(INKSCAPE, preset_path);
4131         for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
4132               iter; 
4133               ++iter ) {
4134             const gchar *attr_name = g_quark_to_string(iter->key);
4135             if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
4136                 continue;
4137             void *widget = g_object_get_data(tbl, attr_name);
4138             if (widget) {
4139                 if (GTK_IS_ADJUSTMENT(widget)) {
4140                     double v = prefs_get_double_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4141                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4142                     gtk_adjustment_set_value(adj, v);
4143                     //std::cout << "set adj " << attr_name << " to " << v << "\n";
4144                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4145                     int v = prefs_get_int_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4146                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4147                     gtk_toggle_action_set_active(toggle, v);
4148                     //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4149                 } else {
4150                     g_warning("Unknown widget type for preset: %s\n", attr_name);
4151                 }
4152             } else {
4153                 g_warning("Bad key found in a preset record: %s\n", attr_name);
4154             }
4155         }
4156         g_free(preset_path);
4157         g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4158     }
4163 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4165     {
4166         g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4168         EgeAdjustmentAction* calligraphy_angle = 0;
4170         {
4171         /* Width */
4172         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4173         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4174         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4175                                                               _("Pen Width"), _("Width:"),
4176                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
4177                                                               "tools.calligraphic", "width", 15,
4178                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4179                                                               1, 100, 1.0, 0.0,
4180                                                               labels, values, G_N_ELEMENTS(labels),
4181                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
4182         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4183         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4184         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4185         }
4187         {
4188         /* Thinning */
4189             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4190             gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4191         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4192                                                               _("Stroke Thinning"), _("Thinning:"),
4193                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4194                                                               "tools.calligraphic", "thinning", 10,
4195                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4196                                                               -100, 100, 1, 0.1,
4197                                                               labels, values, G_N_ELEMENTS(labels),
4198                                                               sp_ddc_velthin_value_changed, 0.01, 0, 100);
4199         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4200         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4201         }
4203         {
4204         /* Angle */
4205         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4206         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4207         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4208                                                               _("Pen Angle"), _("Angle:"),
4209                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4210                                                               "tools.calligraphic", "angle", 30,
4211                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4212                                                               -90.0, 90.0, 1.0, 10.0,
4213                                                               labels, values, G_N_ELEMENTS(labels),
4214                                                               sp_ddc_angle_value_changed, 1, 0 );
4215         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4216         g_object_set_data( holder, "angle_action", eact );
4217         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4218         calligraphy_angle = eact;
4219         }
4221         {
4222         /* Fixation */
4223             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4224         gdouble values[] = {0, 20, 40, 60, 90, 100};
4225         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4226                                                               _("Fixation"), _("Fixation:"),
4227                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
4228                                                               "tools.calligraphic", "flatness", 90,
4229                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4230                                                               0.0, 100, 1.0, 10.0,
4231                                                               labels, values, G_N_ELEMENTS(labels),
4232                                                               sp_ddc_flatness_value_changed, 0.01, 0, 100 );
4233         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4234         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4235         }
4237         {
4238         /* Cap Rounding */
4239             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4240         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4241         // TRANSLATORS: "cap" means "end" (both start and finish) here
4242         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4243                                                               _("Cap rounding"), _("Caps:"),
4244                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4245                                                               "tools.calligraphic", "cap_rounding", 0.0,
4246                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4247                                                               0.0, 5.0, 0.01, 0.1,
4248                                                               labels, values, G_N_ELEMENTS(labels),
4249                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4250         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4251         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4252         }
4254         {
4255         /* Tremor */
4256             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4257         gdouble values[] = {0, 10, 20, 40, 60, 100};
4258         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4259                                                               _("Stroke Tremor"), _("Tremor:"),
4260                                                               _("Increase to make strokes rugged and trembling"),
4261                                                               "tools.calligraphic", "tremor", 0.0,
4262                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4263                                                               0.0, 100, 1, 0.0,
4264                                                               labels, values, G_N_ELEMENTS(labels),
4265                                                               sp_ddc_tremor_value_changed, 0.01, 0, 100 );
4267         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4268         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4269         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4270         }
4272         {
4273         /* Wiggle */
4274         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4275         gdouble values[] = {0, 20, 40, 60, 100};
4276         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4277                                                               _("Pen Wiggle"), _("Wiggle:"),
4278                                                               _("Increase to make the pen waver and wiggle"),
4279                                                               "tools.calligraphic", "wiggle", 0.0,
4280                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4281                                                               0.0, 100, 1, 0.0,
4282                                                               labels, values, G_N_ELEMENTS(labels),
4283                                                               sp_ddc_wiggle_value_changed, 0.01, 0, 100 );
4284         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4285         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4286         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4287         }
4289         {
4290         /* Mass */
4291             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4292         gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4293         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4294                                                               _("Pen Mass"), _("Mass:"),
4295                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
4296                                                               "tools.calligraphic", "mass", 2.0,
4297                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4298                                                               0.0, 100, 1, 0.0,
4299                                                               labels, values, G_N_ELEMENTS(labels),
4300                                                               sp_ddc_mass_value_changed, 0.01, 0, 100 );
4301         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4302         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4303         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4304         }
4307         /* Trace Background button */
4308         {
4309             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4310                                                           _("Trace Background"),
4311                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4312                                                           "trace_background",
4313                                                           Inkscape::ICON_SIZE_DECORATION );
4314             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4315             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4316             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
4317             g_object_set_data( holder, "tracebackground", act );
4318         }
4320         /* Use Pressure button */
4321         {
4322             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4323                                                           _("Pressure"),
4324                                                           _("Use the pressure of the input device to alter the width of the pen"),
4325                                                           "use_pressure",
4326                                                           Inkscape::ICON_SIZE_DECORATION );
4327             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4328             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4329             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
4330             g_object_set_data( holder, "usepressure", act );
4331         }
4333         /* Use Tilt button */
4334         {
4335             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4336                                                           _("Tilt"),
4337                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
4338                                                           "use_tilt",
4339                                                           Inkscape::ICON_SIZE_DECORATION );
4340             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4341             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4342             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4343             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4344             g_object_set_data( holder, "usetilt", act );
4345         }
4347         /*calligraphic profile */
4348         {
4349             GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
4350             EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
4351             ege_select_one_action_set_appearance (act1, "compact");
4352             g_object_set_data (holder, "profile_selector", act1 );
4354             g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
4356             sp_dcc_build_presets_list (holder);
4358             g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
4359             gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
4360         }
4361     }
4365 //########################
4366 //##    Circle / Arc    ##
4367 //########################
4369 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4371     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4372     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4374     if (v1 == 0 && v2 == 0) {
4375         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4376             gtk_action_set_sensitive( ocb, FALSE );
4377             gtk_action_set_sensitive( make_whole, FALSE );
4378         }
4379     } else {
4380         gtk_action_set_sensitive( ocb, TRUE );
4381         gtk_action_set_sensitive( make_whole, TRUE );
4382     }
4385 static void
4386 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4388     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4390     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4391         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
4392     }
4394     // quit if run by the attr_changed listener
4395     if (g_object_get_data( tbl, "freeze" )) {
4396         return;
4397     }
4399     // in turn, prevent listener from responding
4400     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4402     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4404     bool modmade = false;
4405     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4406          items != NULL;
4407          items = items->next)
4408     {
4409         SPItem *item = SP_ITEM(items->data);
4411         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4413             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4414             SPArc *arc = SP_ARC(item);
4416             if (!strcmp(value_name, "start"))
4417                 ge->start = (adj->value * M_PI)/ 180;
4418             else
4419                 ge->end = (adj->value * M_PI)/ 180;
4421             sp_genericellipse_normalize(ge);
4422             ((SPObject *)arc)->updateRepr();
4423             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4425             modmade = true;
4426         }
4427     }
4429     g_free(namespaced_name);
4431     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4433     sp_arctb_sensitivize( tbl, adj->value, other->value );
4435     if (modmade) {
4436         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4437                                    _("Arc: Change start/end"));
4438     }
4440     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4444 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
4446     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
4449 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4451     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
4455 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4457     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4458     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4459         if ( ege_select_one_action_get_active( act ) != 0 ) {
4460             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
4461         } else {
4462             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
4463         }
4464     }
4466     // quit if run by the attr_changed listener
4467     if (g_object_get_data( tbl, "freeze" )) {
4468         return;
4469     }
4471     // in turn, prevent listener from responding
4472     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4474     bool modmade = false;
4476     if ( ege_select_one_action_get_active(act) != 0 ) {
4477         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4478              items != NULL;
4479              items = items->next)
4480         {
4481             if (SP_IS_ARC((SPItem *) items->data)) {
4482                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4483                 repr->setAttribute("sodipodi:open", "true");
4484                 SP_OBJECT((SPItem *) items->data)->updateRepr();
4485                 modmade = true;
4486             }
4487         }
4488     } else {
4489         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4490              items != NULL;
4491              items = items->next)
4492         {
4493             if (SP_IS_ARC((SPItem *) items->data))    {
4494                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4495                 repr->setAttribute("sodipodi:open", NULL);
4496                 SP_OBJECT((SPItem *) items->data)->updateRepr();
4497                 modmade = true;
4498             }
4499         }
4500     }
4502     if (modmade) {
4503         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4504                                    _("Arc: Change open/closed"));
4505     }
4507     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4510 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4512     GtkAdjustment *adj;
4513     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
4514     gtk_adjustment_set_value(adj, 0.0);
4515     gtk_adjustment_value_changed(adj);
4517     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
4518     gtk_adjustment_set_value(adj, 0.0);
4519     gtk_adjustment_value_changed(adj);
4521     spinbutton_defocus( GTK_OBJECT(obj) );
4524 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
4525                                       gchar const */*old_value*/, gchar const */*new_value*/,
4526                                       bool /*is_interactive*/, gpointer data)
4528     GObject *tbl = G_OBJECT(data);
4530     // quit if run by the _changed callbacks
4531     if (g_object_get_data( tbl, "freeze" )) {
4532         return;
4533     }
4535     // in turn, prevent callbacks from responding
4536     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4538     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4539     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4541     GtkAdjustment *adj1,*adj2;
4542     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4543     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4544     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4545     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4547     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4549     char const *openstr = NULL;
4550     openstr = repr->attribute("sodipodi:open");
4551     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4553     if (openstr) {
4554         ege_select_one_action_set_active( ocb, 1 );
4555     } else {
4556         ege_select_one_action_set_active( ocb, 0 );
4557     }
4559     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4562 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4563     NULL, /* child_added */
4564     NULL, /* child_removed */
4565     arc_tb_event_attr_changed,
4566     NULL, /* content_changed */
4567     NULL  /* order_changed */
4568 };
4571 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4573     int n_selected = 0;
4574     Inkscape::XML::Node *repr = NULL;
4576     purge_repr_listener( tbl, tbl );
4578     for (GSList const *items = selection->itemList();
4579          items != NULL;
4580          items = items->next)
4581     {
4582         if (SP_IS_ARC((SPItem *) items->data)) {
4583             n_selected++;
4584             repr = SP_OBJECT_REPR((SPItem *) items->data);
4585         }
4586     }
4588     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4590     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4591     if (n_selected == 0) {
4592         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4593     } else if (n_selected == 1) {
4594         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4595         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4597         if (repr) {
4598             g_object_set_data( tbl, "repr", repr );
4599             Inkscape::GC::anchor(repr);
4600             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4601             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4602         }
4603     } else {
4604         // FIXME: implement averaging of all parameters for multiple selected
4605         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4606         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4607         sp_arctb_sensitivize( tbl, 1, 0 );
4608     }
4612 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4614     EgeAdjustmentAction* eact = 0;
4615     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
4618     {
4619         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4620         ege_output_action_set_use_markup( act, TRUE );
4621         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4622         g_object_set_data( holder, "mode_action", act );
4623     }
4625     /* Start */
4626     {
4627         eact = create_adjustment_action( "ArcStartAction",
4628                                          _("Start"), _("Start:"),
4629                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
4630                                          "tools.shapes.arc", "start", 0.0,
4631                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4632                                          -360.0, 360.0, 1.0, 10.0,
4633                                          0, 0, 0,
4634                                          sp_arctb_start_value_changed);
4635         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4636     }
4638     /* End */
4639     {
4640         eact = create_adjustment_action( "ArcEndAction",
4641                                          _("End"), _("End:"),
4642                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
4643                                          "tools.shapes.arc", "end", 0.0,
4644                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4645                                          -360.0, 360.0, 1.0, 10.0,
4646                                          0, 0, 0,
4647                                          sp_arctb_end_value_changed);
4648         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4649     }
4651     /* Segments / Pie checkbox */
4652     {
4653         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4655         GtkTreeIter iter;
4656         gtk_list_store_append( model, &iter );
4657         gtk_list_store_set( model, &iter,
4658                             0, _("Closed arc"),
4659                             1, _("Switch to segment (closed shape with two radii)"),
4660                             2, "circle_closed_arc",
4661                             -1 );
4663         gtk_list_store_append( model, &iter );
4664         gtk_list_store_set( model, &iter,
4665                             0, _("Open Arc"),
4666                             1, _("Switch to arc (unclosed shape)"),
4667                             2, "circle_open_arc",
4668                             -1 );
4670         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4671         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4672         g_object_set_data( holder, "open_action", act );
4674         ege_select_one_action_set_appearance( act, "full" );
4675         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4676         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4677         ege_select_one_action_set_icon_column( act, 2 );
4678         ege_select_one_action_set_icon_size( act, secondarySize );
4679         ege_select_one_action_set_tooltip_column( act, 1  );
4681         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4682         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4683         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4684         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4685     }
4687     /* Make Whole */
4688     {
4689         InkAction* inky = ink_action_new( "ArcResetAction",
4690                                           _("Make whole"),
4691                                           _("Make the shape a whole ellipse, not arc or segment"),
4692                                           "reset_circle",
4693                                           secondarySize );
4694         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4695         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4696         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4697         g_object_set_data( holder, "make_whole", inky );
4698     }
4700     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4701     // sensitivize make whole and open checkbox
4702     {
4703         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4704         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4705         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4706     }
4709     sigc::connection *connection = new sigc::connection(
4710         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4711         );
4712     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4713     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4719 // toggle button callbacks and updaters
4721 //########################
4722 //##      Dropper       ##
4723 //########################
4725 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4726     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4727     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4728     if ( set_action ) {
4729         if ( gtk_toggle_action_get_active( act ) ) {
4730             gtk_action_set_sensitive( set_action, TRUE );
4731         } else {
4732             gtk_action_set_sensitive( set_action, FALSE );
4733         }
4734     }
4736     spinbutton_defocus(GTK_OBJECT(tbl));
4739 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4740     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4741     spinbutton_defocus(GTK_OBJECT(tbl));
4745 /**
4746  * Dropper auxiliary toolbar construction and setup.
4747  *
4748  * TODO: Would like to add swatch of current color.
4749  * TODO: Add queue of last 5 or so colors selected with new swatches so that
4750  *       can drag and drop places. Will provide a nice mixing palette.
4751  */
4752 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4754     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4756     {
4757         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4758         ege_output_action_set_use_markup( act, TRUE );
4759         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4760     }
4762     {
4763         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4764                                                       _("Pick opacity"),
4765                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4766                                                       NULL,
4767                                                       Inkscape::ICON_SIZE_DECORATION );
4768         g_object_set( act, "short_label", _("Pick"), NULL );
4769         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4770         g_object_set_data( holder, "pick_action", act );
4771         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4772         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4773     }
4775     {
4776         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4777                                                       _("Assign opacity"),
4778                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4779                                                       NULL,
4780                                                       Inkscape::ICON_SIZE_DECORATION );
4781         g_object_set( act, "short_label", _("Assign"), NULL );
4782         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4783         g_object_set_data( holder, "set_action", act );
4784         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4785         // make sure it's disabled if we're not picking alpha
4786         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4787         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4788     }
4792 //########################
4793 //##      LPETool       ##
4794 //########################
4796 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
4798 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
4799 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
4801     using namespace Inkscape::LivePathEffect;
4803     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
4804     SPEventContext *ec = desktop->event_context;
4805     if (!SP_IS_LPETOOL_CONTEXT(ec)) {
4806         return;
4807     }
4809     // only take action if run by the attr_changed listener
4810     if (!g_object_get_data(tbl, "freeze")) {
4811         // in turn, prevent listener from responding
4812         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
4814         gint mode = ege_select_one_action_get_active(act);
4815         EffectType type = lpesubtools[mode];
4817         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4818         bool success = lpetool_try_construction(lc, type);
4819         if (success) {
4820             // since the construction was already performed, we set the state back to inactive
4821             ege_select_one_action_set_active(act, 0);
4822             mode = 0;
4823         } else {
4824             // switch to the chosen subtool
4825             SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
4826         }
4828         if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4829             prefs_set_int_attribute( "tools.lpetool", "mode", mode );
4830         }
4832         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
4833     }
4836 void
4837 sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4839     SPEventContext *ec = selection->desktop()->event_context;
4840     if (!SP_IS_LPETOOL_CONTEXT(ec))
4841         return;
4843     lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
4846 void
4847 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
4849     using namespace Inkscape::LivePathEffect;
4850     SPEventContext *ec = selection->desktop()->event_context;
4851     if (!SP_IS_LPETOOL_CONTEXT(ec))
4852         return;
4853     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
4855     lpetool_delete_measuring_items(lc);
4856     lpetool_create_measuring_items(lc, selection);
4858     // activate line segment combo box if a single item with LPELineSegment is selected
4859     GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
4860     SPItem *item = selection->singleItem();
4861     if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
4862         SPLPEItem *lpeitem = SP_LPE_ITEM(item);
4863         Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
4864         if (lpe && lpe->effectType() == LINE_SEGMENT) {
4865             LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
4866             g_object_set_data(tbl, "currentlpe", lpe);
4867             g_object_set_data(tbl, "currentlpeitem", lpeitem);
4868             gtk_action_set_sensitive(w, TRUE);
4869             ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
4870         } else {
4871             g_object_set_data(tbl, "currentlpe", NULL);
4872             g_object_set_data(tbl, "currentlpeitem", NULL);
4873             gtk_action_set_sensitive(w, FALSE);
4874         }
4875     } else {
4876         g_object_set_data(tbl, "currentlpe", NULL);
4877         g_object_set_data(tbl, "currentlpeitem", NULL);
4878         gtk_action_set_sensitive(w, FALSE);
4879     }
4882 static void
4883 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
4884     SPDesktop *desktop = static_cast<SPDesktop *>(data);
4886     bool show = gtk_toggle_action_get_active( act );
4887     prefs_set_int_attribute ("tools.lpetool", "show_bbox",  show ? 1 : 0);
4889     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
4890         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4891         lpetool_context_reset_limiting_bbox(lc);
4892     }
4895 static void
4896 lpetool_toggle_show_measuring_info (GtkToggleAction *act, gpointer data) {
4897     SPDesktop *desktop = static_cast<SPDesktop *>(data);
4898     if (!tools_isactive(desktop, TOOLS_LPETOOL))
4899         return;
4901     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4902     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
4903         bool show = gtk_toggle_action_get_active( act );
4904         prefs_set_int_attribute ("tools.lpetool", "show_measuring_info",  show ? 1 : 0);
4905         lpetool_show_measuring_info(lc, show);
4906     }
4909 static void
4910 lpetool_unit_changed(GtkAction* act, GObject* tbl) {
4911     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
4912     SPUnit const *unit = tracker->getActiveUnit();
4913     prefs_set_int_attribute("tools.lpetool", "unitid", unit->unit_id);
4915     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4916     if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
4917         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4918         lpetool_delete_measuring_items(lc);
4919         lpetool_create_measuring_items(lc);
4920     }
4923 static void
4924 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
4925     SPDesktop *desktop = static_cast<SPDesktop *>(data);
4926     Inkscape::Selection *selection = desktop->selection;
4928     boost::optional<NR::Rect> bbox = selection->bounds();
4930     if (bbox) {
4931         Geom::Point A(bbox->min());
4932         Geom::Point B(bbox->max());
4934         A *= desktop->doc2dt();
4935         B *= desktop->doc2dt();
4937         // TODO: should we provide a way to store points in prefs?
4938         prefs_set_double_attribute ("tools.lpetool", "bbox_upperleftx", A[Geom::X]);
4939         prefs_set_double_attribute ("tools.lpetool", "bbox_upperlefty", A[Geom::Y]);
4940         prefs_set_double_attribute ("tools.lpetool", "bbox_lowerrightx", B[Geom::X]);
4941         prefs_set_double_attribute ("tools.lpetool", "bbox_lowerrighty", B[Geom::Y]);
4943         lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
4944     }
4946     gtk_toggle_action_set_active(act, false);
4949 static void
4950 sp_line_segment_build_list(GObject *tbl) 
4952     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
4954     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
4955     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4956     gtk_list_store_clear (model);
4958     // TODO: we add the entries of rht combo box manually; later this should be done automatically
4959     {
4960         GtkTreeIter iter;
4961         gtk_list_store_append( model, &iter );
4962         gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
4963         gtk_list_store_append( model, &iter );
4964         gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
4965         gtk_list_store_append( model, &iter );
4966         gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
4967         gtk_list_store_append( model, &iter );
4968         gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
4969     }
4971     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
4974 static void
4975 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
4976     using namespace Inkscape::LivePathEffect;
4978     // quit if run by the attr_changed listener
4979     if (g_object_get_data(tbl, "freeze")) {
4980         return;
4981     }
4983     // in turn, prevent listener from responding
4984     g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
4986     LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
4987     SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
4988     if (lpeitem) {
4989         SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
4990         lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
4991         sp_lpe_item_update_patheffect(lpeitem, true, true);
4992     }
4994     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4997 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4999     UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5000     tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5001     g_object_set_data(holder, "tracker", tracker);
5002     SPUnit const *unit = tracker->getActiveUnit();
5003     prefs_set_int_attribute("tools.lpetool", "unitid", unit->unit_id);
5005     /** Automatically create a list of LPEs that get added to the toolbar **/
5006     {
5007         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5009         GtkTreeIter iter;
5011         // the first toggle button represents the state that no subtool is active (remove this when
5012         // this can be modeled by EgeSelectOneAction or some other action)
5013         gtk_list_store_append( model, &iter );
5014         gtk_list_store_set( model, &iter,
5015                             0, _("All inactive"),
5016                             1, _("No geometric tool is active"),
5017                             2, _("all_inactive"),
5018                             -1 );
5020         Inkscape::LivePathEffect::EffectType type;
5021         for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5022             type =  lpesubtools[i];
5023             gtk_list_store_append( model, &iter );
5024             gtk_list_store_set( model, &iter,
5025                                 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5026                                 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5027                                 2, Inkscape::LivePathEffect::LPETypeConverter.get_key(type).c_str(),
5028                                 -1 );
5029         }
5031         EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5032         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5033         g_object_set_data( holder, "lpetool_mode_action", act );
5035         ege_select_one_action_set_appearance( act, "full" );
5036         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5037         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5038         ege_select_one_action_set_icon_column( act, 2 );
5039         ege_select_one_action_set_tooltip_column( act, 1  );
5041         gint lpeToolMode = prefs_get_int_attribute("tools.lpetool", "mode", 0);
5042         ege_select_one_action_set_active( act, lpeToolMode );
5043         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5044     }
5046     /* Show limiting bounding box */
5047     {
5048         InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5049                                                       _("Show limiting bounding box"),
5050                                                       _("Show bounding box (used to cut infinite lines)"),
5051                                                       "lpetool_show_bbox",
5052                                                       Inkscape::ICON_SIZE_DECORATION );
5053         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5054         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5055         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.lpetool", "show_bbox", 1 ) );
5056     }
5058     /* Set limiting bounding box to bbox of current selection */
5059     {
5060         InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5061                                                       _("Get limiting bounding box from selection"),
5062                                                       _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5063                                                       "lpetool_set_bbox",
5064                                                       Inkscape::ICON_SIZE_DECORATION );
5065         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5066         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5067         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5068     }
5071     /* Combo box to choose line segment type */
5072     {
5073         GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5074         EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5075         ege_select_one_action_set_appearance (act, "compact");
5076         g_object_set_data (holder, "lpetool_line_segment_action", act );
5078         g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5080         sp_line_segment_build_list (holder);
5082         g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5083         gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5084         gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5085     }
5087     /* Display measuring info for selected items */
5088     {
5089         InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5090                                                       _("Display measuring info"),
5091                                                       _("Display measuring info for selected items"),
5092                                                       "lpetool_measuring_info",
5093                                                       Inkscape::ICON_SIZE_DECORATION );
5094         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5095         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), desktop );
5096         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.lpetool", "show_measuring_info", 1 ) );
5097     }
5099     // add the units menu
5100     {
5101         GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5102         gtk_action_group_add_action( mainActions, act );
5103         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5104     }
5106     //watch selection
5107     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5109     sigc::connection *c_selection_modified =
5110         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5111                               (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
5112     pool->add_connection ("selection-modified", c_selection_modified);
5114     sigc::connection *c_selection_changed =
5115         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5116                               (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5117     pool->add_connection ("selection-changed", c_selection_changed);
5120 //########################
5121 //##       Eraser       ##
5122 //########################
5124 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5126     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5127     gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
5128     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5129         prefs_set_int_attribute( "tools.eraser", "mode", eraserMode );
5130     }
5132     // only take action if run by the attr_changed listener
5133     if (!g_object_get_data( tbl, "freeze" )) {
5134         // in turn, prevent listener from responding
5135         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5137         if ( eraserMode != 0 ) {
5138         } else {
5139         }
5140         // TODO finish implementation
5142         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5143     }
5146 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5148     {
5149         /* Width */
5150         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5151         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5152         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5153                                                               _("Pen Width"), _("Width:"),
5154                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
5155                                                               "tools.eraser", "width", 15,
5156                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5157                                                               1, 100, 1.0, 0.0,
5158                                                               labels, values, G_N_ELEMENTS(labels),
5159                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
5160         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5161         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5162         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5163     }
5165     {
5166         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5168         GtkTreeIter iter;
5169         gtk_list_store_append( model, &iter );
5170         gtk_list_store_set( model, &iter,
5171                             0, _("Delete"),
5172                             1, _("Delete objects touched by the eraser"),
5173                             2, "delete_object",
5174                             -1 );
5176         gtk_list_store_append( model, &iter );
5177         gtk_list_store_set( model, &iter,
5178                             0, _("Cut"),
5179                             1, _("Cut out from objects"),
5180                             2, "difference",
5181                             -1 );
5183         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5184         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5185         g_object_set_data( holder, "eraser_mode_action", act );
5187         ege_select_one_action_set_appearance( act, "full" );
5188         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5189         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5190         ege_select_one_action_set_icon_column( act, 2 );
5191         ege_select_one_action_set_tooltip_column( act, 1  );
5193         gint eraserMode = (prefs_get_int_attribute("tools.eraser", "mode", 0) != 0) ? 1 : 0;
5194         ege_select_one_action_set_active( act, eraserMode );
5195         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
5196     }
5200 //########################
5201 //##    Text Toolbox    ##
5202 //########################
5203 /*
5204 static void
5205 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
5207     //Call back for letter sizing spinbutton
5210 static void
5211 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
5213     //Call back for line height spinbutton
5216 static void
5217 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5219     //Call back for horizontal kerning spinbutton
5222 static void
5223 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5225     //Call back for vertical kerning spinbutton
5228 static void
5229 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
5231     //Call back for letter rotation spinbutton
5232 }*/
5234 namespace {
5236 bool popdown_visible = false;
5237 bool popdown_hasfocus = false;
5239 void
5240 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
5242     SPStyle *query =
5243         sp_style_new (SP_ACTIVE_DOCUMENT);
5245 //    int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5247     int result_family =
5248         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5250     int result_style =
5251         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5253     int result_numbers =
5254         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5256     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5258     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5259     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
5260         // there are no texts in selection, read from prefs
5261  
5262         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
5263         if (repr) {
5264             sp_style_read_from_repr (query, repr);
5265             if (g_object_get_data(tbl, "text_style_from_prefs")) {
5266                 // do not reset the toolbar style from prefs if we already did it last time
5267                 sp_style_unref(query);
5268                 return;
5269             }
5270             g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
5271         } else {
5272             sp_style_unref(query);
5273             return;
5274         }
5275     } else {
5276         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
5277     }
5279     if (query->text)
5280     {
5281         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
5282             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5283             gtk_entry_set_text (GTK_ENTRY (entry), "");
5285         } else if (query->text->font_specification.value || query->text->font_family.value) {
5287             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5289             // Get the font that corresponds
5290             Glib::ustring familyName;
5292             font_instance * font = font_factory::Default()->FaceFromStyle(query);
5293             if (font) {
5294                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
5295                 font->Unref();
5296                 font = NULL;
5297             }
5299             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
5301             Gtk::TreePath path;
5302             try {
5303                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
5304             } catch (...) {
5305                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
5306                 sp_style_unref(query);
5307                 return;
5308             }
5310             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5311             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5313             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
5315             gtk_tree_selection_select_path (tselection, path.gobj());
5316             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5318             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
5319         }
5321         //Size
5322         {
5323             GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
5324             gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
5325             g_object_set_data(tbl, "size-block", gpointer(1));
5326             gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
5327             g_object_set_data(tbl, "size-block", gpointer(0));
5328             g_free(str);
5329         }
5331         //Anchor
5332         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
5333         {
5334             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
5335             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5336             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5337             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5338         }
5339         else
5340         {
5341             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
5342             {
5343                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
5344                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5345                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5346                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5347             }
5348             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
5349             {
5350                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
5351                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5352                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5353                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5354             }
5355             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
5356             {
5357                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
5358                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5359                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5360                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5361             }
5362         }
5364         //Style
5365         {
5366             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
5368             gboolean active = gtk_toggle_button_get_active (button);
5369             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
5371             if (active != check)
5372             {
5373                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5374                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5375                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5376             }
5377         }
5379         {
5380             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
5382             gboolean active = gtk_toggle_button_get_active (button);
5383             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
5385             if (active != check)
5386             {
5387                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5388                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5389                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5390             }
5391         }
5393         //Orientation
5394         //locking both buttons, changing one affect all group (both)
5395         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
5396         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5398         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
5399         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
5401         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
5402         {
5403             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5404         }
5405         else
5406         {
5407             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
5408         }
5409         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5410         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
5411     }
5413     sp_style_unref(query);
5416 void
5417 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
5419     sp_text_toolbox_selection_changed (selection, tbl);
5422 void
5423 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
5425     sp_text_toolbox_selection_changed (NULL, tbl);
5428 void
5429 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
5430                                 GObject             *tbl)
5432     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
5433     GtkTreeModel *model = 0;
5434     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5435     GtkTreeIter   iter;
5436     char         *family = 0;
5438     gdk_pointer_ungrab (GDK_CURRENT_TIME);
5439     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5441     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
5442         return;
5443     }
5445     gtk_tree_model_get (model, &iter, 0, &family, -1);
5447     if (g_object_get_data (G_OBJECT (selection), "block"))
5448     {
5449         gtk_entry_set_text (GTK_ENTRY (entry), family);
5450         return;
5451     }
5453     gtk_entry_set_text (GTK_ENTRY (entry), family);
5455     SPStyle *query =
5456         sp_style_new (SP_ACTIVE_DOCUMENT);
5458     int result_fontspec =
5459         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5461     //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5463     SPCSSAttr *css = sp_repr_css_attr_new ();
5466     // First try to get the font spec from the stored value
5467     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
5469     if (fontSpec.empty()) {
5470         // Construct a new font specification if it does not yet exist
5471         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5472         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5473         fontFromStyle->Unref();
5474     }
5476     if (!fontSpec.empty()) {
5477         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
5478         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
5479             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
5480             if (font) {
5481                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5483                 // Set all the these just in case they were altered when finding the best
5484                 // match for the new family and old style...
5486                 gchar c[256];
5488                 font->Family(c, 256);
5489                 sp_repr_css_set_property (css, "font-family", c);
5491                 font->Attribute( "weight", c, 256);
5492                 sp_repr_css_set_property (css, "font-weight", c);
5494                 font->Attribute("style", c, 256);
5495                 sp_repr_css_set_property (css, "font-style", c);
5497                 font->Attribute("stretch", c, 256);
5498                 sp_repr_css_set_property (css, "font-stretch", c);
5500                 font->Attribute("variant", c, 256);
5501                 sp_repr_css_set_property (css, "font-variant", c);
5503                 font->Unref();
5504             }
5505         }
5506     }
5508     // If querying returned nothing, set the default style of the tool (for new texts)
5509     if (result_fontspec == QUERY_STYLE_NOTHING)
5510     {
5511         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5512         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
5513     }
5514     else
5515     {
5516         sp_desktop_set_style (desktop, css, true, true);
5517     }
5519     sp_style_unref(query);
5521     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5522                                    _("Text: Change font family"));
5523     sp_repr_css_attr_unref (css);
5524     g_free(family);
5525     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5527     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5530 /* This is where execution comes when the contents of the font family box have been completed
5531    by the press of the return key */
5532 void
5533 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
5534                                        GObject      *tbl)
5536     const char *family = gtk_entry_get_text (entry);   // Fetch the requested font family
5538 // Try to match that to a known font. If not, then leave current font alone and remain focused on text box
5539     try {
5540         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
5541         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5542         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5543         gtk_tree_selection_select_path (selection, path.gobj());
5544         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5545         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5546     } catch (...) {
5547         if (family && strlen (family))
5548         {
5549             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5550         }
5551     }
5554 void
5555 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
5556                                    gpointer          data)
5558     if (g_object_get_data (G_OBJECT (button), "block")) return;
5559     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
5560     int prop = GPOINTER_TO_INT(data);
5562     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5563     SPCSSAttr *css = sp_repr_css_attr_new ();
5565     switch (prop)
5566     {
5567         case 0:
5568         {
5569             sp_repr_css_set_property (css, "text-anchor", "start");
5570             sp_repr_css_set_property (css, "text-align", "start");
5571             break;
5572         }
5573         case 1:
5574         {
5575             sp_repr_css_set_property (css, "text-anchor", "middle");
5576             sp_repr_css_set_property (css, "text-align", "center");
5577             break;
5578         }
5580         case 2:
5581         {
5582             sp_repr_css_set_property (css, "text-anchor", "end");
5583             sp_repr_css_set_property (css, "text-align", "end");
5584             break;
5585         }
5587         case 3:
5588         {
5589             sp_repr_css_set_property (css, "text-anchor", "start");
5590             sp_repr_css_set_property (css, "text-align", "justify");
5591             break;
5592         }
5593     }
5595     SPStyle *query =
5596         sp_style_new (SP_ACTIVE_DOCUMENT);
5597     int result_numbers =
5598         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5600     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5601     if (result_numbers == QUERY_STYLE_NOTHING)
5602     {
5603         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5604     }
5606     sp_style_unref(query);
5608     sp_desktop_set_style (desktop, css, true, true);
5609     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5610                                    _("Text: Change alignment"));
5611     sp_repr_css_attr_unref (css);
5613     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5616 void
5617 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
5618                                gpointer          data)
5620     if (g_object_get_data (G_OBJECT (button), "block")) return;
5622     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5623     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5624     int          prop       = GPOINTER_TO_INT(data);
5625     bool         active     = gtk_toggle_button_get_active (button);
5627     SPStyle *query =
5628         sp_style_new (SP_ACTIVE_DOCUMENT);
5630     int result_fontspec =
5631         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5633     //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5634     //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5635     //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5637     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
5638     Glib::ustring newFontSpec = "";
5640     if (fontSpec.empty()) {
5641         // Construct a new font specification if it does not yet exist
5642         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5643         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5644         fontFromStyle->Unref();
5645     }
5647     switch (prop)
5648     {
5649         case 0:
5650         {
5651             if (!fontSpec.empty()) {
5652                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
5653             }
5654             if (fontSpec != newFontSpec) {
5655                 // Don't even set the bold if the font didn't exist on the system
5656                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
5657             }
5658             break;
5659         }
5661         case 1:
5662         {
5663             if (!fontSpec.empty()) {
5664                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
5665             }
5666             if (fontSpec != newFontSpec) {
5667                 // Don't even set the italic if the font didn't exist on the system
5668                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
5669             }
5670             break;
5671         }
5672     }
5674     if (!newFontSpec.empty()) {
5675         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5676     }
5678     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5679     if (result_fontspec == QUERY_STYLE_NOTHING)
5680     {
5681         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5682     }
5684     sp_style_unref(query);
5686     sp_desktop_set_style (desktop, css, true, true);
5687     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5688                                    _("Text: Change font style"));
5689     sp_repr_css_attr_unref (css);
5691     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5694 void
5695 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
5696                                      gpointer         data)
5698     if (g_object_get_data (G_OBJECT (button), "block")) {
5699         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5700         return;
5701     }
5703     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5704     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5705     int          prop       = GPOINTER_TO_INT(data);
5707     switch (prop)
5708     {
5709         case 0:
5710         {
5711             sp_repr_css_set_property (css, "writing-mode", "lr");
5712             break;
5713         }
5715         case 1:
5716         {
5717             sp_repr_css_set_property (css, "writing-mode", "tb");
5718             break;
5719         }
5720     }
5722     SPStyle *query =
5723         sp_style_new (SP_ACTIVE_DOCUMENT);
5724     int result_numbers =
5725         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5727     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5728     if (result_numbers == QUERY_STYLE_NOTHING)
5729     {
5730         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5731     }
5733     sp_desktop_set_style (desktop, css, true, true);
5734     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5735                                    _("Text: Change orientation"));
5736     sp_repr_css_attr_unref (css);
5738     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5741 gboolean
5742 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5744     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5745     if (!desktop) return FALSE;
5747     switch (get_group0_keyval (event)) {
5748         case GDK_Escape: // defocus
5749             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5750             sp_text_toolbox_selection_changed (NULL, tbl); // update
5751             return TRUE; // I consumed the event
5752             break;
5753     }
5754     return FALSE;
5757 gboolean
5758 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5760     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5761     if (!desktop) return FALSE;
5763     switch (get_group0_keyval (event)) {
5764         case GDK_KP_Enter:
5765         case GDK_Return:
5766         case GDK_Escape: // defocus
5767             gtk_widget_hide (w);
5768             popdown_visible = false;
5769             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5770             return TRUE; // I consumed the event
5771             break;
5772         case GDK_w:
5773         case GDK_W:
5774             if (event->state & GDK_CONTROL_MASK) {
5775                 gtk_widget_hide (w);
5776                 popdown_visible = false;
5777                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5778                 return TRUE; // I consumed the event
5779             }
5780             break;
5781     }
5782     return FALSE;
5786 void
5787 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
5788                                GObject     *tbl)
5790     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5792     if (g_object_get_data (tbl, "size-block")) return;
5794     // If this is not from selecting a size in the list (in which case get_active will give the
5795     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5796     // process this event. This fixes GTK's stupid insistence on sending an activate change every
5797     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5798     if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5799         return;
5801     gdouble value = -1;
5802     {
5803         gchar *endptr;
5804         gchar *const text = gtk_combo_box_get_active_text(cbox);
5805         if (text) {
5806             value = g_strtod(text, &endptr);
5807             if (endptr == text) {  // Conversion failed, non-numeric input.
5808                 value = -1;
5809             }
5810             g_free(text);
5811         }
5812     }
5813     if (value <= 0) {
5814         return; // could not parse value
5815     }
5817     SPCSSAttr *css = sp_repr_css_attr_new ();
5818     Inkscape::CSSOStringStream osfs;
5819     osfs << value;
5820     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5822     SPStyle *query =
5823         sp_style_new (SP_ACTIVE_DOCUMENT);
5824     int result_numbers =
5825         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5827     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5828     if (result_numbers == QUERY_STYLE_NOTHING)
5829     {
5830         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5831     }
5833     sp_style_unref(query);
5835     sp_desktop_set_style (desktop, css, true, true);
5836     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5837                                    _("Text: Change font size"));
5838     sp_repr_css_attr_unref (css);
5840     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5843 gboolean
5844 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5846     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5847     if (!desktop) return FALSE;
5849     if (!g_object_get_data (tbl, "esc-pressed")) {
5850         g_object_set_data (tbl, "enter-pressed", gpointer(1));
5851         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5852         sp_text_toolbox_size_changed (cbox, tbl);
5853         g_object_set_data (tbl, "enter-pressed", gpointer(0));
5854     }
5855     return FALSE; // I consumed the event
5859 gboolean
5860 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5862     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5863     if (!desktop) return FALSE;
5865     switch (get_group0_keyval (event)) {
5866         case GDK_Escape: // defocus
5867             g_object_set_data (tbl, "esc-pressed", gpointer(1));
5868             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5869             g_object_set_data (tbl, "esc-pressed", gpointer(0));
5870             return TRUE; // I consumed the event
5871             break;
5872         case GDK_Return: // defocus
5873         case GDK_KP_Enter:
5874             g_object_set_data (tbl, "enter-pressed", gpointer(1));
5875             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5876             sp_text_toolbox_size_changed (cbox, tbl);
5877             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5878             g_object_set_data (tbl, "enter-pressed", gpointer(0));
5879             return TRUE; // I consumed the event
5880             break;
5881     }
5882     return FALSE;
5885 void
5886 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
5887                                          GObject            *tbl)
5889     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
5890     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5891     int x, y;
5893     if (!popdown_visible)
5894     {
5895         gdk_window_get_origin (widget->window, &x, &y);
5896         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
5897         gtk_widget_show_all (popdown);
5898         //sp_transientize (popdown);
5900         gdk_pointer_grab (widget->window, TRUE,
5901                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
5902                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
5903                                         GDK_POINTER_MOTION_MASK),
5904                           NULL, NULL, GDK_CURRENT_TIME);
5906         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
5908         popdown_visible = true;
5909     }
5910     else
5911     {
5912         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5913         gdk_pointer_ungrab (GDK_CURRENT_TIME);
5914         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5915         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5916         gtk_widget_hide (popdown);
5917         popdown_visible = false;
5918     }
5921 gboolean
5922 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
5923                                  GdkEventFocus    */*event*/,
5924                                  GObject          */*tbl*/)
5926     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
5927     return FALSE;
5930 gboolean
5931 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
5932                                    GdkEventFocus    */*event*/,
5933                                    GObject          */*tbl*/)
5935     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5937     if (popdown_hasfocus) {
5938         gtk_widget_hide (popdown);
5939         popdown_hasfocus = false;
5940         popdown_visible = false;
5941         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5942         return TRUE;
5943     }
5944     return FALSE;
5947 gboolean
5948 sp_text_toolbox_popdown_focus_in (GtkWidget        */*popdown*/,
5949                                    GdkEventFocus    */*event*/,
5950                                    GObject          */*tbl*/)
5952     popdown_hasfocus = true;
5953     return TRUE;
5957 void
5958 cell_data_func  (GtkTreeViewColumn */*column*/,
5959                  GtkCellRenderer   *cell,
5960                  GtkTreeModel      *tree_model,
5961                  GtkTreeIter       *iter,
5962                  gpointer           /*data*/)
5964     gchar *family;
5965     gtk_tree_model_get(tree_model, iter, 0, &family, -1);
5966     gchar *const family_escaped = g_markup_escape_text(family, -1);
5968     static char const *const sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
5969     gchar *const sample_escaped = g_markup_escape_text(sample, -1);
5971     std::stringstream markup;
5972     markup << family_escaped << "  <span foreground='darkgray' font_family='"
5973            << family_escaped << "'>" << sample_escaped << "</span>";
5974     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
5976     g_free(family);
5977     g_free(family_escaped);
5978     g_free(sample_escaped);
5981 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
5982     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
5983     if (completion) {
5984         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
5985         g_object_unref (completion);
5986     }
5989 GtkWidget*
5990 sp_text_toolbox_new (SPDesktop *desktop)
5992     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
5993     GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
5995     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
5996     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
5998     GtkTooltips *tt = gtk_tooltips_new();
5999     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
6001     ////////////Family
6002     //Window
6003     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
6004     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
6006     //Entry
6007     GtkWidget           *entry = gtk_entry_new ();
6008     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
6009     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
6010     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
6011     gtk_entry_completion_set_text_column (completion, 0);
6012     gtk_entry_completion_set_minimum_key_length (completion, 1);
6013     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
6014     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
6015     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
6016     gtk_toolbar_append_widget( tbl, entry, "", "" );
6017     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
6019     //Button
6020     GtkWidget   *button = gtk_button_new ();
6021     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
6022     gtk_toolbar_append_widget( tbl, button, "", "");
6024     //Popdown
6025     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
6026     GtkWidget           *treeview = gtk_tree_view_new ();
6028     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
6029     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
6030     gtk_tree_view_column_pack_start (column, cell, FALSE);
6031     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
6032     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
6033     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
6035     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
6036     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
6037     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
6039     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
6041     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
6042     gtk_container_add (GTK_CONTAINER (sw), treeview);
6044     gtk_container_add (GTK_CONTAINER (window), sw);
6045     gtk_widget_set_size_request (window, 300, 450);
6047     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
6048     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
6049     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
6051     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
6053     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
6054     g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
6055     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
6057     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
6058     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6060     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
6061     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
6062     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
6063     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
6064     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
6066     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
6067     GtkWidget *box = gtk_event_box_new ();
6068     gtk_container_add (GTK_CONTAINER (box), image);
6069     gtk_toolbar_append_widget( tbl, box, "", "");
6070     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
6071     GtkTooltips *tooltips = gtk_tooltips_new ();
6072     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
6073     gtk_widget_hide (GTK_WIDGET (box));
6074     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
6076     ////////////Size
6077     gchar const *const sizes[] = {
6078         "4", "6", "8", "9", "10", "11", "12", "13", "14",
6079         "16", "18", "20", "22", "24", "28",
6080         "32", "36", "40", "48", "56", "64", "72", "144"
6081     };
6083     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
6084     for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
6085         gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
6086     }
6087     gtk_widget_set_size_request (cbox, 80, -1);
6088     gtk_toolbar_append_widget( tbl, cbox, "", "");
6089     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
6090     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
6091     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
6092     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
6094     ////////////Text anchor
6095     GtkWidget *group   = gtk_radio_button_new (NULL);
6096     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
6097     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
6099     // left
6100     GtkWidget *rbutton = group;
6101     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6102     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
6103     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6105     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6106     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
6107     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
6108     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
6110     // center
6111     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6112     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6113     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
6114     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6116     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6117     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
6118     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
6119     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
6121     // right
6122     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6123     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6124     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
6125     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6127     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6128     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
6129     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
6130     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
6132     // fill
6133     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6134     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6135     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
6136     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6138     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6139     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
6140     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
6141     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
6143     gtk_toolbar_append_widget( tbl, row, "", "");
6145     //spacer
6146     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6148     ////////////Text style
6149     row = gtk_hbox_new (FALSE, 4);
6151     // bold
6152     rbutton = gtk_toggle_button_new ();
6153     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6154     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
6155     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6156     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
6158     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6159     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
6160     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
6162     // italic
6163     rbutton = gtk_toggle_button_new ();
6164     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6165     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
6166     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6167     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
6169     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6170     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
6171     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
6173     gtk_toolbar_append_widget( tbl, row, "", "");
6175     //spacer
6176     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6178     ////////////Text orientation
6179     group   = gtk_radio_button_new (NULL);
6180     row     = gtk_hbox_new (FALSE, 4);
6181     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
6183     // horizontal
6184     rbutton = group;
6185     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6186     gtk_container_add           (GTK_CONTAINER (rbutton),
6187                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
6188     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6189     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
6191     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6192     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
6193     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
6195     // vertical
6196     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6197     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6198     gtk_container_add           (GTK_CONTAINER (rbutton),
6199                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
6200     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6201     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
6203     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6204     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
6205     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
6206     gtk_toolbar_append_widget( tbl, row, "", "" );
6209     //watch selection
6210     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
6212     sigc::connection *c_selection_changed =
6213         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
6214                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
6215     pool->add_connection ("selection-changed", c_selection_changed);
6217     sigc::connection *c_selection_modified =
6218         new sigc::connection (sp_desktop_selection (desktop)->connectModified
6219                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
6220     pool->add_connection ("selection-modified", c_selection_modified);
6222     sigc::connection *c_subselection_changed =
6223         new sigc::connection (desktop->connectToolSubselectionChanged
6224                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
6225     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
6227     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
6230     gtk_widget_show_all( GTK_WIDGET(tbl) );
6232     return GTK_WIDGET(tbl);
6233 } // end of sp_text_toolbox_new()
6235 }//<unnamed> namespace
6238 //#########################
6239 //##      Connector      ##
6240 //#########################
6242 static void sp_connector_path_set_avoid(void)
6244     cc_selection_set_avoid(true);
6248 static void sp_connector_path_set_ignore(void)
6250     cc_selection_set_avoid(false);
6255 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
6257     // quit if run by the _changed callbacks
6258     if (g_object_get_data( tbl, "freeze" )) {
6259         return;
6260     }
6262     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
6263     SPDocument *doc = sp_desktop_document(desktop);
6265     if (!sp_document_get_undo_sensitive(doc))
6266     {
6267         return;
6268     }
6270     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6272     if ( repr->attribute("inkscape:connector-spacing") ) {
6273         gdouble priorValue = gtk_adjustment_get_value(adj);
6274         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
6275         if ( priorValue == gtk_adjustment_get_value(adj) ) {
6276             return;
6277         }
6278     } else if ( adj->value == defaultConnSpacing ) {
6279         return;
6280     }
6282     // in turn, prevent callbacks from responding
6283     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6285     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
6286     SP_OBJECT(desktop->namedview)->updateRepr();
6288     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
6289     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
6290         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
6291         NR::Matrix m = NR::identity();
6292         avoid_item_move(&m, item);
6293     }
6295     if (items) {
6296         g_slist_free(items);
6297     }
6299     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
6300             _("Change connector spacing"));
6302     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6304     spinbutton_defocus(GTK_OBJECT(tbl));
6307 static void sp_connector_graph_layout(void)
6309     if (!SP_ACTIVE_DESKTOP) return;
6311     // hack for clones, see comment in align-and-distribute.cpp
6312     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
6313     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
6315     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
6317     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
6319     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
6322 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6324     if ( gtk_toggle_action_get_active( act ) ) {
6325         prefs_set_string_attribute("tools.connector", "directedlayout",
6326                 "true");
6327     } else {
6328         prefs_set_string_attribute("tools.connector", "directedlayout",
6329                 "false");
6330     }
6333 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6335     if ( gtk_toggle_action_get_active( act ) ) {
6336         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
6337                 "true");
6338     } else {
6339         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
6340                 "false");
6341     }
6345 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
6347     prefs_set_double_attribute("tools.connector", "length", adj->value);
6348     spinbutton_defocus(GTK_OBJECT(tbl));
6351 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
6352                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
6353                                             bool /*is_interactive*/, gpointer data)
6355     GtkWidget *tbl = GTK_WIDGET(data);
6357     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6358         return;
6359     }
6360     if (strcmp(name, "inkscape:connector-spacing") != 0) {
6361         return;
6362     }
6364     GtkAdjustment *adj = (GtkAdjustment*)
6365             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
6366     gdouble spacing = defaultConnSpacing;
6367     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
6369     gtk_adjustment_set_value(adj, spacing);
6373 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
6374     NULL, /* child_added */
6375     NULL, /* child_removed */
6376     connector_tb_event_attr_changed,
6377     NULL, /* content_changed */
6378     NULL  /* order_changed */
6379 };
6382 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
6384     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
6386     {
6387         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
6388                                           _("Avoid"),
6389                                           _("Make connectors avoid selected objects"),
6390                                           "connector_avoid",
6391                                           secondarySize );
6392         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
6393         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6394     }
6396     {
6397         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
6398                                           _("Ignore"),
6399                                           _("Make connectors ignore selected objects"),
6400                                           "connector_ignore",
6401                                           secondarySize );
6402         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
6403         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6404     }
6406     EgeAdjustmentAction* eact = 0;
6408     // Spacing spinbox
6409     eact = create_adjustment_action( "ConnectorSpacingAction",
6410                                      _("Connector Spacing"), _("Spacing:"),
6411                                      _("The amount of space left around objects by auto-routing connectors"),
6412                                      "tools.connector", "spacing", defaultConnSpacing,
6413                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
6414                                      0, 100, 1.0, 10.0,
6415                                      0, 0, 0,
6416                                      connector_spacing_changed, 1, 0 );
6417     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6419     // Graph (connector network) layout
6420     {
6421         InkAction* inky = ink_action_new( "ConnectorGraphAction",
6422                                           _("Graph"),
6423                                           _("Nicely arrange selected connector network"),
6424                                           "graph_layout",
6425                                           secondarySize );
6426         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
6427         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6428     }
6430     // Default connector length spinbox
6431     eact = create_adjustment_action( "ConnectorLengthAction",
6432                                      _("Connector Length"), _("Length:"),
6433                                      _("Ideal length for connectors when layout is applied"),
6434                                      "tools.connector", "length", 100,
6435                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
6436                                      10, 1000, 10.0, 100.0,
6437                                      0, 0, 0,
6438                                      connector_length_changed, 1, 0 );
6439     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6442     // Directed edges toggle button
6443     {
6444         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
6445                                                       _("Downwards"),
6446                                                       _("Make connectors with end-markers (arrows) point downwards"),
6447                                                       "directed_graph",
6448                                                       Inkscape::ICON_SIZE_DECORATION );
6449         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6451         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
6452         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6453                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6455         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
6456     }
6458     // Avoid overlaps toggle button
6459     {
6460         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
6461                                                       _("Remove overlaps"),
6462                                                       _("Do not allow overlapping shapes"),
6463                                                       "remove_overlaps",
6464                                                       Inkscape::ICON_SIZE_DECORATION );
6465         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6467         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
6468         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6469                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6471         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
6472     }
6474     // Code to watch for changes to the connector-spacing attribute in
6475     // the XML.
6476     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6477     g_assert(repr != NULL);
6479     purge_repr_listener( holder, holder );
6481     if (repr) {
6482         g_object_set_data( holder, "repr", repr );
6483         Inkscape::GC::anchor(repr);
6484         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
6485         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
6486     }
6487 } // end of sp_connector_toolbox_prep()
6490 //#########################
6491 //##     Paintbucket     ##
6492 //#########################
6494 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
6496     gint channels = ege_select_one_action_get_active( act );
6497     flood_channels_set_channels( channels );
6500 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
6502     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
6505 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
6507     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
6510 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
6512     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
6513     SPUnit const *unit = tracker->getActiveUnit();
6515     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
6517     prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
6520 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
6522     // FIXME: make defaults settable via Inkscape Options
6523     struct KeyValue {
6524         char const *key;
6525         double value;
6526     } const key_values[] = {
6527         {"threshold", 15},
6528         {"offset", 0.0}
6529     };
6531     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
6532         KeyValue const &kv = key_values[i];
6533         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
6534         if ( adj ) {
6535             gtk_adjustment_set_value(adj, kv.value);
6536         }
6537     }
6539     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
6540     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
6541     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
6542     ege_select_one_action_set_active( autogap_action, 0 );
6545 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6547     EgeAdjustmentAction* eact = 0;
6549     {
6550         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6552         GList* items = 0;
6553         gint count = 0;
6554         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
6555         {
6556             GtkTreeIter iter;
6557             gtk_list_store_append( model, &iter );
6558             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6559             count++;
6560         }
6561         g_list_free( items );
6562         items = 0;
6563         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
6564         g_object_set( act1, "short_label", _("Fill by:"), NULL );
6565         ege_select_one_action_set_appearance( act1, "compact" );
6566         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
6567         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
6568         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
6569         g_object_set_data( holder, "channels_action", act1 );
6570     }
6572     // Spacing spinbox
6573     {
6574         eact = create_adjustment_action(
6575             "ThresholdAction",
6576             _("Fill Threshold"), _("Threshold:"),
6577             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
6578             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
6579             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
6580             0, 0, 0,
6581             paintbucket_threshold_changed, 1, 0 );
6583         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6584         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6585     }
6587     // Create the units menu.
6588     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
6589     const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
6590     if (stored_unit)
6591         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
6592     g_object_set_data( holder, "tracker", tracker );
6593     {
6594         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
6595         gtk_action_group_add_action( mainActions, act );
6596     }
6598     // Offset spinbox
6599     {
6600         eact = create_adjustment_action(
6601             "OffsetAction",
6602             _("Grow/shrink by"), _("Grow/shrink by:"),
6603             _("The amount to grow (positive) or shrink (negative) the created fill path"),
6604             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
6605             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
6606             0, 0, 0,
6607             paintbucket_offset_changed, 1, 2);
6608         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
6610         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6611     }
6613     /* Auto Gap */
6614     {
6615         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6617         GList* items = 0;
6618         gint count = 0;
6619         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
6620         {
6621             GtkTreeIter iter;
6622             gtk_list_store_append( model, &iter );
6623             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6624             count++;
6625         }
6626         g_list_free( items );
6627         items = 0;
6628         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
6629         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
6630         ege_select_one_action_set_appearance( act2, "compact" );
6631         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
6632         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
6633         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
6634         g_object_set_data( holder, "autogap_action", act2 );
6635     }
6637     /* Reset */
6638     {
6639         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
6640                                           _("Defaults"),
6641                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
6642                                           GTK_STOCK_CLEAR );
6643         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
6644         gtk_action_group_add_action( mainActions, act );
6645         gtk_action_set_sensitive( act, TRUE );
6646     }
6650 /*
6651   Local Variables:
6652   mode:c++
6653   c-file-style:"stroustrup"
6654   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
6655   indent-tabs-mode:nil
6656   fill-column:99
6657   End:
6658 */
6659 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :