Code

Remove debugging messages
[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         "  </toolbar>"
454         "  <toolbar name='DropperToolbar'>"
455         "    <toolitem action='DropperOpacityAction' />"
456         "    <toolitem action='DropperPickAlphaAction' />"
457         "    <toolitem action='DropperSetAlphaAction' />"
458         "  </toolbar>"
460         "  <toolbar name='ConnectorToolbar'>"
461         "    <toolitem action='ConnectorAvoidAction' />"
462         "    <toolitem action='ConnectorIgnoreAction' />"
463         "    <toolitem action='ConnectorSpacingAction' />"
464         "    <toolitem action='ConnectorGraphAction' />"
465         "    <toolitem action='ConnectorLengthAction' />"
466         "    <toolitem action='ConnectorDirectedAction' />"
467         "    <toolitem action='ConnectorOverlapAction' />"
468         "  </toolbar>"
470         "</ui>"
473 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
475 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
477 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
478 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
480 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
481 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
483 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
484 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
487 GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick( GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
488                                                               Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
489                                                               Inkscape::UI::View::View *view, GtkTooltips *tt);
491 class VerbAction : public Gtk::Action {
492 public:
493     static Glib::RefPtr<VerbAction> create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
495     virtual ~VerbAction();
496     virtual void set_active(bool active = true);
498 protected:
499     virtual Gtk::Widget* create_menu_item_vfunc();
500     virtual Gtk::Widget* create_tool_item_vfunc();
502     virtual void connect_proxy_vfunc(Gtk::Widget* proxy);
503     virtual void disconnect_proxy_vfunc(Gtk::Widget* proxy);
505     virtual void on_activate();
507 private:
508     Inkscape::Verb* verb;
509     Inkscape::Verb* verb2;
510     Inkscape::UI::View::View *view;
511     GtkTooltips *tooltips;
512     bool active;
514     VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
515 };
518 Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips)
520     Glib::RefPtr<VerbAction> result;
521     SPAction *action = verb->get_action(view);
522     if ( action ) {
523         //SPAction* action2 = verb2 ? verb2->get_action(view) : 0;
524         result = Glib::RefPtr<VerbAction>(new VerbAction(verb, verb2, view, tooltips));
525     }
527     return result;
530 VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips) :
531     Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(verb->get_image()), Glib::ustring(_(verb->get_name())), Glib::ustring(_(verb->get_tip()))),
532     verb(verb),
533     verb2(verb2),
534     view(view),
535     tooltips(tooltips),
536     active(false)
540 VerbAction::~VerbAction()
544 Gtk::Widget* VerbAction::create_menu_item_vfunc()
546 // First call in to get the icon rendered if present in SVG
547     Gtk::Widget *widget = sp_icon_get_icon( property_stock_id().get_value().get_string(), Inkscape::ICON_SIZE_MENU );
548     delete widget;
549     widget = 0;
551     Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
552 //     g_message("create_menu_item_vfunc() = %p  for '%s'", widg, verb->get_id());
553     return widg;
556 Gtk::Widget* VerbAction::create_tool_item_vfunc()
558 //     Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
559     Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
560     GtkWidget* toolbox = 0;
561     GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
562                                                                           SP_BUTTON_TYPE_TOGGLE,
563                                                                           verb,
564                                                                           verb2,
565                                                                           view,
566                                                                           tooltips );
567     if ( active ) {
568         sp_button_toggle_set_down( SP_BUTTON(button), active);
569     }
570     gtk_widget_show_all( button );
571     Gtk::Widget* wrapped = Glib::wrap(button);
572     Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
573     holder->add(*wrapped);
575 //     g_message("create_tool_item_vfunc() = %p  for '%s'", holder, verb->get_id());
576     return holder;
579 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
581 //     g_message("connect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
582     Gtk::Action::connect_proxy_vfunc(proxy);
585 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
587 //     g_message("disconnect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
588     Gtk::Action::disconnect_proxy_vfunc(proxy);
591 void VerbAction::set_active(bool active)
593     this->active = active;
594     Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
595     for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
596         Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
597         if (ti) {
598             // *should* have one child that is the SPButton
599             Gtk::Widget* child = ti->get_child();
600             if ( child && SP_IS_BUTTON(child->gobj()) ) {
601                 SPButton* button = SP_BUTTON(child->gobj());
602                 sp_button_toggle_set_down( button, active );
603             }
604         }
605     }
608 void VerbAction::on_activate()
610     if ( verb ) {
611         SPAction *action = verb->get_action(view);
612         if ( action ) {
613             sp_action_perform(action, 0);
614         }
615     }
618 /* Global text entry widgets necessary for update */
619 /* GtkWidget *dropper_rgb_entry,
620           *dropper_opacity_entry ; */
621 // should be made a private member once this is converted to class
623 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
624     connection->disconnect();
625     delete connection;
628 static void purge_repr_listener( GObject* obj, GObject* tbl )
630     (void)obj;
631     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
632     if (oldrepr) { // remove old listener
633         sp_repr_remove_listener_by_data(oldrepr, tbl);
634         Inkscape::GC::release(oldrepr);
635         oldrepr = 0;
636         g_object_set_data( tbl, "repr", NULL );
637     }
640 GtkWidget *
641 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
642                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
643                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
645     SPAction *action = verb->get_action(view);
646     if (!action) return NULL;
648     SPAction *doubleclick_action;
649     if (doubleclick_verb)
650         doubleclick_action = doubleclick_verb->get_action(view);
651     else
652         doubleclick_action = NULL;
654     /* fixme: Handle sensitive/unsensitive */
655     /* fixme: Implement sp_button_new_from_action */
656     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
657     gtk_widget_show(b);
660     unsigned int shortcut = sp_shortcut_get_primary(verb);
661     if (shortcut) {
662         gchar key[256];
663         sp_ui_shortcut_string(shortcut, key);
664         gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
665         if ( t ) {
666             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
667         }
668         g_free(tip);
669     } else {
670         if ( t ) {
671             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
672         }
673     }
675     return b;
679 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
681     SPAction* targetAction = SP_ACTION(user_data);
682     if ( targetAction ) {
683         sp_action_perform( targetAction, NULL );
684     }
687 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
689     if ( data ) {
690         GtkAction* act = GTK_ACTION(data);
691         gtk_action_set_sensitive( act, sensitive );
692     }
695 static SPActionEventVector action_event_vector = {
696     {NULL},
697     NULL,
698     NULL,
699     sp_action_action_set_sensitive,
700     NULL,
701     NULL
702 };
704 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
706     GtkAction* act = 0;
708     SPAction* targetAction = verb->get_action(view);
709     InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size  );
710     act = GTK_ACTION(inky);
711     gtk_action_set_sensitive( act, targetAction->sensitive );
713     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
715     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
716     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
718     return act;
721 Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
723     Inkscape::UI::View::View *view = desktop;
724     gint verbsToUse[] = {
725         // disabled until we have icons for them:
726         //find
727         //SP_VERB_EDIT_TILE,
728         //SP_VERB_EDIT_UNTILE,
729         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
730         SP_VERB_DIALOG_DISPLAY,
731         SP_VERB_DIALOG_FILL_STROKE,
732         SP_VERB_DIALOG_NAMEDVIEW,
733         SP_VERB_DIALOG_TEXT,
734         SP_VERB_DIALOG_XML_EDITOR,
735         SP_VERB_EDIT_CLONE,
736         SP_VERB_EDIT_COPY,
737         SP_VERB_EDIT_CUT,
738         SP_VERB_EDIT_DUPLICATE,
739         SP_VERB_EDIT_PASTE,
740         SP_VERB_EDIT_REDO,
741         SP_VERB_EDIT_UNDO,
742         SP_VERB_EDIT_UNLINK_CLONE,
743         SP_VERB_FILE_EXPORT,
744         SP_VERB_FILE_IMPORT,
745         SP_VERB_FILE_NEW,
746         SP_VERB_FILE_OPEN,
747         SP_VERB_FILE_PRINT,
748         SP_VERB_FILE_SAVE,
749         SP_VERB_OBJECT_TO_CURVE,
750         SP_VERB_SELECTION_GROUP,
751         SP_VERB_SELECTION_OUTLINE,
752         SP_VERB_SELECTION_UNGROUP,
753         SP_VERB_ZOOM_1_1,
754         SP_VERB_ZOOM_1_2,
755         SP_VERB_ZOOM_2_1,
756         SP_VERB_ZOOM_DRAWING,
757         SP_VERB_ZOOM_IN,
758         SP_VERB_ZOOM_NEXT,
759         SP_VERB_ZOOM_OUT,
760         SP_VERB_ZOOM_PAGE,
761         SP_VERB_ZOOM_PAGE_WIDTH,
762         SP_VERB_ZOOM_PREV,
763         SP_VERB_ZOOM_SELECTION,
764     };
766     Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
768     static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
769     Glib::RefPtr<Gtk::ActionGroup> mainActions;
770     if ( groups.find(desktop) != groups.end() ) {
771         mainActions = groups[desktop];
772     }
774     if ( !mainActions ) {
775         mainActions = Gtk::ActionGroup::create("main");
776         groups[desktop] = mainActions;
777     }
779     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
780         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
781         if ( verb ) {
782             if (!mainActions->get_action(verb->get_id())) {
783                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
784                 mainActions->add(Glib::wrap(act));
785             }
786         }
787     }
789     if ( !mainActions->get_action("ToolZoom") ) {
790         GtkTooltips *tt = gtk_tooltips_new();
791         for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
792             Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
793             if ( va ) {
794                 mainActions->add(va);
795                 if ( i == 0 ) {
796                     va->set_active(true);
797                 }
798             }
799         }
800     }
803     return mainActions;
807 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
809     gtk_widget_set_size_request( widget,
810                                  widget->allocation.width,
811                                  widget->allocation.height );
814 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
816     gtk_widget_set_size_request( widget, -1, -1 );
821 GtkWidget *
822 sp_tool_toolbox_new()
824     GtkTooltips *tt = gtk_tooltips_new();
825     GtkWidget* tb = gtk_toolbar_new();
826     gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
827     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
829     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
830     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
832     gtk_widget_set_sensitive(tb, FALSE);
834     GtkWidget *hb = gtk_handle_box_new();
835     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
836     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
837     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
839     gtk_container_add(GTK_CONTAINER(hb), tb);
840     gtk_widget_show(GTK_WIDGET(tb));
842     sigc::connection* conn = new sigc::connection;
843     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
845     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
846     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
848     return hb;
851 GtkWidget *
852 sp_aux_toolbox_new()
854     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
856     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
858     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
860     gtk_widget_set_sensitive(tb, FALSE);
862     GtkWidget *hb = gtk_handle_box_new();
863     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
864     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
865     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
867     gtk_container_add(GTK_CONTAINER(hb), tb);
868     gtk_widget_show(GTK_WIDGET(tb));
870     sigc::connection* conn = new sigc::connection;
871     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
873     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
874     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
876     return hb;
879 //####################################
880 //# Commands Bar
881 //####################################
883 GtkWidget *
884 sp_commands_toolbox_new()
886     GtkWidget *tb = gtk_toolbar_new();
888     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
889     gtk_widget_set_sensitive(tb, FALSE);
891     GtkWidget *hb = gtk_handle_box_new();
892     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
893     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
894     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
896     gtk_container_add(GTK_CONTAINER(hb), tb);
897     gtk_widget_show(GTK_WIDGET(tb));
899     sigc::connection* conn = new sigc::connection;
900     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
902     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
903     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
905     return hb;
909 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
910                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
911                                                        gchar const *path, gchar const *data, gdouble def,
912                                                        GtkWidget *focusTarget,
913                                                        GtkWidget *us,
914                                                        GObject *dataKludge,
915                                                        gboolean altx, gchar const *altx_mark,
916                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
917                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
918                                                        void (*callback)(GtkAdjustment *, GObject *),
919                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
921     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
922                                                              lower, upper, step, page, page ) );
923     if (us) {
924         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
925     }
927     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
929     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
930     if ( shortLabel ) {
931         g_object_set( act, "short_label", shortLabel, NULL );
932     }
934     if ( (descrCount > 0) && descrLabels && descrValues ) {
935         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
936     }
938     if ( focusTarget ) {
939         ege_adjustment_action_set_focuswidget( act, focusTarget );
940     }
942     if ( altx && altx_mark ) {
943         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
944     }
946     if ( dataKludge ) {
947         g_object_set_data( dataKludge, data, adj );
948     }
950     // Using a cast just to make sure we pass in the right kind of function pointer
951     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
953     return act;
957 //####################################
958 //# node editing callbacks
959 //####################################
961 /**
962  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
963  */
964 static ShapeEditor *get_current_shape_editor()
966     if (!SP_ACTIVE_DESKTOP) {
967         return NULL;
968     }
970     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
972     if (!SP_IS_NODE_CONTEXT(event_context)) {
973         return NULL;
974     }
976     return SP_NODE_CONTEXT(event_context)->shape_editor;
980 void
981 sp_node_path_edit_add(void)
983     ShapeEditor *shape_editor = get_current_shape_editor();
984     if (shape_editor) shape_editor->add_node();
987 void
988 sp_node_path_edit_delete(void)
990     ShapeEditor *shape_editor = get_current_shape_editor();
991     if (shape_editor) shape_editor->delete_nodes_preserving_shape();
994 void
995 sp_node_path_edit_delete_segment(void)
997     ShapeEditor *shape_editor = get_current_shape_editor();
998     if (shape_editor) shape_editor->delete_segment();
1001 void
1002 sp_node_path_edit_break(void)
1004     ShapeEditor *shape_editor = get_current_shape_editor();
1005     if (shape_editor) shape_editor->break_at_nodes();
1008 void
1009 sp_node_path_edit_join(void)
1011     ShapeEditor *shape_editor = get_current_shape_editor();
1012     if (shape_editor) shape_editor->join_nodes();
1015 void
1016 sp_node_path_edit_join_segment(void)
1018     ShapeEditor *shape_editor = get_current_shape_editor();
1019     if (shape_editor) shape_editor->join_segments();
1022 void
1023 sp_node_path_edit_toline(void)
1025     ShapeEditor *shape_editor = get_current_shape_editor();
1026     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1029 void
1030 sp_node_path_edit_tocurve(void)
1032     ShapeEditor *shape_editor = get_current_shape_editor();
1033     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1036 void
1037 sp_node_path_edit_cusp(void)
1039     ShapeEditor *shape_editor = get_current_shape_editor();
1040     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1043 void
1044 sp_node_path_edit_smooth(void)
1046     ShapeEditor *shape_editor = get_current_shape_editor();
1047     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1050 void
1051 sp_node_path_edit_symmetrical(void)
1053     ShapeEditor *shape_editor = get_current_shape_editor();
1054     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1057 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1058     bool show = gtk_toggle_action_get_active( act );
1059     prefs_set_int_attribute ("tools.nodes", "show_handles",  show ? 1 : 0);
1060     ShapeEditor *shape_editor = get_current_shape_editor();
1061     if (shape_editor) shape_editor->show_handles(show);
1064 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1065     bool show = gtk_toggle_action_get_active( act );
1066     prefs_set_int_attribute ("tools.nodes", "show_helperpath",  show ? 1 : 0);
1067     ShapeEditor *shape_editor = get_current_shape_editor();
1068     if (shape_editor) shape_editor->show_helperpath(show);
1071 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1072     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1075 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1076     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1079 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1080     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1083 /* is called when the node selection is modified */
1084 static void
1085 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1087     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1088     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1089     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1090     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1092     // quit if run by the attr_changed listener
1093     if (g_object_get_data( tbl, "freeze" )) {
1094         return;
1095     }
1097     // in turn, prevent listener from responding
1098     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1100     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1101     SPUnit const *unit = tracker->getActiveUnit();
1103     ShapeEditor *shape_editor = get_current_shape_editor();
1104     if (shape_editor && shape_editor->has_nodepath()) {
1105         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1106         int n_selected = 0;
1107         if (nodepath) {
1108             n_selected = nodepath->numSelected();
1109         }
1111         if (n_selected == 0) {
1112             gtk_action_set_sensitive(xact, FALSE);
1113             gtk_action_set_sensitive(yact, FALSE);
1114         } else {
1115             gtk_action_set_sensitive(xact, TRUE);
1116             gtk_action_set_sensitive(yact, TRUE);
1117             Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1118             Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1120             if (n_selected == 1) {
1121                 Geom::Point sel_node = nodepath->singleSelectedCoords();
1122                 if (oldx != sel_node[Geom::X] || oldy != sel_node[Geom::Y]) {
1123                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[Geom::X], *unit));
1124                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[Geom::Y], *unit));
1125                 }
1126             } else {
1127                 boost::optional<Geom::Coord> x = sp_node_selected_common_coord(nodepath, Geom::X);
1128                 boost::optional<Geom::Coord> y = sp_node_selected_common_coord(nodepath, Geom::Y);
1129                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1130                     /* Note: Currently x and y will always have a value, even if the coordinates of the
1131                        selected nodes don't coincide (in this case we use the coordinates of the center
1132                        of the bounding box). So the entries are never set to zero. */
1133                     // FIXME: Maybe we should clear the entry if several nodes are selected
1134                     //        instead of providing a kind of average value
1135                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1136                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1137                 }
1138             }
1139         }
1140     } else {
1141         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1142         gtk_action_set_sensitive(xact, FALSE);
1143         gtk_action_set_sensitive(yact, FALSE);
1144     }
1146     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1149 static void
1150 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1152     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1154     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1155     SPUnit const *unit = tracker->getActiveUnit();
1157     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1158         prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
1159     }
1161     // quit if run by the attr_changed listener
1162     if (g_object_get_data( tbl, "freeze" )) {
1163         return;
1164     }
1166     // in turn, prevent listener from responding
1167     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1169     ShapeEditor *shape_editor = get_current_shape_editor();
1170     if (shape_editor && shape_editor->has_nodepath()) {
1171         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1172         if (!strcmp(value_name, "x")) {
1173             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::X);
1174         }
1175         if (!strcmp(value_name, "y")) {
1176             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::Y);
1177         }
1178     }
1180     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1183 static void
1184 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1186     sp_node_path_value_changed(adj, tbl, "x");
1189 static void
1190 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1192     sp_node_path_value_changed(adj, tbl, "y");
1195 void
1196 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1198     {
1199     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1200     SPItem *item = selection->singleItem();
1201     if (item && SP_IS_LPE_ITEM(item)) {
1202        if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1203            gtk_action_set_sensitive(w, TRUE);
1204        } else {
1205            gtk_action_set_sensitive(w, FALSE);
1206        }
1207     } else {
1208        gtk_action_set_sensitive(w, FALSE);
1209     }
1210     }
1212     {
1213     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1214     SPItem *item = selection->singleItem();
1215     if (item && item->clip_ref && item->clip_ref->getObject()) {
1216        gtk_action_set_sensitive(w, TRUE);
1217     } else {
1218        gtk_action_set_sensitive(w, FALSE);
1219     }
1220     }
1222     {
1223     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1224     SPItem *item = selection->singleItem();
1225     if (item && item->mask_ref && item->mask_ref->getObject()) {
1226        gtk_action_set_sensitive(w, TRUE);
1227     } else {
1228        gtk_action_set_sensitive(w, FALSE);
1229     }
1230     }
1233 void
1234 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1236     sp_node_toolbox_sel_changed (selection, tbl);
1241 //################################
1242 //##    Node Editing Toolbox    ##
1243 //################################
1245 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1247     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1248     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1249     g_object_set_data( holder, "tracker", tracker );
1251     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
1253     {
1254         InkAction* inky = ink_action_new( "NodeInsertAction",
1255                                           _("Insert node"),
1256                                           _("Insert new nodes into selected segments"),
1257                                           "node_insert",
1258                                           secondarySize );
1259         g_object_set( inky, "short_label", _("Insert"), NULL );
1260         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1261         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1262     }
1264     {
1265         InkAction* inky = ink_action_new( "NodeDeleteAction",
1266                                           _("Delete node"),
1267                                           _("Delete selected nodes"),
1268                                           "node_delete",
1269                                           secondarySize );
1270         g_object_set( inky, "short_label", _("Delete"), NULL );
1271         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1272         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1273     }
1275     {
1276         InkAction* inky = ink_action_new( "NodeJoinAction",
1277                                           _("Join endnodes"),
1278                                           _("Join selected endnodes"),
1279                                           "node_join",
1280                                           secondarySize );
1281         g_object_set( inky, "short_label", _("Join"), NULL );
1282         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1283         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1284     }
1286     {
1287         InkAction* inky = ink_action_new( "NodeBreakAction",
1288                                           _("Break nodes"),
1289                                           _("Break path at selected nodes"),
1290                                           "node_break",
1291                                           secondarySize );
1292         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1293         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1294     }
1297     {
1298         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1299                                           _("Join with segment"),
1300                                           _("Join selected endnodes with a new segment"),
1301                                           "node_join_segment",
1302                                           secondarySize );
1303         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1304         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1305     }
1307     {
1308         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1309                                           _("Delete segment"),
1310                                           _("Delete segment between two non-endpoint nodes"),
1311                                           "node_delete_segment",
1312                                           secondarySize );
1313         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1314         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1315     }
1317     {
1318         InkAction* inky = ink_action_new( "NodeCuspAction",
1319                                           _("Node Cusp"),
1320                                           _("Make selected nodes corner"),
1321                                           "node_cusp",
1322                                           secondarySize );
1323         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1324         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1325     }
1327     {
1328         InkAction* inky = ink_action_new( "NodeSmoothAction",
1329                                           _("Node Smooth"),
1330                                           _("Make selected nodes smooth"),
1331                                           "node_smooth",
1332                                           secondarySize );
1333         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1334         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1335     }
1337     {
1338         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1339                                           _("Node Symmetric"),
1340                                           _("Make selected nodes symmetric"),
1341                                           "node_symmetric",
1342                                           secondarySize );
1343         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1344         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1345     }
1347     {
1348         InkAction* inky = ink_action_new( "NodeLineAction",
1349                                           _("Node Line"),
1350                                           _("Make selected segments lines"),
1351                                           "node_line",
1352                                           secondarySize );
1353         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1354         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1355     }
1357     {
1358         InkAction* inky = ink_action_new( "NodeCurveAction",
1359                                           _("Node Curve"),
1360                                           _("Make selected segments curves"),
1361                                           "node_curve",
1362                                           secondarySize );
1363         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1364         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1365     }
1367     {
1368         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1369                                                       _("Show Handles"),
1370                                                       _("Show the Bezier handles of selected nodes"),
1371                                                       "nodes_show_handles",
1372                                                       Inkscape::ICON_SIZE_DECORATION );
1373         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1374         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1375         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1376     }
1378     {
1379         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1380                                                       _("Show Outline"),
1381                                                       _("Show the outline of the path"),
1382                                                       "nodes_show_helperpath",
1383                                                       Inkscape::ICON_SIZE_DECORATION );
1384         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1385         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1386         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_helperpath", 0 ) );
1387     }
1389     {
1390         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1391                                           _("Next path effect parameter"),
1392                                           _("Show next path effect parameter for editing"),
1393                                           "edit_next_parameter",
1394                                           Inkscape::ICON_SIZE_DECORATION );
1395         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1396         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1397         g_object_set_data( holder, "nodes_lpeedit", inky);
1398     }
1400     {
1401         InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1402                                           _("Edit clipping path"),
1403                                           _("Edit the clipping path of the object"),
1404                                           "nodeedit-clippath",
1405                                           Inkscape::ICON_SIZE_DECORATION );
1406         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1407         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1408         g_object_set_data( holder, "nodes_clippathedit", inky);
1409     }
1411     {
1412         InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1413                                           _("Edit mask path"),
1414                                           _("Edit the mask of the object"),
1415                                           "nodeedit-mask",
1416                                           Inkscape::ICON_SIZE_DECORATION );
1417         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1418         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1419         g_object_set_data( holder, "nodes_maskedit", inky);
1420     }
1422     /* X coord of selected node(s) */
1423     {
1424         EgeAdjustmentAction* eact = 0;
1425         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1426         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1427         eact = create_adjustment_action( "NodeXAction",
1428                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1429                                          "tools.nodes", "Xcoord", 0,
1430                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1431                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1432                                          labels, values, G_N_ELEMENTS(labels),
1433                                          sp_node_path_x_value_changed );
1434         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1435         g_object_set_data( holder, "nodes_x_action", eact );
1436         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1437         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1438     }
1440     /* Y coord of selected node(s) */
1441     {
1442         EgeAdjustmentAction* eact = 0;
1443         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1444         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1445         eact = create_adjustment_action( "NodeYAction",
1446                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1447                                          "tools.nodes", "Ycoord", 0,
1448                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1449                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1450                                          labels, values, G_N_ELEMENTS(labels),
1451                                          sp_node_path_y_value_changed );
1452         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1453         g_object_set_data( holder, "nodes_y_action", eact );
1454         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1455         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1456     }
1458     // add the units menu
1459     {
1460         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1461         gtk_action_group_add_action( mainActions, act );
1462     }
1465     sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1467     //watch selection
1468     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1470     sigc::connection *c_selection_changed =
1471         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1472                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1473     pool->add_connection ("selection-changed", c_selection_changed);
1475     sigc::connection *c_selection_modified =
1476         new sigc::connection (sp_desktop_selection (desktop)->connectModified
1477                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1478     pool->add_connection ("selection-modified", c_selection_modified);
1480     sigc::connection *c_subselection_changed =
1481         new sigc::connection (desktop->connectToolSubselectionChanged
1482                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1483     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1485     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1487     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1488 } // end of sp_node_toolbox_prep()
1491 //########################
1492 //##    Zoom Toolbox    ##
1493 //########################
1495 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1497     // no custom GtkAction setup needed
1498 } // end of sp_zoom_toolbox_prep()
1500 void
1501 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1503     toolbox_set_desktop(toolbox,
1504                         desktop,
1505                         setup_tool_toolbox,
1506                         update_tool_toolbox,
1507                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1508                                                                          "event_context_connection")));
1512 void
1513 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1515     toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1516                         desktop,
1517                         setup_aux_toolbox,
1518                         update_aux_toolbox,
1519                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1520                                                                          "event_context_connection")));
1523 void
1524 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1526     toolbox_set_desktop(toolbox,
1527                         desktop,
1528                         setup_commands_toolbox,
1529                         update_commands_toolbox,
1530                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1531                                                                          "event_context_connection")));
1534 static void
1535 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1537     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1538     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1540     if (old_desktop) {
1541         GList *children, *iter;
1543         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1544         for ( iter = children ; iter ; iter = iter->next ) {
1545             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1546         }
1547         g_list_free(children);
1548     }
1550     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1552     if (desktop) {
1553         gtk_widget_set_sensitive(toolbox, TRUE);
1554         setup_func(toolbox, desktop);
1555         update_func(desktop, desktop->event_context, toolbox);
1556         *conn = desktop->connectEventContextChanged
1557             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1558     } else {
1559         gtk_widget_set_sensitive(toolbox, FALSE);
1560     }
1562 } // end of toolbox_set_desktop()
1565 static void
1566 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1568     gchar const * descr =
1569         "<ui>"
1570         "  <toolbar name='ToolToolbar'>"
1571         "    <toolitem action='ToolSelector' />"
1572         "    <toolitem action='ToolNode' />"
1573         "    <toolitem action='ToolTweak' />"
1574         "    <toolitem action='ToolZoom' />"
1575         "    <toolitem action='ToolRect' />"
1576         "    <toolitem action='Tool3DBox' />"
1577         "    <toolitem action='ToolArc' />"
1578         "    <toolitem action='ToolStar' />"
1579         "    <toolitem action='ToolSpiral' />"
1580         "    <toolitem action='ToolPencil' />"
1581         "    <toolitem action='ToolPen' />"
1582         "    <toolitem action='ToolCalligraphic' />"
1583         "    <toolitem action='ToolEraser' />"
1584         "    <toolitem action='ToolLPETool' />"
1585         "    <toolitem action='ToolPaintBucket' />"
1586         "    <toolitem action='ToolText' />"
1587         "    <toolitem action='ToolConnector' />"
1588         "    <toolitem action='ToolGradient' />"
1589         "    <toolitem action='ToolDropper' />"
1590         "  </toolbar>"
1591         "</ui>";
1592     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1593     GtkUIManager* mgr = gtk_ui_manager_new();
1594     GError* errVal = 0;
1596     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1597     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1599     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" );
1600     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1601         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1602     }
1603     Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1604     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1606     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
1607     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1609     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1611     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1612     if ( child ) {
1613         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1614     }
1616     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1617 //     Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1621 static void
1622 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1624     gchar const *const tname = ( eventcontext
1625                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1626                                  : NULL );
1627     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1629     for (int i = 0 ; tools[i].type_name ; i++ ) {
1630         Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1631         if ( act ) {
1632             bool setActive = tname && !strcmp(tname, tools[i].type_name);
1633             Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1634             if ( verbAct ) {
1635                 verbAct->set_active(setActive);
1636             }
1637         }
1638     }
1641 static void
1642 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1644     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1645     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1646     GtkUIManager* mgr = gtk_ui_manager_new();
1647     GError* errVal = 0;
1648     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1649     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1651     std::map<std::string, GtkWidget*> dataHolders;
1653     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1654         if ( aux_toolboxes[i].prep_func ) {
1655             // converted to GtkActions and UIManager
1657             GtkWidget* kludge = gtk_toolbar_new();
1658             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1659             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1660             dataHolders[aux_toolboxes[i].type_name] = kludge;
1661             aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1662         } else {
1664             GtkWidget *sub_toolbox = 0;
1665             if (aux_toolboxes[i].create_func == NULL)
1666                 sub_toolbox = sp_empty_toolbox_new(desktop);
1667             else {
1668                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1669             }
1671             gtk_size_group_add_widget( grouper, sub_toolbox );
1673             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1674             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1676         }
1677     }
1679     // Second pass to create toolbars *after* all GtkActions are created
1680     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1681         if ( aux_toolboxes[i].prep_func ) {
1682             // converted to GtkActions and UIManager
1684             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1686             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1687             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1689             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1690             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1691             g_free( tmp );
1692             tmp = 0;
1694             Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1695             if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1696                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1697             }
1698             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1701             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1703             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1704                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1705                 swatch->setDesktop( desktop );
1706                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1707                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1708                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1709                 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 );
1710             }
1712             gtk_widget_show_all( holder );
1713             sp_set_font_size_smaller( holder );
1715             gtk_size_group_add_widget( grouper, holder );
1717             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1718             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1719         }
1720     }
1722     g_object_unref( G_OBJECT(grouper) );
1725 static void
1726 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1728     gchar const *tname = ( eventcontext
1729                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1730                            : NULL );
1731     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1732         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1733         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1734             gtk_widget_show_all(sub_toolbox);
1735             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1736         } else {
1737             gtk_widget_hide(sub_toolbox);
1738         }
1739     }
1742 static void
1743 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1745     gchar const * descr =
1746         "<ui>"
1747         "  <toolbar name='CommandsToolbar'>"
1748         "    <toolitem action='FileNew' />"
1749         "    <toolitem action='FileOpen' />"
1750         "    <toolitem action='FileSave' />"
1751         "    <toolitem action='FilePrint' />"
1752         "    <separator />"
1753         "    <toolitem action='FileImport' />"
1754         "    <toolitem action='FileExport' />"
1755         "    <separator />"
1756         "    <toolitem action='EditUndo' />"
1757         "    <toolitem action='EditRedo' />"
1758         "    <separator />"
1759         "    <toolitem action='EditCopy' />"
1760         "    <toolitem action='EditCut' />"
1761         "    <toolitem action='EditPaste' />"
1762         "    <separator />"
1763         "    <toolitem action='ZoomSelection' />"
1764         "    <toolitem action='ZoomDrawing' />"
1765         "    <toolitem action='ZoomPage' />"
1766         "    <separator />"
1767         "    <toolitem action='EditDuplicate' />"
1768         "    <toolitem action='EditClone' />"
1769         "    <toolitem action='EditUnlinkClone' />"
1770         "    <separator />"
1771         "    <toolitem action='SelectionGroup' />"
1772         "    <toolitem action='SelectionUnGroup' />"
1773         "    <separator />"
1774         "    <toolitem action='DialogFillStroke' />"
1775         "    <toolitem action='DialogText' />"
1776         "    <toolitem action='DialogXMLEditor' />"
1777         "    <toolitem action='DialogAlignDistribute' />"
1778         "    <separator />"
1779         "    <toolitem action='DialogPreferences' />"
1780         "    <toolitem action='DialogDocumentProperties' />"
1781         "  </toolbar>"
1782         "</ui>";
1783     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1786     GtkUIManager* mgr = gtk_ui_manager_new();
1787     GError* errVal = 0;
1789     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1790     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1792     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1793     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1794         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1795     }
1797     Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1798     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1800     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1801     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1804     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1806     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1807     if ( child ) {
1808         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1809     }
1811     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1814 static void
1815 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1819 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1821     gtk_widget_show(toolbox_toplevel);
1822     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1824     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1825     if (!shown_toolbox) {
1826         return;
1827     }
1828     gtk_widget_show(toolbox);
1830     gtk_widget_show_all(shown_toolbox);
1833 static GtkWidget *
1834 sp_empty_toolbox_new(SPDesktop *desktop)
1836     GtkWidget *tbl = gtk_toolbar_new();
1837     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1838     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1840     gtk_widget_show_all(tbl);
1841     sp_set_font_size_smaller (tbl);
1843     return tbl;
1846 #define MODE_LABEL_WIDTH 70
1848 //########################
1849 //##       Star         ##
1850 //########################
1852 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1854     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1856     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1857         // do not remember prefs if this call is initiated by an undo change, because undoing object
1858         // creation sets bogus values to its attributes before it is deleted
1859         prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1860     }
1862     // quit if run by the attr_changed listener
1863     if (g_object_get_data( dataKludge, "freeze" )) {
1864         return;
1865     }
1867     // in turn, prevent listener from responding
1868     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1870     bool modmade = false;
1872     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1873     GSList const *items = selection->itemList();
1874     for (; items != NULL; items = items->next) {
1875         if (SP_IS_STAR((SPItem *) items->data)) {
1876             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1877             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1878             sp_repr_set_svg_double(repr, "sodipodi:arg2",
1879                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1880                                     + M_PI / (gint)adj->value));
1881             SP_OBJECT((SPItem *) items->data)->updateRepr();
1882             modmade = true;
1883         }
1884     }
1885     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1886                                    _("Star: Change number of corners"));
1888     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1891 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1893     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1895     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1896         prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1897     }
1899     // quit if run by the attr_changed listener
1900     if (g_object_get_data( dataKludge, "freeze" )) {
1901         return;
1902     }
1904     // in turn, prevent listener from responding
1905     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1907     bool modmade = false;
1908     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1909     GSList const *items = selection->itemList();
1910     for (; items != NULL; items = items->next) {
1911         if (SP_IS_STAR((SPItem *) items->data)) {
1912             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1914             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1915             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1916             if (r2 < r1) {
1917                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1918             } else {
1919                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1920             }
1922             SP_OBJECT((SPItem *) items->data)->updateRepr();
1923             modmade = true;
1924         }
1925     }
1927     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1928                                    _("Star: Change spoke ratio"));
1930     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1933 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1935     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1936     bool flat = ege_select_one_action_get_active( act ) == 0;
1938     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1939         prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1940                                     flat ? "true" : "false" );
1941     }
1943     // quit if run by the attr_changed listener
1944     if (g_object_get_data( dataKludge, "freeze" )) {
1945         return;
1946     }
1948     // in turn, prevent listener from responding
1949     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1951     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1952     GSList const *items = selection->itemList();
1953     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1954     bool modmade = false;
1956     if ( prop_action ) {
1957         gtk_action_set_sensitive( prop_action, !flat );
1958     }
1960     for (; items != NULL; items = items->next) {
1961         if (SP_IS_STAR((SPItem *) items->data)) {
1962             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1963             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1964             SP_OBJECT((SPItem *) items->data)->updateRepr();
1965             modmade = true;
1966         }
1967     }
1969     if (modmade) {
1970         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1971                          flat ? _("Make polygon") : _("Make star"));
1972     }
1974     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1977 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1979     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1981     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1982         prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1983     }
1985     // quit if run by the attr_changed listener
1986     if (g_object_get_data( dataKludge, "freeze" )) {
1987         return;
1988     }
1990     // in turn, prevent listener from responding
1991     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1993     bool modmade = false;
1995     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1996     GSList const *items = selection->itemList();
1997     for (; items != NULL; items = items->next) {
1998         if (SP_IS_STAR((SPItem *) items->data)) {
1999             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2000             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2001             SP_OBJECT(items->data)->updateRepr();
2002             modmade = true;
2003         }
2004     }
2005     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2006                                    _("Star: Change rounding"));
2008     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2011 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2013     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2015     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2016         prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
2017     }
2019     // quit if run by the attr_changed listener
2020     if (g_object_get_data( dataKludge, "freeze" )) {
2021         return;
2022     }
2024     // in turn, prevent listener from responding
2025     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2027     bool modmade = false;
2029     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2030     GSList const *items = selection->itemList();
2031     for (; items != NULL; items = items->next) {
2032         if (SP_IS_STAR((SPItem *) items->data)) {
2033             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2034             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2035             SP_OBJECT(items->data)->updateRepr();
2036             modmade = true;
2037         }
2038     }
2039     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2040                                    _("Star: Change randomization"));
2042     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2046 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2047                                        gchar const */*old_value*/, gchar const */*new_value*/,
2048                                        bool /*is_interactive*/, gpointer data)
2050     GtkWidget *tbl = GTK_WIDGET(data);
2052     // quit if run by the _changed callbacks
2053     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2054         return;
2055     }
2057     // in turn, prevent callbacks from responding
2058     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2060     GtkAdjustment *adj = 0;
2062     gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2063     bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2065     if (!strcmp(name, "inkscape:randomized")) {
2066         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2067         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2068     } else if (!strcmp(name, "inkscape:rounded")) {
2069         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2070         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2071     } else if (!strcmp(name, "inkscape:flatsided")) {
2072         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2073         char const *flatsides = repr->attribute("inkscape:flatsided");
2074         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2075         if ( flatsides && !strcmp(flatsides,"false") ) {
2076             ege_select_one_action_set_active( flat_action, 1 );
2077             gtk_action_set_sensitive( prop_action, TRUE );
2078         } else {
2079             ege_select_one_action_set_active( flat_action, 0 );
2080             gtk_action_set_sensitive( prop_action, FALSE );
2081         }
2082     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2083         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2084         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2085         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2086         if (r2 < r1) {
2087             gtk_adjustment_set_value(adj, r2/r1);
2088         } else {
2089             gtk_adjustment_set_value(adj, r1/r2);
2090         }
2091     } else if (!strcmp(name, "sodipodi:sides")) {
2092         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2093         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2094     }
2096     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2100 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2102     NULL, /* child_added */
2103     NULL, /* child_removed */
2104     star_tb_event_attr_changed,
2105     NULL, /* content_changed */
2106     NULL  /* order_changed */
2107 };
2110 /**
2111  *  \param selection Should not be NULL.
2112  */
2113 static void
2114 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2116     int n_selected = 0;
2117     Inkscape::XML::Node *repr = NULL;
2119     purge_repr_listener( tbl, tbl );
2121     for (GSList const *items = selection->itemList();
2122          items != NULL;
2123          items = items->next)
2124     {
2125         if (SP_IS_STAR((SPItem *) items->data)) {
2126             n_selected++;
2127             repr = SP_OBJECT_REPR((SPItem *) items->data);
2128         }
2129     }
2131     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2133     if (n_selected == 0) {
2134         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2135     } else if (n_selected == 1) {
2136         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2138         if (repr) {
2139             g_object_set_data( tbl, "repr", repr );
2140             Inkscape::GC::anchor(repr);
2141             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2142             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2143         }
2144     } else {
2145         // FIXME: implement averaging of all parameters for multiple selected stars
2146         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2147         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2148     }
2152 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2154     // FIXME: in this and all other _default functions, set some flag telling the value_changed
2155     // callbacks to lump all the changes for all selected objects in one undo step
2157     GtkAdjustment *adj = 0;
2159     // fixme: make settable in prefs!
2160     gint mag = 5;
2161     gdouble prop = 0.5;
2162     gboolean flat = FALSE;
2163     gdouble randomized = 0;
2164     gdouble rounded = 0;
2166     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2167     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2169     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2170     gtk_action_set_sensitive( sb2, !flat );
2172     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2173     gtk_adjustment_set_value(adj, mag);
2174     gtk_adjustment_value_changed(adj);
2176     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2177     gtk_adjustment_set_value(adj, prop);
2178     gtk_adjustment_value_changed(adj);
2180     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2181     gtk_adjustment_set_value(adj, rounded);
2182     gtk_adjustment_value_changed(adj);
2184     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2185     gtk_adjustment_set_value(adj, randomized);
2186     gtk_adjustment_value_changed(adj);
2190 void
2191 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2193     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2194     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2195     GtkWidget *l = gtk_label_new(NULL);
2196     gtk_label_set_markup(GTK_LABEL(l), title);
2197     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2198     if ( GTK_IS_TOOLBAR(tbl) ) {
2199         gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2200     } else {
2201         gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2202     }
2203     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2207 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2209     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2211     {
2212         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2213         ege_output_action_set_use_markup( act, TRUE );
2214         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2215         g_object_set_data( holder, "mode_action", act );
2216     }
2218     {
2219         EgeAdjustmentAction* eact = 0;
2220         gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2221         bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2223         /* Flatsided checkbox */
2224         {
2225             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2227             GtkTreeIter iter;
2228             gtk_list_store_append( model, &iter );
2229             gtk_list_store_set( model, &iter,
2230                                 0, _("Polygon"),
2231                                 1, _("Regular polygon (with one handle) instead of a star"),
2232                                 2, "star_flat",
2233                                 -1 );
2235             gtk_list_store_append( model, &iter );
2236             gtk_list_store_set( model, &iter,
2237                                 0, _("Star"),
2238                                 1, _("Star instead of a regular polygon (with one handle)"),
2239                                 2, "star_angled",
2240                                 -1 );
2242             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2243             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2244             g_object_set_data( holder, "flat_action", act );
2246             ege_select_one_action_set_appearance( act, "full" );
2247             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2248             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2249             ege_select_one_action_set_icon_column( act, 2 );
2250             ege_select_one_action_set_icon_size( act, secondarySize );
2251             ege_select_one_action_set_tooltip_column( act, 1  );
2253             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2254             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2255         }
2257         /* Magnitude */
2258         {
2259         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2260         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2261         eact = create_adjustment_action( "MagnitudeAction",
2262                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2263                                          "tools.shapes.star", "magnitude", 3,
2264                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2265                                          3, 1024, 1, 5,
2266                                          labels, values, G_N_ELEMENTS(labels),
2267                                          sp_stb_magnitude_value_changed,
2268                                          1.0, 0 );
2269         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2270         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2271         }
2273         /* Spoke ratio */
2274         {
2275         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2276         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2277         eact = create_adjustment_action( "SpokeAction",
2278                                          _("Spoke ratio"), _("Spoke ratio:"),
2279                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2280                                          // Base radius is the same for the closest handle.
2281                                          _("Base radius to tip radius ratio"),
2282                                          "tools.shapes.star", "proportion", 0.5,
2283                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2284                                          0.01, 1.0, 0.01, 0.1,
2285                                          labels, values, G_N_ELEMENTS(labels),
2286                                          sp_stb_proportion_value_changed );
2287         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2288         g_object_set_data( holder, "prop_action", eact );
2289         }
2291         if ( !isFlatSided ) {
2292             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2293         } else {
2294             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2295         }
2297         /* Roundedness */
2298         {
2299         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2300         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2301         eact = create_adjustment_action( "RoundednessAction",
2302                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2303                                          "tools.shapes.star", "rounded", 0.0,
2304                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2305                                          -10.0, 10.0, 0.01, 0.1,
2306                                          labels, values, G_N_ELEMENTS(labels),
2307                                          sp_stb_rounded_value_changed );
2308         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2309         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2310         }
2312         /* Randomization */
2313         {
2314         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2315         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2316         eact = create_adjustment_action( "RandomizationAction",
2317                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2318                                          "tools.shapes.star", "randomized", 0.0,
2319                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2320                                          -10.0, 10.0, 0.001, 0.01,
2321                                          labels, values, G_N_ELEMENTS(labels),
2322                                          sp_stb_randomized_value_changed, 0.1, 3 );
2323         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2324         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2325         }
2326     }
2328     {
2329         /* Reset */
2330         {
2331             GtkAction* act = gtk_action_new( "StarResetAction",
2332                                              _("Defaults"),
2333                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2334                                              GTK_STOCK_CLEAR );
2335             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2336             gtk_action_group_add_action( mainActions, act );
2337             gtk_action_set_sensitive( act, TRUE );
2338         }
2339     }
2341     sigc::connection *connection = new sigc::connection(
2342         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2343         );
2344     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2345     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2349 //########################
2350 //##       Rect         ##
2351 //########################
2353 static void sp_rtb_sensitivize( GObject *tbl )
2355     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2356     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2357     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2359     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2360         gtk_action_set_sensitive( not_rounded, FALSE );
2361     } else {
2362         gtk_action_set_sensitive( not_rounded, TRUE );
2363     }
2367 static void
2368 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2369                           void (*setter)(SPRect *, gdouble))
2371     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2373     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2374     SPUnit const *unit = tracker->getActiveUnit();
2376     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2377         prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2378     }
2380     // quit if run by the attr_changed listener
2381     if (g_object_get_data( tbl, "freeze" )) {
2382         return;
2383     }
2385     // in turn, prevent listener from responding
2386     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2388     bool modmade = false;
2389     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2390     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2391         if (SP_IS_RECT(items->data)) {
2392             if (adj->value != 0) {
2393                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2394             } else {
2395                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2396             }
2397             modmade = true;
2398         }
2399     }
2401     sp_rtb_sensitivize( tbl );
2403     if (modmade) {
2404         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2405                                    _("Change rectangle"));
2406     }
2408     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2411 static void
2412 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2414     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2417 static void
2418 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2420     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2423 static void
2424 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2426     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2429 static void
2430 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2432     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2437 static void
2438 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2440     GtkAdjustment *adj = 0;
2442     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2443     gtk_adjustment_set_value(adj, 0.0);
2444     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2445     gtk_adjustment_value_changed(adj);
2447     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2448     gtk_adjustment_set_value(adj, 0.0);
2449     gtk_adjustment_value_changed(adj);
2451     sp_rtb_sensitivize( obj );
2454 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2455                                        gchar const */*old_value*/, gchar const */*new_value*/,
2456                                        bool /*is_interactive*/, gpointer data)
2458     GObject *tbl = G_OBJECT(data);
2460     // quit if run by the _changed callbacks
2461     if (g_object_get_data( tbl, "freeze" )) {
2462         return;
2463     }
2465     // in turn, prevent callbacks from responding
2466     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2468     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2469     SPUnit const *unit = tracker->getActiveUnit();
2471     gpointer item = g_object_get_data( tbl, "item" );
2472     if (item && SP_IS_RECT(item)) {
2473         {
2474             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2475             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2476             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2477         }
2479         {
2480             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2481             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2482             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2483         }
2485         {
2486             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2487             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2488             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2489         }
2491         {
2492             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2493             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2494             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2495         }
2496     }
2498     sp_rtb_sensitivize( tbl );
2500     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2504 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2505     NULL, /* child_added */
2506     NULL, /* child_removed */
2507     rect_tb_event_attr_changed,
2508     NULL, /* content_changed */
2509     NULL  /* order_changed */
2510 };
2512 /**
2513  *  \param selection should not be NULL.
2514  */
2515 static void
2516 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2518     int n_selected = 0;
2519     Inkscape::XML::Node *repr = NULL;
2520     SPItem *item = NULL;
2522     if ( g_object_get_data( tbl, "repr" ) ) {
2523         g_object_set_data( tbl, "item", NULL );
2524     }
2525     purge_repr_listener( tbl, tbl );
2527     for (GSList const *items = selection->itemList();
2528          items != NULL;
2529          items = items->next) {
2530         if (SP_IS_RECT((SPItem *) items->data)) {
2531             n_selected++;
2532             item = (SPItem *) items->data;
2533             repr = SP_OBJECT_REPR(item);
2534         }
2535     }
2537     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2539     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2541     if (n_selected == 0) {
2542         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2544         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2545         gtk_action_set_sensitive(w, FALSE);
2546         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2547         gtk_action_set_sensitive(h, FALSE);
2549     } else if (n_selected == 1) {
2550         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2551         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2553         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2554         gtk_action_set_sensitive(w, TRUE);
2555         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2556         gtk_action_set_sensitive(h, TRUE);
2558         if (repr) {
2559             g_object_set_data( tbl, "repr", repr );
2560             g_object_set_data( tbl, "item", item );
2561             Inkscape::GC::anchor(repr);
2562             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2563             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2564         }
2565     } else {
2566         // FIXME: implement averaging of all parameters for multiple selected
2567         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2568         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2569         sp_rtb_sensitivize( tbl );
2570     }
2574 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2576     EgeAdjustmentAction* eact = 0;
2577     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2579     {
2580         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2581         ege_output_action_set_use_markup( act, TRUE );
2582         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2583         g_object_set_data( holder, "mode_action", act );
2584     }
2586     // rx/ry units menu: create
2587     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2588     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2589     // fixme: add % meaning per cent of the width/height
2590     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2591     g_object_set_data( holder, "tracker", tracker );
2593     /* W */
2594     {
2595         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2596         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2597         eact = create_adjustment_action( "RectWidthAction",
2598                                          _("Width"), _("W:"), _("Width of rectangle"),
2599                                          "tools.shapes.rect", "width", 0,
2600                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2601                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2602                                          labels, values, G_N_ELEMENTS(labels),
2603                                          sp_rtb_width_value_changed );
2604         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2605         g_object_set_data( holder, "width_action", eact );
2606         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2607         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2608     }
2610     /* H */
2611     {
2612         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2613         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2614         eact = create_adjustment_action( "RectHeightAction",
2615                                          _("Height"), _("H:"), _("Height of rectangle"),
2616                                          "tools.shapes.rect", "height", 0,
2617                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2618                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2619                                          labels, values, G_N_ELEMENTS(labels),
2620                                          sp_rtb_height_value_changed );
2621         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2622         g_object_set_data( holder, "height_action", eact );
2623         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2624         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2625     }
2627     /* rx */
2628     {
2629         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2630         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2631         eact = create_adjustment_action( "RadiusXAction",
2632                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2633                                          "tools.shapes.rect", "rx", 0,
2634                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2635                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2636                                          labels, values, G_N_ELEMENTS(labels),
2637                                          sp_rtb_rx_value_changed);
2638         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2639         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2640     }
2642     /* ry */
2643     {
2644         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2645         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2646         eact = create_adjustment_action( "RadiusYAction",
2647                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2648                                          "tools.shapes.rect", "ry", 0,
2649                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2650                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2651                                          labels, values, G_N_ELEMENTS(labels),
2652                                          sp_rtb_ry_value_changed);
2653         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2654         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2655     }
2657     // add the units menu
2658     {
2659         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2660         gtk_action_group_add_action( mainActions, act );
2661     }
2663     /* Reset */
2664     {
2665         InkAction* inky = ink_action_new( "RectResetAction",
2666                                           _("Not rounded"),
2667                                           _("Make corners sharp"),
2668                                           "squared_corner",
2669                                           secondarySize );
2670         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2671         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2672         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2673         g_object_set_data( holder, "not_rounded", inky );
2674     }
2676     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2677     sp_rtb_sensitivize( holder );
2679     sigc::connection *connection = new sigc::connection(
2680         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2681         );
2682     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2683     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2686 //########################
2687 //##       3D Box       ##
2688 //########################
2690 // normalize angle so that it lies in the interval [0,360]
2691 static double box3d_normalize_angle (double a) {
2692     double angle = a + ((int) (a/360.0))*360;
2693     if (angle < 0) {
2694         angle += 360.0;
2695     }
2696     return angle;
2699 static void
2700 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2701                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2702     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2703     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2704     //       are reset).
2705     bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2707     if (is_infinite) {
2708         gtk_toggle_action_set_active(tact, TRUE);
2709         gtk_action_set_sensitive(act, TRUE);
2711         double angle = persp3d_get_infinite_angle(persp, axis);
2712         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2713             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2714         }
2715     } else {
2716         gtk_toggle_action_set_active(tact, FALSE);
2717         gtk_action_set_sensitive(act, FALSE);
2718     }
2721 static void
2722 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2723     if (!persp_repr) {
2724         g_print ("No perspective given to box3d_resync_toolbar().\n");
2725         return;
2726     }
2728     GtkWidget *tbl = GTK_WIDGET(data);
2729     GtkAdjustment *adj = 0;
2730     GtkAction *act = 0;
2731     GtkToggleAction *tact = 0;
2732     Persp3D *persp = persp3d_get_from_repr(persp_repr);
2733     {
2734         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2735         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2736         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2738         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2739     }
2740     {
2741         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2742         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2743         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2745         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2746     }
2747     {
2748         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2749         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2750         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2752         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2753     }
2756 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2757                                                   gchar const */*old_value*/, gchar const */*new_value*/,
2758                                                   bool /*is_interactive*/, gpointer data)
2760     GtkWidget *tbl = GTK_WIDGET(data);
2762     // quit if run by the attr_changed listener
2763     // note: it used to work without the differently called freeze_ attributes (here and in
2764     //       box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2765     if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2766         return;
2767     }
2769     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2770     // sp_document_maybe_done() when the document is undo insensitive)
2771     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2773     // TODO: Only update the appropriate part of the toolbar
2774 //    if (!strcmp(name, "inkscape:vp_z")) {
2775         box3d_resync_toolbar(repr, G_OBJECT(tbl));
2776 //    }
2778     Persp3D *persp = persp3d_get_from_repr(repr);
2779     persp3d_update_box_reprs(persp);
2781     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2784 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2786     NULL, /* child_added */
2787     NULL, /* child_removed */
2788     box3d_persp_tb_event_attr_changed,
2789     NULL, /* content_changed */
2790     NULL  /* order_changed */
2791 };
2793 /**
2794  *  \param selection Should not be NULL.
2795  */
2796 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2797 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2798 static void
2799 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2801     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2802     // disable the angle entry fields for this direction (otherwise entering a value in them should only
2803     // update the perspectives with infinite VPs and leave the other ones untouched).
2805     Inkscape::XML::Node *persp_repr = NULL;
2806     purge_repr_listener(tbl, tbl);
2808     SPItem *item = selection->singleItem();
2809     if (item && SP_IS_BOX3D(item)) {
2810         // FIXME: Also deal with multiple selected boxes
2811         SPBox3D *box = SP_BOX3D(item);
2812         Persp3D *persp = box3d_get_perspective(box);
2813         persp_repr = SP_OBJECT_REPR(persp);
2814         if (persp_repr) {
2815             g_object_set_data(tbl, "repr", persp_repr);
2816             Inkscape::GC::anchor(persp_repr);
2817             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2818             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2819         }
2821         inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2822         prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2824         box3d_resync_toolbar(persp_repr, tbl);
2825     }
2828 static void
2829 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2831     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2832     SPDocument *document = sp_desktop_document(desktop);
2834     // quit if run by the attr_changed listener
2835     // note: it used to work without the differently called freeze_ attributes (here and in
2836     //       box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2837     if (g_object_get_data( dataKludge, "freeze_attr" )) {
2838         return;
2839     }
2841     // in turn, prevent listener from responding
2842     g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2844     //Persp3D *persp = document->current_persp3d;
2845     std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
2846     if (sel_persps.empty()) {
2847         // this can happen when the document is created; we silently ignore it
2848         return;
2849     }
2850     Persp3D *persp = sel_persps.front();
2852     persp->tmat.set_infinite_direction (axis, adj->value);
2853     SP_OBJECT(persp)->updateRepr();
2855     // TODO: use the correct axis here, too
2856     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2858     g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2862 static void
2863 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2865     box3d_angle_value_changed(adj, dataKludge, Proj::X);
2868 static void
2869 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2871     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2874 static void
2875 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2877     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2881 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2883     // TODO: Take all selected perspectives into account
2884     std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
2885     if (sel_persps.empty()) {
2886         // this can happen when the document is created; we silently ignore it
2887         return;
2888     }
2889     Persp3D *persp = sel_persps.front();
2891     bool set_infinite = gtk_toggle_action_get_active(act);
2892     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2895 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2897     box3d_vp_state_changed(act, box3d_angle, Proj::X);
2900 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2902     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2905 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2907     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2910 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2912     EgeAdjustmentAction* eact = 0;
2913     SPDocument *document = sp_desktop_document (desktop);
2914     Persp3D *persp = document->current_persp3d;
2916     EgeAdjustmentAction* box3d_angle_x = 0;
2917     EgeAdjustmentAction* box3d_angle_y = 0;
2918     EgeAdjustmentAction* box3d_angle_z = 0;
2920     /* Angle X */
2921     {
2922         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2923         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2924         eact = create_adjustment_action( "3DBoxAngleXAction",
2925                                          _("Angle in X direction"), _("Angle X:"),
2926                                          // Translators: PL is short for 'perspective line'
2927                                          _("Angle of PLs in X direction"),
2928                                          "tools.shapes.3dbox", "box3d_angle_x", 30,
2929                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2930                                          -360.0, 360.0, 1.0, 10.0,
2931                                          labels, values, G_N_ELEMENTS(labels),
2932                                          box3d_angle_x_value_changed );
2933         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2934         g_object_set_data( holder, "box3d_angle_x_action", eact );
2935         box3d_angle_x = eact;
2936     }
2938     if (!persp3d_VP_is_finite(persp, Proj::X)) {
2939         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2940     } else {
2941         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2942     }
2945     /* VP X state */
2946     {
2947         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2948                                                       // Translators: VP is short for 'vanishing point'
2949                                                       _("State of VP in X direction"),
2950                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2951                                                       "toggle_vp_x",
2952                                                       Inkscape::ICON_SIZE_DECORATION );
2953         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2954         g_object_set_data( holder, "box3d_vp_x_state_action", act );
2955         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2956         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2957         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2958     }
2960     /* Angle Y */
2961     {
2962         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2963         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2964         eact = create_adjustment_action( "3DBoxAngleYAction",
2965                                          _("Angle in Y direction"), _("Angle Y:"),
2966                                          // Translators: PL is short for 'perspective line'
2967                                          _("Angle of PLs in Y direction"),
2968                                          "tools.shapes.3dbox", "box3d_angle_y", 30,
2969                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2970                                          -360.0, 360.0, 1.0, 10.0,
2971                                          labels, values, G_N_ELEMENTS(labels),
2972                                          box3d_angle_y_value_changed );
2973         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2974         g_object_set_data( holder, "box3d_angle_y_action", eact );
2975         box3d_angle_y = eact;
2976     }
2978     if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2979         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2980     } else {
2981         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2982     }
2984     /* VP Y state */
2985     {
2986         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2987                                                       // Translators: VP is short for 'vanishing point'
2988                                                       _("State of VP in Y direction"),
2989                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2990                                                       "toggle_vp_y",
2991                                                       Inkscape::ICON_SIZE_DECORATION );
2992         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2993         g_object_set_data( holder, "box3d_vp_y_state_action", act );
2994         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2995         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2996         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2997     }
2999     /* Angle Z */
3000     {
3001         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3002         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3003         eact = create_adjustment_action( "3DBoxAngleZAction",
3004                                          _("Angle in Z direction"), _("Angle Z:"),
3005                                          // Translators: PL is short for 'perspective line'
3006                                          _("Angle of PLs in Z direction"),
3007                                          "tools.shapes.3dbox", "box3d_angle_z", 30,
3008                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3009                                          -360.0, 360.0, 1.0, 10.0,
3010                                          labels, values, G_N_ELEMENTS(labels),
3011                                          box3d_angle_z_value_changed );
3012         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3013         g_object_set_data( holder, "box3d_angle_z_action", eact );
3014         box3d_angle_z = eact;
3015     }
3017     if (!persp3d_VP_is_finite(persp, Proj::Z)) {
3018         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3019     } else {
3020         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3021     }
3023     /* VP Z state */
3024     {
3025         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3026                                                       // Translators: VP is short for 'vanishing point'
3027                                                       _("State of VP in Z direction"),
3028                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3029                                                       "toggle_vp_z",
3030                                                       Inkscape::ICON_SIZE_DECORATION );
3031         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3032         g_object_set_data( holder, "box3d_vp_z_state_action", act );
3033         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3034         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3035         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3036     }
3038     sigc::connection *connection = new sigc::connection(
3039         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3040        );
3041     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3042     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3045 //########################
3046 //##       Spiral       ##
3047 //########################
3049 static void
3050 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
3052     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3054     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3055         prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
3056     }
3058     // quit if run by the attr_changed listener
3059     if (g_object_get_data( tbl, "freeze" )) {
3060         return;
3061     }
3063     // in turn, prevent listener from responding
3064     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3066     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3068     bool modmade = false;
3069     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3070          items != NULL;
3071          items = items->next)
3072     {
3073         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3074             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3075             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3076             SP_OBJECT((SPItem *) items->data)->updateRepr();
3077             modmade = true;
3078         }
3079     }
3081     g_free(namespaced_name);
3083     if (modmade) {
3084         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3085                                    _("Change spiral"));
3086     }
3088     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3091 static void
3092 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3094     sp_spl_tb_value_changed(adj, tbl, "revolution");
3097 static void
3098 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3100     sp_spl_tb_value_changed(adj, tbl, "expansion");
3103 static void
3104 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3106     sp_spl_tb_value_changed(adj, tbl, "t0");
3109 static void
3110 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3112     GtkWidget *tbl = GTK_WIDGET(obj);
3114     GtkAdjustment *adj;
3116     // fixme: make settable
3117     gdouble rev = 5;
3118     gdouble exp = 1.0;
3119     gdouble t0 = 0.0;
3121     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3122     gtk_adjustment_set_value(adj, rev);
3123     gtk_adjustment_value_changed(adj);
3125     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3126     gtk_adjustment_set_value(adj, exp);
3127     gtk_adjustment_value_changed(adj);
3129     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3130     gtk_adjustment_set_value(adj, t0);
3131     gtk_adjustment_value_changed(adj);
3133     spinbutton_defocus(GTK_OBJECT(tbl));
3137 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3138                                          gchar const */*old_value*/, gchar const */*new_value*/,
3139                                          bool /*is_interactive*/, gpointer data)
3141     GtkWidget *tbl = GTK_WIDGET(data);
3143     // quit if run by the _changed callbacks
3144     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3145         return;
3146     }
3148     // in turn, prevent callbacks from responding
3149     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3151     GtkAdjustment *adj;
3152     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3153     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3155     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3156     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3158     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3159     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3161     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3165 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3166     NULL, /* child_added */
3167     NULL, /* child_removed */
3168     spiral_tb_event_attr_changed,
3169     NULL, /* content_changed */
3170     NULL  /* order_changed */
3171 };
3173 static void
3174 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3176     int n_selected = 0;
3177     Inkscape::XML::Node *repr = NULL;
3179     purge_repr_listener( tbl, tbl );
3181     for (GSList const *items = selection->itemList();
3182          items != NULL;
3183          items = items->next)
3184     {
3185         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3186             n_selected++;
3187             repr = SP_OBJECT_REPR((SPItem *) items->data);
3188         }
3189     }
3191     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3193     if (n_selected == 0) {
3194         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3195     } else if (n_selected == 1) {
3196         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3198         if (repr) {
3199             g_object_set_data( tbl, "repr", repr );
3200             Inkscape::GC::anchor(repr);
3201             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3202             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3203         }
3204     } else {
3205         // FIXME: implement averaging of all parameters for multiple selected
3206         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3207         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3208     }
3212 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3214     EgeAdjustmentAction* eact = 0;
3215     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3217     {
3218         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3219         ege_output_action_set_use_markup( act, TRUE );
3220         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3221         g_object_set_data( holder, "mode_action", act );
3222     }
3224     /* Revolution */
3225     {
3226         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3227         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3228         eact = create_adjustment_action( "SpiralRevolutionAction",
3229                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3230                                          "tools.shapes.spiral", "revolution", 3.0,
3231                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3232                                          0.01, 1024.0, 0.1, 1.0,
3233                                          labels, values, G_N_ELEMENTS(labels),
3234                                          sp_spl_tb_revolution_value_changed, 1, 2);
3235         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3236     }
3238     /* Expansion */
3239     {
3240         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3241         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3242         eact = create_adjustment_action( "SpiralExpansionAction",
3243                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3244                                          "tools.shapes.spiral", "expansion", 1.0,
3245                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3246                                          0.0, 1000.0, 0.01, 1.0,
3247                                          labels, values, G_N_ELEMENTS(labels),
3248                                          sp_spl_tb_expansion_value_changed);
3249         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3250     }
3252     /* T0 */
3253     {
3254         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3255         gdouble values[] = {0, 0.5, 0.9};
3256         eact = create_adjustment_action( "SpiralT0Action",
3257                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3258                                          "tools.shapes.spiral", "t0", 0.0,
3259                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3260                                          0.0, 0.999, 0.01, 1.0,
3261                                          labels, values, G_N_ELEMENTS(labels),
3262                                          sp_spl_tb_t0_value_changed);
3263         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3264     }
3266     /* Reset */
3267     {
3268         InkAction* inky = ink_action_new( "SpiralResetAction",
3269                                           _("Defaults"),
3270                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3271                                           GTK_STOCK_CLEAR,
3272                                           secondarySize );
3273         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3274         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3275     }
3278     sigc::connection *connection = new sigc::connection(
3279         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3280         );
3281     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3282     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3285 //########################
3286 //##     Pen/Pencil     ##
3287 //########################
3289 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3290 static char const *
3291 freehand_tool_name(GObject *dataKludge)
3293     SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3294     return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3295              ? "tools.freehand.pen"
3296              : "tools.freehand.pencil" );
3299 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3301     gint mode = ege_select_one_action_get_active(act);
3303     prefs_set_int_attribute(freehand_tool_name(tbl), "freehand-mode", mode);
3305     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3307     // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3308     // preparatory work here
3309     if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3310         SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3311         sp_pen_context_set_polyline_mode(pc);
3312     }
3315 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3317     /* Freehand mode toggle buttons */
3318     {
3319         guint freehandMode = prefs_get_int_attribute(tool_is_pencil ? "tools.freehand.pencil" : "tools.freehand.pen", "freehand-mode", 0);
3320         Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3322         {
3323             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3325             GtkTreeIter iter;
3326             gtk_list_store_append( model, &iter );
3327             gtk_list_store_set( model, &iter,
3328                                 0, _("Bezier"),
3329                                 1, _("Create regular Bezier path"),
3330                                 2, "bezier_mode",
3331                                 -1 );
3333             gtk_list_store_append( model, &iter );
3334             gtk_list_store_set( model, &iter,
3335                                 0, _("Spiro"),
3336                                 1, _("Create Spiro path"),
3337                                 2, "spiro_splines_mode",
3338                                 -1 );
3340             if (!tool_is_pencil) {
3341                 gtk_list_store_append( model, &iter );
3342                 gtk_list_store_set( model, &iter,
3343                                     0, _("Zigzag"),
3344                                     1, _("Create a sequence of straight line segments"),
3345                                     2, "polylines_mode",
3346                                     -1 );
3348                 gtk_list_store_append( model, &iter );
3349                 gtk_list_store_set( model, &iter,
3350                                     0, _("Paraxial"),
3351                                     1, _("Create a sequence of paraxial line segments"),
3352                                     2, "paraxial_lines_mode",
3353                                     -1 );
3354             }
3356             EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3357                                                                 "FreehandModeActionPencil" :
3358                                                                 "FreehandModeActionPen",
3359                                                                 (_("Mode:")), ("Mode"), NULL, GTK_TREE_MODEL(model) );
3360             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3362             ege_select_one_action_set_appearance( act, "full" );
3363             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3364             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3365             ege_select_one_action_set_icon_column( act, 2 );
3366             ege_select_one_action_set_icon_size( act, secondarySize );
3367             ege_select_one_action_set_tooltip_column( act, 1  );
3369             ege_select_one_action_set_active( act, freehandMode);
3370             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3371         }
3372     }
3375 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3376     gint shape = ege_select_one_action_get_active( act );
3377     prefs_set_int_attribute(freehand_tool_name(dataKludge), "shape", shape);
3380 /**
3381  * \brief Generate the list of freehand advanced shape option entries.
3382  */
3383 GList * freehand_shape_dropdown_items_list() {
3384     GList *glist = NULL;
3386     glist = g_list_append (glist, _("None"));
3387     glist = g_list_append (glist, _("Triangle in"));
3388     glist = g_list_append (glist, _("Triangle out"));
3389     glist = g_list_append (glist, _("Ellipse"));
3390     glist = g_list_append (glist, _("From clipboard"));
3392     return glist;
3395 static void
3396 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3397     /*advanced shape options */
3398     {
3399         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3401         GList* items = 0;
3402         gint count = 0;
3403         for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3404         {
3405             GtkTreeIter iter;
3406             gtk_list_store_append( model, &iter );
3407             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3408             count++;
3409         }
3410         g_list_free( items );
3411         items = 0;
3412         EgeSelectOneAction* act1 = ege_select_one_action_new(
3413             tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3414             _("Shape:"), ("Shape"), NULL, GTK_TREE_MODEL(model));
3415         g_object_set( act1, "short_label", _("Shape:"), NULL );
3416         ege_select_one_action_set_appearance( act1, "compact" );
3417         ege_select_one_action_set_active( act1, prefs_get_int_attribute(tool_is_pencil ? "tools.freehand.pencil" : "tools.freehand.pen", "shape", 0) );
3418         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3419         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3420         g_object_set_data( holder, "shape_action", act1 );
3421     }
3424 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3426     sp_add_freehand_mode_toggle(mainActions, holder, false);
3427     freehand_add_advanced_shape_options(mainActions, holder, false);
3431 static void
3432 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3434     GtkWidget *tbl = GTK_WIDGET(obj);
3436     GtkAdjustment *adj;
3438     // fixme: make settable
3439     gdouble tolerance = 4;
3441     adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3442     gtk_adjustment_set_value(adj, tolerance);
3443     gtk_adjustment_value_changed(adj);
3445     spinbutton_defocus(GTK_OBJECT(tbl));
3448 static void
3449 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3452     // quit if run by the attr_changed listener
3453     if (g_object_get_data( tbl, "freeze" )) {
3454         return;
3455     }
3456     // in turn, prevent listener from responding
3457     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3458     prefs_set_double_attribute("tools.freehand.pencil",
3459                                "tolerance", adj->value);
3460     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3466 static void
3467 sp_pencil_tb_tolerance_value_changed_external(Inkscape::XML::Node */*repr*/,
3468                                               const gchar */*key*/,
3469                                               const gchar */*oldval*/,
3470                                               const gchar */*newval*/,
3471                                               bool /*is_interactive*/,
3472                                               void * data)
3474     GObject* tbl = G_OBJECT(data);
3475     if (g_object_get_data( tbl, "freeze" )) {
3476         return;
3477     }
3479     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3481     GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl,
3482                                                             "tolerance");
3484     double v = prefs_get_double_attribute("tools.freehand.pencil",
3485                                             "tolerance", adj->value);
3486     gtk_adjustment_set_value(adj, v);
3487     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3491 static Inkscape::XML::NodeEventVector pencil_node_events =
3493     NULL,
3494     NULL,
3495     sp_pencil_tb_tolerance_value_changed_external,
3496     NULL,
3497     NULL,
3498 };
3501 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3503     sp_add_freehand_mode_toggle(mainActions, holder, true);
3505     EgeAdjustmentAction* eact = 0;
3507     /* Tolerance */
3508     {
3509         gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
3510         gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
3511         eact = create_adjustment_action( "PencilToleranceAction",
3512                                          _("Smoothing:"), _("Smoothing: "),
3513                  _("How much smoothing (simplifying) is applied to the line"),
3514                                          "tools.freehand.pencil", "tolerance",
3515                                          3.0,
3516                                          GTK_WIDGET(desktop->canvas), NULL,
3517                                          holder, TRUE, "altx-pencil",
3518                                          1, 100.0, 0.5, 0,
3519                                          labels, values, G_N_ELEMENTS(labels),
3520                                          sp_pencil_tb_tolerance_value_changed,
3521                                          1, 2);
3522         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3523         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3525         Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE,
3526                                                       "tools.freehand.pencil");
3527         repr->addListener(&pencil_node_events, G_OBJECT(holder));
3528         g_object_set_data(G_OBJECT(holder), "repr", repr);
3530     }
3532     /* advanced shape options */
3533     freehand_add_advanced_shape_options(mainActions, holder, true);
3535     /* Reset */
3536     {
3537         InkAction* inky = ink_action_new( "PencilResetAction",
3538                                           _("Defaults"),
3539                                           _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3540                                           GTK_STOCK_CLEAR,
3541                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3542         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
3543         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3544     }
3546     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3551 //########################
3552 //##       Tweak        ##
3553 //########################
3555 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3557     prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3560 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3562     prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3565 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3567     prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3570 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3572     int mode = ege_select_one_action_get_active( act );
3573     prefs_set_int_attribute("tools.tweak", "mode", mode);
3575     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3576     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3577     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3578     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3579     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3580     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3581     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3582         if (doh) gtk_action_set_sensitive (doh, TRUE);
3583         if (dos) gtk_action_set_sensitive (dos, TRUE);
3584         if (dol) gtk_action_set_sensitive (dol, TRUE);
3585         if (doo) gtk_action_set_sensitive (doo, TRUE);
3586         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3587         if (fid) gtk_action_set_sensitive (fid, FALSE);
3588     } else {
3589         if (doh) gtk_action_set_sensitive (doh, FALSE);
3590         if (dos) gtk_action_set_sensitive (dos, FALSE);
3591         if (dol) gtk_action_set_sensitive (dol, FALSE);
3592         if (doo) gtk_action_set_sensitive (doo, FALSE);
3593         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3594         if (fid) gtk_action_set_sensitive (fid, TRUE);
3595     }
3598 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3600     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3603 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3604     bool show = gtk_toggle_action_get_active( act );
3605     prefs_set_int_attribute ("tools.tweak", "doh",  show ? 1 : 0);
3607 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3608     bool show = gtk_toggle_action_get_active( act );
3609     prefs_set_int_attribute ("tools.tweak", "dos",  show ? 1 : 0);
3611 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3612     bool show = gtk_toggle_action_get_active( act );
3613     prefs_set_int_attribute ("tools.tweak", "dol",  show ? 1 : 0);
3615 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3616     bool show = gtk_toggle_action_get_active( act );
3617     prefs_set_int_attribute ("tools.tweak", "doo",  show ? 1 : 0);
3620 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3622     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3624     {
3625         /* Width */
3626         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3627         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3628         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3629                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3630                                                               "tools.tweak", "width", 15,
3631                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3632                                                               1, 100, 1.0, 0.0,
3633                                                               labels, values, G_N_ELEMENTS(labels),
3634                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
3635         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3636         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3637         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3638     }
3641     {
3642         /* Force */
3643         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3644         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3645         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3646                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
3647                                                               "tools.tweak", "force", 20,
3648                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3649                                                               1, 100, 1.0, 0.0,
3650                                                               labels, values, G_N_ELEMENTS(labels),
3651                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
3652         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3653         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3654         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3655     }
3657     /* Mode */
3658     {
3659         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3661         GtkTreeIter iter;
3662         gtk_list_store_append( model, &iter );
3663         gtk_list_store_set( model, &iter,
3664                             0, _("Push mode"),
3665                             1, _("Push parts of paths in any direction"),
3666                             2, "tweak_push_mode",
3667                             -1 );
3669         gtk_list_store_append( model, &iter );
3670         gtk_list_store_set( model, &iter,
3671                             0, _("Shrink mode"),
3672                             1, _("Shrink (inset) parts of paths"),
3673                             2, "tweak_shrink_mode",
3674                             -1 );
3676         gtk_list_store_append( model, &iter );
3677         gtk_list_store_set( model, &iter,
3678                             0, _("Grow mode"),
3679                             1, _("Grow (outset) parts of paths"),
3680                             2, "tweak_grow_mode",
3681                             -1 );
3683         gtk_list_store_append( model, &iter );
3684         gtk_list_store_set( model, &iter,
3685                             0, _("Attract mode"),
3686                             1, _("Attract parts of paths towards cursor"),
3687                             2, "tweak_attract_mode",
3688                             -1 );
3690         gtk_list_store_append( model, &iter );
3691         gtk_list_store_set( model, &iter,
3692                             0, _("Repel mode"),
3693                             1, _("Repel parts of paths from cursor"),
3694                             2, "tweak_repel_mode",
3695                             -1 );
3697         gtk_list_store_append( model, &iter );
3698         gtk_list_store_set( model, &iter,
3699                             0, _("Roughen mode"),
3700                             1, _("Roughen parts of paths"),
3701                             2, "tweak_roughen_mode",
3702                             -1 );
3704         gtk_list_store_append( model, &iter );
3705         gtk_list_store_set( model, &iter,
3706                             0, _("Color paint mode"),
3707                             1, _("Paint the tool's color upon selected objects"),
3708                             2, "tweak_colorpaint_mode",
3709                             -1 );
3711         gtk_list_store_append( model, &iter );
3712         gtk_list_store_set( model, &iter,
3713                             0, _("Color jitter mode"),
3714                             1, _("Jitter the colors of selected objects"),
3715                             2, "tweak_colorjitter_mode",
3716                             -1 );
3718         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3719         g_object_set( act, "short_label", _("Mode:"), NULL );
3720         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3721         g_object_set_data( holder, "mode_action", act );
3723         ege_select_one_action_set_appearance( act, "full" );
3724         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3725         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3726         ege_select_one_action_set_icon_column( act, 2 );
3727         ege_select_one_action_set_icon_size( act, secondarySize );
3728         ege_select_one_action_set_tooltip_column( act, 1  );
3730         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3731         ege_select_one_action_set_active( act, mode );
3732         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3734         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3735     }
3737     guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3739     {
3740         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3741         ege_output_action_set_use_markup( act, TRUE );
3742         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3743         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3744             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3745         g_object_set_data( holder, "tweak_channels_label", act);
3746     }
3748     {
3749         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3750                                                       _("Hue"),
3751                                                       _("In color mode, act on objects' hue"),
3752                                                       NULL,
3753                                                       Inkscape::ICON_SIZE_DECORATION );
3754         //TRANSLATORS:  "H" here stands for hue
3755         g_object_set( act, "short_label", _("H"), NULL );
3756         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3757         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3758         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3759         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3760             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3761         g_object_set_data( holder, "tweak_doh", act);
3762     }
3763     {
3764         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3765                                                       _("Saturation"),
3766                                                       _("In color mode, act on objects' saturation"),
3767                                                       NULL,
3768                                                       Inkscape::ICON_SIZE_DECORATION );
3769         //TRANSLATORS: "S" here stands for Saturation
3770         g_object_set( act, "short_label", _("S"), NULL );
3771         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3772         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3773         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3774         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3775             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3776         g_object_set_data( holder, "tweak_dos", act );
3777     }
3778     {
3779         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3780                                                       _("Lightness"),
3781                                                       _("In color mode, act on objects' lightness"),
3782                                                       NULL,
3783                                                       Inkscape::ICON_SIZE_DECORATION );
3784         //TRANSLATORS: "L" here stands for Lightness
3785         g_object_set( act, "short_label", _("L"), NULL );
3786         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3787         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3788         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3789         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3790             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3791         g_object_set_data( holder, "tweak_dol", act );
3792     }
3793     {
3794         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3795                                                       _("Opacity"),
3796                                                       _("In color mode, act on objects' opacity"),
3797                                                       NULL,
3798                                                       Inkscape::ICON_SIZE_DECORATION );
3799         //TRANSLATORS: "O" here stands for Opacity
3800         g_object_set( act, "short_label", _("O"), NULL );
3801         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3802         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3803         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3804         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3805             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3806         g_object_set_data( holder, "tweak_doo", act );
3807     }
3809     {   /* Fidelity */
3810         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3811         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3812         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3813                                                               _("Fidelity"), _("Fidelity:"),
3814                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3815                                                               "tools.tweak", "fidelity", 50,
3816                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3817                                                               1, 100, 1.0, 10.0,
3818                                                               labels, values, G_N_ELEMENTS(labels),
3819                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
3820         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3821         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3822         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3823             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3824         g_object_set_data( holder, "tweak_fidelity", eact );
3825     }
3828     /* Use Pressure button */
3829     {
3830         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3831                                                       _("Pressure"),
3832                                                       _("Use the pressure of the input device to alter the force of tweak action"),
3833                                                       "use_pressure",
3834                                                       Inkscape::ICON_SIZE_DECORATION );
3835         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3836         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3837         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3838     }
3843 //########################
3844 //##     Calligraphy    ##
3845 //########################
3846 static void update_presets_list (GObject *tbl)
3848     if (g_object_get_data(tbl, "presets_blocked"))
3849         return;
3851     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3852     if (!sel) {
3853         ege_select_one_action_set_active(sel, 0);
3854         return;
3855     }
3857     int total_prefs = pref_path_number_of_children("tools.calligraphic.preset");
3859     for (int i = 1; i <= total_prefs; i++) {
3860         gchar *preset_path = get_pref_nth_child("tools.calligraphic.preset", i);
3861         Inkscape::XML::Node *preset_repr = inkscape_get_repr(INKSCAPE, preset_path);
3863         bool match = true;
3865         for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
3866               iter; 
3867               ++iter ) {
3868             const gchar *attr_name = g_quark_to_string(iter->key);
3869             if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
3870                 continue;
3871             void *widget = g_object_get_data(tbl, attr_name);
3872             if (widget) {
3873                 if (GTK_IS_ADJUSTMENT(widget)) {
3874                     double v = prefs_get_double_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
3875                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
3876                     //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
3877                     if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
3878                         match = false;
3879                         break;
3880                     }
3881                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
3882                     int v = prefs_get_int_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
3883                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
3884                     //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
3885                     if (gtk_toggle_action_get_active(toggle) != v) {
3886                         match = false;
3887                         break;
3888                     }
3889                 } 
3890             } 
3891         }
3893         if (match) {
3894             // newly added item is at the same index as the 
3895             // save command, so we need to change twice for it to take effect
3896             ege_select_one_action_set_active(sel, 0);
3897             ege_select_one_action_set_active(sel, i);
3898             return;
3899         }
3900     }
3902     // no match found
3903     ege_select_one_action_set_active(sel, 0);
3906 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3908     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value * 0.01 );
3909     update_presets_list(tbl);
3912 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3914     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value * 0.01 );
3915     update_presets_list(tbl);
3918 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3920     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3921     update_presets_list(tbl);
3924 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3926     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3927     update_presets_list(tbl);
3930 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3932     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value * 0.01 );
3933     update_presets_list(tbl);
3936 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3938     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value * 0.01);
3939     update_presets_list(tbl);
3942 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3944     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value * 0.01 );
3945     update_presets_list(tbl);
3948 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3950     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3951     update_presets_list(tbl);
3954 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
3956     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3957     update_presets_list(tbl);
3960 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
3962     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3963     update_presets_list(tbl);
3966 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
3968     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
3969     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3970     update_presets_list(tbl);
3971     if (calligraphy_angle )
3972         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3976 static gchar const *const widget_names[] = {
3977     "width",
3978     "mass",
3979     "wiggle",
3980     "angle",
3981     "thinning",
3982     "tremor",
3983     "flatness",
3984     "cap_rounding",
3985     "usepressure",
3986     "tracebackground",
3987     "usetilt"
3988 };
3991 static void sp_dcc_build_presets_list(GObject *tbl) 
3993     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
3995     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3996     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
3997     gtk_list_store_clear (model);
3999     {
4000         GtkTreeIter iter;
4001         gtk_list_store_append( model, &iter );
4002         gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4003     }
4005     //TODO: switch back to prefs API
4006     Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
4007     Inkscape::XML::Node *child_repr = sp_repr_children(repr);
4008     int ii=1;
4009     while (child_repr) {
4010         GtkTreeIter iter;
4011         char *preset_name = (char *) child_repr->attribute("name");
4012         gtk_list_store_append( model, &iter );
4013         gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );
4014         child_repr = sp_repr_next(child_repr);
4015     }
4017     {
4018         GtkTreeIter iter;
4019         gtk_list_store_append( model, &iter );
4020         gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4021         g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4022     }
4024     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4026     update_presets_list (tbl);
4029 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4031     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4032     if (! desktop) return;
4034     if (g_object_get_data(tbl, "presets_blocked")) 
4035         return;
4037     Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
4038     if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) {
4039         // dialog cancelled
4040         update_presets_list (tbl);
4041         return;
4042     }
4043     Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
4045     if (!profile_name.c_str() || *profile_name.c_str() == 0) {
4046         // empty name entered
4047         update_presets_list (tbl);
4048         return;
4049     }
4051     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4053     int new_index = -1;
4054     gchar *pref_path = NULL;
4055     int total_prefs = pref_path_number_of_children("tools.calligraphic.preset");
4057     for (int i = 1; i <= total_prefs; i++) {
4058         gchar *path = get_pref_nth_child("tools.calligraphic.preset", i);
4059         const gchar *name = prefs_get_string_attribute(path, "name");
4060         if (name && !strcmp(name, profile_name.c_str())) {
4061             // we already have preset with this name, replace its values
4062             new_index = i;
4063             pref_path = g_strdup(path);
4064             break;
4065         }
4066     }
4068     if (new_index == -1) {
4069         // no preset with this name, create 
4070         new_index = total_prefs + 1;
4071         gchar *profile_id = g_strdup_printf("dcc%d", new_index);
4072         pref_path = create_pref("tools.calligraphic.preset", profile_id);
4073         g_free(profile_id);
4074     }
4076     for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4077         gchar const *const widget_name = widget_names[i];
4078         void *widget = g_object_get_data(tbl, widget_name);
4079         if (widget) {
4080             if (GTK_IS_ADJUSTMENT(widget)) {
4081                 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4082                 double v = gtk_adjustment_get_value(adj);
4083                 prefs_set_double_attribute(pref_path, widget_name, v);
4084                 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4085             } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4086                 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4087                 int v = gtk_toggle_action_get_active(toggle);
4088                 prefs_set_int_attribute(pref_path, widget_name, v);
4089                 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4090             } else {
4091                g_warning("Unknown widget type for preset: %s\n", widget_name);
4092             }
4093         } else {
4094             g_warning("Bad key when writing preset: %s\n", widget_name);
4095         }
4096     }
4097     prefs_set_string_attribute(pref_path, "name", profile_name.c_str());
4099     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4101     sp_dcc_build_presets_list (tbl);
4103     g_free(pref_path);
4107 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4109     gint preset_index = ege_select_one_action_get_active( act );
4110     gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4112     if (preset_index == save_presets_index) {
4113         // this is the Save command
4114         sp_dcc_save_profile(NULL, tbl);
4115         return;
4116     }
4118     if (g_object_get_data(tbl, "presets_blocked")) 
4119         return;
4121     gchar *const preset_path = get_pref_nth_child("tools.calligraphic.preset", preset_index);
4123     if (preset_path) {
4124         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
4126         Inkscape::XML::Node *preset_repr = inkscape_get_repr(INKSCAPE, preset_path);
4128         for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
4129               iter; 
4130               ++iter ) {
4131             const gchar *attr_name = g_quark_to_string(iter->key);
4132             if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
4133                 continue;
4134             void *widget = g_object_get_data(tbl, attr_name);
4135             if (widget) {
4136                 if (GTK_IS_ADJUSTMENT(widget)) {
4137                     double v = prefs_get_double_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4138                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4139                     gtk_adjustment_set_value(adj, v);
4140                     //std::cout << "set adj " << attr_name << " to " << v << "\n";
4141                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4142                     int v = prefs_get_int_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4143                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4144                     gtk_toggle_action_set_active(toggle, v);
4145                     //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4146                 } else {
4147                     g_warning("Unknown widget type for preset: %s\n", attr_name);
4148                 }
4149             } else {
4150                 g_warning("Bad key found in a preset record: %s\n", attr_name);
4151             }
4152         }
4153         g_free(preset_path);
4154         g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4155     }
4160 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4162     {
4163         g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4165         EgeAdjustmentAction* calligraphy_angle = 0;
4167         {
4168         /* Width */
4169         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4170         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4171         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4172                                                               _("Pen Width"), _("Width:"),
4173                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
4174                                                               "tools.calligraphic", "width", 15,
4175                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4176                                                               1, 100, 1.0, 0.0,
4177                                                               labels, values, G_N_ELEMENTS(labels),
4178                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
4179         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4180         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4181         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4182         }
4184         {
4185         /* Thinning */
4186             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4187             gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4188         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4189                                                               _("Stroke Thinning"), _("Thinning:"),
4190                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4191                                                               "tools.calligraphic", "thinning", 10,
4192                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4193                                                               -100, 100, 1, 0.1,
4194                                                               labels, values, G_N_ELEMENTS(labels),
4195                                                               sp_ddc_velthin_value_changed, 0.01, 0, 100);
4196         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4197         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4198         }
4200         {
4201         /* Angle */
4202         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4203         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4204         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4205                                                               _("Pen Angle"), _("Angle:"),
4206                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4207                                                               "tools.calligraphic", "angle", 30,
4208                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4209                                                               -90.0, 90.0, 1.0, 10.0,
4210                                                               labels, values, G_N_ELEMENTS(labels),
4211                                                               sp_ddc_angle_value_changed, 1, 0 );
4212         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4213         g_object_set_data( holder, "angle_action", eact );
4214         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4215         calligraphy_angle = eact;
4216         }
4218         {
4219         /* Fixation */
4220             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4221         gdouble values[] = {0, 20, 40, 60, 90, 100};
4222         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4223                                                               _("Fixation"), _("Fixation:"),
4224                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
4225                                                               "tools.calligraphic", "flatness", 90,
4226                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4227                                                               0.0, 100, 1.0, 10.0,
4228                                                               labels, values, G_N_ELEMENTS(labels),
4229                                                               sp_ddc_flatness_value_changed, 0.01, 0, 100 );
4230         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4231         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4232         }
4234         {
4235         /* Cap Rounding */
4236             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4237         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4238         // TRANSLATORS: "cap" means "end" (both start and finish) here
4239         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4240                                                               _("Cap rounding"), _("Caps:"),
4241                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4242                                                               "tools.calligraphic", "cap_rounding", 0.0,
4243                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4244                                                               0.0, 5.0, 0.01, 0.1,
4245                                                               labels, values, G_N_ELEMENTS(labels),
4246                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4247         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4248         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4249         }
4251         {
4252         /* Tremor */
4253             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4254         gdouble values[] = {0, 10, 20, 40, 60, 100};
4255         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4256                                                               _("Stroke Tremor"), _("Tremor:"),
4257                                                               _("Increase to make strokes rugged and trembling"),
4258                                                               "tools.calligraphic", "tremor", 0.0,
4259                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4260                                                               0.0, 100, 1, 0.0,
4261                                                               labels, values, G_N_ELEMENTS(labels),
4262                                                               sp_ddc_tremor_value_changed, 0.01, 0, 100 );
4264         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4265         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4266         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4267         }
4269         {
4270         /* Wiggle */
4271         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4272         gdouble values[] = {0, 20, 40, 60, 100};
4273         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4274                                                               _("Pen Wiggle"), _("Wiggle:"),
4275                                                               _("Increase to make the pen waver and wiggle"),
4276                                                               "tools.calligraphic", "wiggle", 0.0,
4277                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4278                                                               0.0, 100, 1, 0.0,
4279                                                               labels, values, G_N_ELEMENTS(labels),
4280                                                               sp_ddc_wiggle_value_changed, 0.01, 0, 100 );
4281         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4282         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4283         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4284         }
4286         {
4287         /* Mass */
4288             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4289         gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4290         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4291                                                               _("Pen Mass"), _("Mass:"),
4292                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
4293                                                               "tools.calligraphic", "mass", 2.0,
4294                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4295                                                               0.0, 100, 1, 0.0,
4296                                                               labels, values, G_N_ELEMENTS(labels),
4297                                                               sp_ddc_mass_value_changed, 0.01, 0, 100 );
4298         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4299         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4300         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4301         }
4304         /* Trace Background button */
4305         {
4306             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4307                                                           _("Trace Background"),
4308                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4309                                                           "trace_background",
4310                                                           Inkscape::ICON_SIZE_DECORATION );
4311             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4312             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4313             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
4314             g_object_set_data( holder, "tracebackground", act );
4315         }
4317         /* Use Pressure button */
4318         {
4319             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4320                                                           _("Pressure"),
4321                                                           _("Use the pressure of the input device to alter the width of the pen"),
4322                                                           "use_pressure",
4323                                                           Inkscape::ICON_SIZE_DECORATION );
4324             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4325             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4326             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
4327             g_object_set_data( holder, "usepressure", act );
4328         }
4330         /* Use Tilt button */
4331         {
4332             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4333                                                           _("Tilt"),
4334                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
4335                                                           "use_tilt",
4336                                                           Inkscape::ICON_SIZE_DECORATION );
4337             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4338             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4339             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4340             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4341             g_object_set_data( holder, "usetilt", act );
4342         }
4344         /*calligraphic profile */
4345         {
4346             GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
4347             EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
4348             ege_select_one_action_set_appearance (act1, "compact");
4349             g_object_set_data (holder, "profile_selector", act1 );
4351             g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
4353             sp_dcc_build_presets_list (holder);
4355             g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
4356             gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
4357         }
4358     }
4362 //########################
4363 //##    Circle / Arc    ##
4364 //########################
4366 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4368     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4369     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4371     if (v1 == 0 && v2 == 0) {
4372         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4373             gtk_action_set_sensitive( ocb, FALSE );
4374             gtk_action_set_sensitive( make_whole, FALSE );
4375         }
4376     } else {
4377         gtk_action_set_sensitive( ocb, TRUE );
4378         gtk_action_set_sensitive( make_whole, TRUE );
4379     }
4382 static void
4383 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4385     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4387     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4388         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
4389     }
4391     // quit if run by the attr_changed listener
4392     if (g_object_get_data( tbl, "freeze" )) {
4393         return;
4394     }
4396     // in turn, prevent listener from responding
4397     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4399     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4401     bool modmade = false;
4402     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4403          items != NULL;
4404          items = items->next)
4405     {
4406         SPItem *item = SP_ITEM(items->data);
4408         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4410             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4411             SPArc *arc = SP_ARC(item);
4413             if (!strcmp(value_name, "start"))
4414                 ge->start = (adj->value * M_PI)/ 180;
4415             else
4416                 ge->end = (adj->value * M_PI)/ 180;
4418             sp_genericellipse_normalize(ge);
4419             ((SPObject *)arc)->updateRepr();
4420             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4422             modmade = true;
4423         }
4424     }
4426     g_free(namespaced_name);
4428     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4430     sp_arctb_sensitivize( tbl, adj->value, other->value );
4432     if (modmade) {
4433         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4434                                    _("Arc: Change start/end"));
4435     }
4437     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4441 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
4443     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
4446 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4448     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
4452 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4454     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4455     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4456         if ( ege_select_one_action_get_active( act ) != 0 ) {
4457             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
4458         } else {
4459             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
4460         }
4461     }
4463     // quit if run by the attr_changed listener
4464     if (g_object_get_data( tbl, "freeze" )) {
4465         return;
4466     }
4468     // in turn, prevent listener from responding
4469     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4471     bool modmade = false;
4473     if ( ege_select_one_action_get_active(act) != 0 ) {
4474         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4475              items != NULL;
4476              items = items->next)
4477         {
4478             if (SP_IS_ARC((SPItem *) items->data)) {
4479                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4480                 repr->setAttribute("sodipodi:open", "true");
4481                 SP_OBJECT((SPItem *) items->data)->updateRepr();
4482                 modmade = true;
4483             }
4484         }
4485     } else {
4486         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4487              items != NULL;
4488              items = items->next)
4489         {
4490             if (SP_IS_ARC((SPItem *) items->data))    {
4491                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4492                 repr->setAttribute("sodipodi:open", NULL);
4493                 SP_OBJECT((SPItem *) items->data)->updateRepr();
4494                 modmade = true;
4495             }
4496         }
4497     }
4499     if (modmade) {
4500         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4501                                    _("Arc: Change open/closed"));
4502     }
4504     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4507 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4509     GtkAdjustment *adj;
4510     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
4511     gtk_adjustment_set_value(adj, 0.0);
4512     gtk_adjustment_value_changed(adj);
4514     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
4515     gtk_adjustment_set_value(adj, 0.0);
4516     gtk_adjustment_value_changed(adj);
4518     spinbutton_defocus( GTK_OBJECT(obj) );
4521 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
4522                                       gchar const */*old_value*/, gchar const */*new_value*/,
4523                                       bool /*is_interactive*/, gpointer data)
4525     GObject *tbl = G_OBJECT(data);
4527     // quit if run by the _changed callbacks
4528     if (g_object_get_data( tbl, "freeze" )) {
4529         return;
4530     }
4532     // in turn, prevent callbacks from responding
4533     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4535     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4536     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4538     GtkAdjustment *adj1,*adj2;
4539     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4540     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4541     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4542     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4544     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4546     char const *openstr = NULL;
4547     openstr = repr->attribute("sodipodi:open");
4548     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4550     if (openstr) {
4551         ege_select_one_action_set_active( ocb, 1 );
4552     } else {
4553         ege_select_one_action_set_active( ocb, 0 );
4554     }
4556     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4559 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4560     NULL, /* child_added */
4561     NULL, /* child_removed */
4562     arc_tb_event_attr_changed,
4563     NULL, /* content_changed */
4564     NULL  /* order_changed */
4565 };
4568 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4570     int n_selected = 0;
4571     Inkscape::XML::Node *repr = NULL;
4573     purge_repr_listener( tbl, tbl );
4575     for (GSList const *items = selection->itemList();
4576          items != NULL;
4577          items = items->next)
4578     {
4579         if (SP_IS_ARC((SPItem *) items->data)) {
4580             n_selected++;
4581             repr = SP_OBJECT_REPR((SPItem *) items->data);
4582         }
4583     }
4585     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4587     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4588     if (n_selected == 0) {
4589         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4590     } else if (n_selected == 1) {
4591         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4592         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4594         if (repr) {
4595             g_object_set_data( tbl, "repr", repr );
4596             Inkscape::GC::anchor(repr);
4597             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4598             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4599         }
4600     } else {
4601         // FIXME: implement averaging of all parameters for multiple selected
4602         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4603         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4604         sp_arctb_sensitivize( tbl, 1, 0 );
4605     }
4609 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4611     EgeAdjustmentAction* eact = 0;
4612     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
4615     {
4616         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4617         ege_output_action_set_use_markup( act, TRUE );
4618         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4619         g_object_set_data( holder, "mode_action", act );
4620     }
4622     /* Start */
4623     {
4624         eact = create_adjustment_action( "ArcStartAction",
4625                                          _("Start"), _("Start:"),
4626                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
4627                                          "tools.shapes.arc", "start", 0.0,
4628                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4629                                          -360.0, 360.0, 1.0, 10.0,
4630                                          0, 0, 0,
4631                                          sp_arctb_start_value_changed);
4632         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4633     }
4635     /* End */
4636     {
4637         eact = create_adjustment_action( "ArcEndAction",
4638                                          _("End"), _("End:"),
4639                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
4640                                          "tools.shapes.arc", "end", 0.0,
4641                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4642                                          -360.0, 360.0, 1.0, 10.0,
4643                                          0, 0, 0,
4644                                          sp_arctb_end_value_changed);
4645         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4646     }
4648     /* Segments / Pie checkbox */
4649     {
4650         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4652         GtkTreeIter iter;
4653         gtk_list_store_append( model, &iter );
4654         gtk_list_store_set( model, &iter,
4655                             0, _("Closed arc"),
4656                             1, _("Switch to segment (closed shape with two radii)"),
4657                             2, "circle_closed_arc",
4658                             -1 );
4660         gtk_list_store_append( model, &iter );
4661         gtk_list_store_set( model, &iter,
4662                             0, _("Open Arc"),
4663                             1, _("Switch to arc (unclosed shape)"),
4664                             2, "circle_open_arc",
4665                             -1 );
4667         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4668         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4669         g_object_set_data( holder, "open_action", act );
4671         ege_select_one_action_set_appearance( act, "full" );
4672         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4673         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4674         ege_select_one_action_set_icon_column( act, 2 );
4675         ege_select_one_action_set_icon_size( act, secondarySize );
4676         ege_select_one_action_set_tooltip_column( act, 1  );
4678         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4679         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4680         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4681         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4682     }
4684     /* Make Whole */
4685     {
4686         InkAction* inky = ink_action_new( "ArcResetAction",
4687                                           _("Make whole"),
4688                                           _("Make the shape a whole ellipse, not arc or segment"),
4689                                           "reset_circle",
4690                                           secondarySize );
4691         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4692         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4693         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4694         g_object_set_data( holder, "make_whole", inky );
4695     }
4697     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4698     // sensitivize make whole and open checkbox
4699     {
4700         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4701         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4702         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4703     }
4706     sigc::connection *connection = new sigc::connection(
4707         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4708         );
4709     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4710     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4716 // toggle button callbacks and updaters
4718 //########################
4719 //##      Dropper       ##
4720 //########################
4722 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4723     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4724     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4725     if ( set_action ) {
4726         if ( gtk_toggle_action_get_active( act ) ) {
4727             gtk_action_set_sensitive( set_action, TRUE );
4728         } else {
4729             gtk_action_set_sensitive( set_action, FALSE );
4730         }
4731     }
4733     spinbutton_defocus(GTK_OBJECT(tbl));
4736 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4737     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4738     spinbutton_defocus(GTK_OBJECT(tbl));
4742 /**
4743  * Dropper auxiliary toolbar construction and setup.
4744  *
4745  * TODO: Would like to add swatch of current color.
4746  * TODO: Add queue of last 5 or so colors selected with new swatches so that
4747  *       can drag and drop places. Will provide a nice mixing palette.
4748  */
4749 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4751     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4753     {
4754         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4755         ege_output_action_set_use_markup( act, TRUE );
4756         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4757     }
4759     {
4760         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4761                                                       _("Pick opacity"),
4762                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4763                                                       NULL,
4764                                                       Inkscape::ICON_SIZE_DECORATION );
4765         g_object_set( act, "short_label", _("Pick"), NULL );
4766         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4767         g_object_set_data( holder, "pick_action", act );
4768         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4769         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4770     }
4772     {
4773         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4774                                                       _("Assign opacity"),
4775                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4776                                                       NULL,
4777                                                       Inkscape::ICON_SIZE_DECORATION );
4778         g_object_set( act, "short_label", _("Assign"), NULL );
4779         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4780         g_object_set_data( holder, "set_action", act );
4781         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4782         // make sure it's disabled if we're not picking alpha
4783         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4784         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4785     }
4789 //########################
4790 //##      LPETool       ##
4791 //########################
4793 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
4795 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
4796 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
4798     using namespace Inkscape::LivePathEffect;
4800     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
4801     SPEventContext *ec = desktop->event_context;
4802     if (!SP_IS_LPETOOL_CONTEXT(ec)) {
4803         return;
4804     }
4806     // only take action if run by the attr_changed listener
4807     if (!g_object_get_data(tbl, "freeze")) {
4808         // in turn, prevent listener from responding
4809         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
4811         gint mode = ege_select_one_action_get_active(act);
4812         EffectType type = lpesubtools[mode];
4814         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4815         bool success = lpetool_try_construction(lc, type);
4816         if (success) {
4817             // since the construction was already performed, we set the state back to inactive
4818             ege_select_one_action_set_active(act, 0);
4819             mode = 0;
4820         } else {
4821             // switch to the chosen subtool
4822             SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
4823         }
4825         if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4826             prefs_set_int_attribute( "tools.lpetool", "mode", mode );
4827         }
4829         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
4830     }
4833 void
4834 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
4836     using namespace Inkscape::LivePathEffect;
4837     {
4838         GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
4839         SPItem *item = selection->singleItem();
4840         SPEventContext *ec = selection->desktop()->event_context;
4841         if (!SP_IS_LPETOOL_CONTEXT(ec))
4842             return;
4843         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
4844         if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
4845             SPLPEItem *lpeitem = SP_LPE_ITEM(item);
4846             Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
4847             if (lpe && lpe->effectType() == LINE_SEGMENT) {
4848                 LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
4849                 g_object_set_data(tbl, "currentlpe", lpe);
4850                 g_object_set_data(tbl, "currentlpeitem", lpeitem);
4851                 gtk_action_set_sensitive(w, TRUE);
4852                 ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
4853             } else {
4854                 g_object_set_data(tbl, "currentlpe", NULL);
4855                 g_object_set_data(tbl, "currentlpeitem", NULL);
4856                 gtk_action_set_sensitive(w, FALSE);
4857             }
4858         } else {
4859             g_object_set_data(tbl, "currentlpe", NULL);
4860             g_object_set_data(tbl, "currentlpeitem", NULL);
4861             gtk_action_set_sensitive(w, FALSE);
4862         }
4863     }
4866 static void
4867 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
4868     SPDesktop *desktop = static_cast<SPDesktop *>(data);
4870     bool show = gtk_toggle_action_get_active( act );
4871     prefs_set_int_attribute ("tools.lpetool", "show_bbox",  show ? 1 : 0);
4873     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
4874         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4875         lpetool_context_reset_limiting_bbox(lc);
4876     }
4879 static void
4880 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
4881     SPDesktop *desktop = static_cast<SPDesktop *>(data);
4882     Inkscape::Selection *selection = desktop->selection;
4884     boost::optional<NR::Rect> bbox = selection->bounds();
4886     if (bbox) {
4887         Geom::Point A(bbox->min());
4888         Geom::Point B(bbox->max());
4890         A *= desktop->doc2dt();
4891         B *= desktop->doc2dt();
4893         // TODO: should we provide a way to store points in prefs?
4894         prefs_set_double_attribute ("tools.lpetool", "bbox_upperleftx", A[Geom::X]);
4895         prefs_set_double_attribute ("tools.lpetool", "bbox_upperlefty", A[Geom::Y]);
4896         prefs_set_double_attribute ("tools.lpetool", "bbox_lowerrightx", B[Geom::X]);
4897         prefs_set_double_attribute ("tools.lpetool", "bbox_lowerrighty", B[Geom::Y]);
4899         lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
4900     }
4902     gtk_toggle_action_set_active(act, false);
4905 static void
4906 sp_line_segment_build_list(GObject *tbl) 
4908     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
4910     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
4911     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4912     gtk_list_store_clear (model);
4914     // TODO: we add the entries of rht combo box manually; later this should be done automatically
4915     {
4916         GtkTreeIter iter;
4917         gtk_list_store_append( model, &iter );
4918         gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
4919         gtk_list_store_append( model, &iter );
4920         gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
4921         gtk_list_store_append( model, &iter );
4922         gtk_list_store_set( model, &iter, 0, _("Open left"), 1, 1, -1 );
4923         gtk_list_store_append( model, &iter );
4924         gtk_list_store_set( model, &iter, 0, _("Open right"), 1, 2, -1 );
4925     }
4927     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
4930 static void
4931 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
4932     using namespace Inkscape::LivePathEffect;
4934     // quit if run by the attr_changed listener
4935     if (g_object_get_data(tbl, "freeze")) {
4936         return;
4937     }
4939     // in turn, prevent listener from responding
4940     g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
4942     LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
4943     SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
4944     if (lpeitem) {
4945         SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
4946         lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
4947         sp_lpe_item_update_patheffect(lpeitem, true, true);
4948     }
4950     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4953 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4955     /** Automatically create a list of LPEs that get added to the toolbar **/
4956     {
4957         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4959         GtkTreeIter iter;
4961         // the first toggle button represents the state that no subtool is active (remove this when
4962         // this can be modeled by EgeSelectOneAction or some other action)
4963         gtk_list_store_append( model, &iter );
4964         gtk_list_store_set( model, &iter,
4965                             0, _("All inactive"),
4966                             1, _("No geometric tool is active"),
4967                             2, _("all_inactive"),
4968                             -1 );
4970         Inkscape::LivePathEffect::EffectType type;
4971         for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
4972             type =  lpesubtools[i];
4973             gtk_list_store_append( model, &iter );
4974             gtk_list_store_set( model, &iter,
4975                                 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
4976                                 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
4977                                 2, Inkscape::LivePathEffect::LPETypeConverter.get_key(type).c_str(),
4978                                 -1 );
4979         }
4981         EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4982         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4983         g_object_set_data( holder, "lpetool_mode_action", act );
4985         ege_select_one_action_set_appearance( act, "full" );
4986         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4987         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4988         ege_select_one_action_set_icon_column( act, 2 );
4989         ege_select_one_action_set_tooltip_column( act, 1  );
4991         gint lpeToolMode = prefs_get_int_attribute("tools.lpetool", "mode", 0);
4992         ege_select_one_action_set_active( act, lpeToolMode );
4993         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
4994     }
4996     /* Show limiting bounding box */
4997     {
4998         InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
4999                                                       _("Show limiting bounding box"),
5000                                                       _("Show bounding box (used to cut infinite lines)"),
5001                                                       "lpetool_show_bbox",
5002                                                       Inkscape::ICON_SIZE_DECORATION );
5003         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5004         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5005         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.lpetool", "show_bbox", 1 ) );
5006     }
5008     /* Set limiting bounding box to bbox of current selection */
5009     {
5010         InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5011                                                       _("Get limiting bounding box from selection"),
5012                                                       _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5013                                                       "lpetool_set_bbox",
5014                                                       Inkscape::ICON_SIZE_DECORATION );
5015         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5016         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5017         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5018     }
5021     /* Combo box to choose line segment type */
5022     {
5023         GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5024         EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5025         ege_select_one_action_set_appearance (act, "compact");
5026         g_object_set_data (holder, "lpetool_line_segment_action", act );
5028         g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5030         sp_line_segment_build_list (holder);
5032         g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5033         gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5034         gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5035     }
5037     //watch selection
5038     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5040     sigc::connection *c_selection_changed =
5041         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5042                               (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5043     pool->add_connection ("selection-changed", c_selection_changed);
5046 //########################
5047 //##       Eraser       ##
5048 //########################
5050 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5052     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5053     gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
5054     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5055         prefs_set_int_attribute( "tools.eraser", "mode", eraserMode );
5056     }
5058     // only take action if run by the attr_changed listener
5059     if (!g_object_get_data( tbl, "freeze" )) {
5060         // in turn, prevent listener from responding
5061         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5063         if ( eraserMode != 0 ) {
5064         } else {
5065         }
5066         // TODO finish implementation
5068         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5069     }
5072 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5074     {
5075         /* Width */
5076         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5077         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5078         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5079                                                               _("Pen Width"), _("Width:"),
5080                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
5081                                                               "tools.eraser", "width", 15,
5082                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5083                                                               1, 100, 1.0, 0.0,
5084                                                               labels, values, G_N_ELEMENTS(labels),
5085                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
5086         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5087         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5088         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5089     }
5091     {
5092         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5094         GtkTreeIter iter;
5095         gtk_list_store_append( model, &iter );
5096         gtk_list_store_set( model, &iter,
5097                             0, _("Delete"),
5098                             1, _("Delete objects touched by the eraser"),
5099                             2, "delete_object",
5100                             -1 );
5102         gtk_list_store_append( model, &iter );
5103         gtk_list_store_set( model, &iter,
5104                             0, _("Cut"),
5105                             1, _("Cut out from objects"),
5106                             2, "difference",
5107                             -1 );
5109         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5110         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5111         g_object_set_data( holder, "eraser_mode_action", act );
5113         ege_select_one_action_set_appearance( act, "full" );
5114         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5115         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5116         ege_select_one_action_set_icon_column( act, 2 );
5117         ege_select_one_action_set_tooltip_column( act, 1  );
5119         gint eraserMode = (prefs_get_int_attribute("tools.eraser", "mode", 0) != 0) ? 1 : 0;
5120         ege_select_one_action_set_active( act, eraserMode );
5121         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
5122     }
5126 //########################
5127 //##    Text Toolbox    ##
5128 //########################
5129 /*
5130 static void
5131 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
5133     //Call back for letter sizing spinbutton
5136 static void
5137 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
5139     //Call back for line height spinbutton
5142 static void
5143 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5145     //Call back for horizontal kerning spinbutton
5148 static void
5149 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5151     //Call back for vertical kerning spinbutton
5154 static void
5155 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
5157     //Call back for letter rotation spinbutton
5158 }*/
5160 namespace {
5162 bool popdown_visible = false;
5163 bool popdown_hasfocus = false;
5165 void
5166 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
5168     SPStyle *query =
5169         sp_style_new (SP_ACTIVE_DOCUMENT);
5171 //    int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5173     int result_family =
5174         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5176     int result_style =
5177         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5179     int result_numbers =
5180         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5182     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5184     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5185     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
5186         // there are no texts in selection, read from prefs
5187  
5188         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
5189         if (repr) {
5190             sp_style_read_from_repr (query, repr);
5191             if (g_object_get_data(tbl, "text_style_from_prefs")) {
5192                 // do not reset the toolbar style from prefs if we already did it last time
5193                 sp_style_unref(query);
5194                 return;
5195             }
5196             g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
5197         } else {
5198             sp_style_unref(query);
5199             return;
5200         }
5201     } else {
5202         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
5203     }
5205     if (query->text)
5206     {
5207         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
5208             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5209             gtk_entry_set_text (GTK_ENTRY (entry), "");
5211         } else if (query->text->font_specification.value || query->text->font_family.value) {
5213             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5215             // Get the font that corresponds
5216             Glib::ustring familyName;
5218             font_instance * font = font_factory::Default()->FaceFromStyle(query);
5219             if (font) {
5220                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
5221                 font->Unref();
5222                 font = NULL;
5223             }
5225             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
5227             Gtk::TreePath path;
5228             try {
5229                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
5230             } catch (...) {
5231                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
5232                 sp_style_unref(query);
5233                 return;
5234             }
5236             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5237             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5239             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
5241             gtk_tree_selection_select_path (tselection, path.gobj());
5242             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5244             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
5245         }
5247         //Size
5248         {
5249             GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
5250             gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
5251             g_object_set_data(tbl, "size-block", gpointer(1));
5252             gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
5253             g_object_set_data(tbl, "size-block", gpointer(0));
5254             g_free(str);
5255         }
5257         //Anchor
5258         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
5259         {
5260             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
5261             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5262             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5263             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5264         }
5265         else
5266         {
5267             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
5268             {
5269                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
5270                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5271                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5272                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5273             }
5274             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
5275             {
5276                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
5277                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5278                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5279                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5280             }
5281             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
5282             {
5283                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
5284                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5285                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5286                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5287             }
5288         }
5290         //Style
5291         {
5292             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
5294             gboolean active = gtk_toggle_button_get_active (button);
5295             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
5297             if (active != check)
5298             {
5299                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5300                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5301                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5302             }
5303         }
5305         {
5306             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
5308             gboolean active = gtk_toggle_button_get_active (button);
5309             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
5311             if (active != check)
5312             {
5313                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5314                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5315                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5316             }
5317         }
5319         //Orientation
5320         //locking both buttons, changing one affect all group (both)
5321         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
5322         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5324         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
5325         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
5327         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
5328         {
5329             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5330         }
5331         else
5332         {
5333             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
5334         }
5335         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5336         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
5337     }
5339     sp_style_unref(query);
5342 void
5343 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
5345     sp_text_toolbox_selection_changed (selection, tbl);
5348 void
5349 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
5351     sp_text_toolbox_selection_changed (NULL, tbl);
5354 void
5355 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
5356                                 GObject             *tbl)
5358     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
5359     GtkTreeModel *model = 0;
5360     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5361     GtkTreeIter   iter;
5362     char         *family = 0;
5364     gdk_pointer_ungrab (GDK_CURRENT_TIME);
5365     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5367     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
5368         return;
5369     }
5371     gtk_tree_model_get (model, &iter, 0, &family, -1);
5373     if (g_object_get_data (G_OBJECT (selection), "block"))
5374     {
5375         gtk_entry_set_text (GTK_ENTRY (entry), family);
5376         return;
5377     }
5379     gtk_entry_set_text (GTK_ENTRY (entry), family);
5381     SPStyle *query =
5382         sp_style_new (SP_ACTIVE_DOCUMENT);
5384     int result_fontspec =
5385         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5387     //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5389     SPCSSAttr *css = sp_repr_css_attr_new ();
5392     // First try to get the font spec from the stored value
5393     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
5395     if (fontSpec.empty()) {
5396         // Construct a new font specification if it does not yet exist
5397         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5398         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5399         fontFromStyle->Unref();
5400     }
5402     if (!fontSpec.empty()) {
5403         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
5404         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
5405             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
5406             if (font) {
5407                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5409                 // Set all the these just in case they were altered when finding the best
5410                 // match for the new family and old style...
5412                 gchar c[256];
5414                 font->Family(c, 256);
5415                 sp_repr_css_set_property (css, "font-family", c);
5417                 font->Attribute( "weight", c, 256);
5418                 sp_repr_css_set_property (css, "font-weight", c);
5420                 font->Attribute("style", c, 256);
5421                 sp_repr_css_set_property (css, "font-style", c);
5423                 font->Attribute("stretch", c, 256);
5424                 sp_repr_css_set_property (css, "font-stretch", c);
5426                 font->Attribute("variant", c, 256);
5427                 sp_repr_css_set_property (css, "font-variant", c);
5429                 font->Unref();
5430             }
5431         }
5432     }
5434     // If querying returned nothing, set the default style of the tool (for new texts)
5435     if (result_fontspec == QUERY_STYLE_NOTHING)
5436     {
5437         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5438         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
5439     }
5440     else
5441     {
5442         sp_desktop_set_style (desktop, css, true, true);
5443     }
5445     sp_style_unref(query);
5447     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5448                                    _("Text: Change font family"));
5449     sp_repr_css_attr_unref (css);
5450     g_free(family);
5451     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5453     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5456 /* This is where execution comes when the contents of the font family box have been completed
5457    by the press of the return key */
5458 void
5459 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
5460                                        GObject      *tbl)
5462     const char *family = gtk_entry_get_text (entry);   // Fetch the requested font family
5464 // Try to match that to a known font. If not, then leave current font alone and remain focused on text box
5465     try {
5466         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
5467         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5468         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5469         gtk_tree_selection_select_path (selection, path.gobj());
5470         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5471         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5472     } catch (...) {
5473         if (family && strlen (family))
5474         {
5475             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5476         }
5477     }
5480 void
5481 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
5482                                    gpointer          data)
5484     if (g_object_get_data (G_OBJECT (button), "block")) return;
5485     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
5486     int prop = GPOINTER_TO_INT(data);
5488     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5489     SPCSSAttr *css = sp_repr_css_attr_new ();
5491     switch (prop)
5492     {
5493         case 0:
5494         {
5495             sp_repr_css_set_property (css, "text-anchor", "start");
5496             sp_repr_css_set_property (css, "text-align", "start");
5497             break;
5498         }
5499         case 1:
5500         {
5501             sp_repr_css_set_property (css, "text-anchor", "middle");
5502             sp_repr_css_set_property (css, "text-align", "center");
5503             break;
5504         }
5506         case 2:
5507         {
5508             sp_repr_css_set_property (css, "text-anchor", "end");
5509             sp_repr_css_set_property (css, "text-align", "end");
5510             break;
5511         }
5513         case 3:
5514         {
5515             sp_repr_css_set_property (css, "text-anchor", "start");
5516             sp_repr_css_set_property (css, "text-align", "justify");
5517             break;
5518         }
5519     }
5521     SPStyle *query =
5522         sp_style_new (SP_ACTIVE_DOCUMENT);
5523     int result_numbers =
5524         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5526     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5527     if (result_numbers == QUERY_STYLE_NOTHING)
5528     {
5529         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5530     }
5532     sp_style_unref(query);
5534     sp_desktop_set_style (desktop, css, true, true);
5535     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5536                                    _("Text: Change alignment"));
5537     sp_repr_css_attr_unref (css);
5539     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5542 void
5543 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
5544                                gpointer          data)
5546     if (g_object_get_data (G_OBJECT (button), "block")) return;
5548     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5549     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5550     int          prop       = GPOINTER_TO_INT(data);
5551     bool         active     = gtk_toggle_button_get_active (button);
5553     SPStyle *query =
5554         sp_style_new (SP_ACTIVE_DOCUMENT);
5556     int result_fontspec =
5557         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5559     //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5560     //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5561     //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5563     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
5564     Glib::ustring newFontSpec = "";
5566     if (fontSpec.empty()) {
5567         // Construct a new font specification if it does not yet exist
5568         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5569         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5570         fontFromStyle->Unref();
5571     }
5573     switch (prop)
5574     {
5575         case 0:
5576         {
5577             if (!fontSpec.empty()) {
5578                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
5579             }
5580             if (fontSpec != newFontSpec) {
5581                 // Don't even set the bold if the font didn't exist on the system
5582                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
5583             }
5584             break;
5585         }
5587         case 1:
5588         {
5589             if (!fontSpec.empty()) {
5590                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
5591             }
5592             if (fontSpec != newFontSpec) {
5593                 // Don't even set the italic if the font didn't exist on the system
5594                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
5595             }
5596             break;
5597         }
5598     }
5600     if (!newFontSpec.empty()) {
5601         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5602     }
5604     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5605     if (result_fontspec == QUERY_STYLE_NOTHING)
5606     {
5607         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5608     }
5610     sp_style_unref(query);
5612     sp_desktop_set_style (desktop, css, true, true);
5613     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5614                                    _("Text: Change font style"));
5615     sp_repr_css_attr_unref (css);
5617     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5620 void
5621 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
5622                                      gpointer         data)
5624     if (g_object_get_data (G_OBJECT (button), "block")) {
5625         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5626         return;
5627     }
5629     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5630     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5631     int          prop       = GPOINTER_TO_INT(data);
5633     switch (prop)
5634     {
5635         case 0:
5636         {
5637             sp_repr_css_set_property (css, "writing-mode", "lr");
5638             break;
5639         }
5641         case 1:
5642         {
5643             sp_repr_css_set_property (css, "writing-mode", "tb");
5644             break;
5645         }
5646     }
5648     SPStyle *query =
5649         sp_style_new (SP_ACTIVE_DOCUMENT);
5650     int result_numbers =
5651         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5653     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5654     if (result_numbers == QUERY_STYLE_NOTHING)
5655     {
5656         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5657     }
5659     sp_desktop_set_style (desktop, css, true, true);
5660     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5661                                    _("Text: Change orientation"));
5662     sp_repr_css_attr_unref (css);
5664     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5667 gboolean
5668 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5670     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5671     if (!desktop) return FALSE;
5673     switch (get_group0_keyval (event)) {
5674         case GDK_Escape: // defocus
5675             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5676             sp_text_toolbox_selection_changed (NULL, tbl); // update
5677             return TRUE; // I consumed the event
5678             break;
5679     }
5680     return FALSE;
5683 gboolean
5684 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5686     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5687     if (!desktop) return FALSE;
5689     switch (get_group0_keyval (event)) {
5690         case GDK_KP_Enter:
5691         case GDK_Return:
5692         case GDK_Escape: // defocus
5693             gtk_widget_hide (w);
5694             popdown_visible = false;
5695             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5696             return TRUE; // I consumed the event
5697             break;
5698         case GDK_w:
5699         case GDK_W:
5700             if (event->state & GDK_CONTROL_MASK) {
5701                 gtk_widget_hide (w);
5702                 popdown_visible = false;
5703                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5704                 return TRUE; // I consumed the event
5705             }
5706             break;
5707     }
5708     return FALSE;
5712 void
5713 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
5714                                GObject     *tbl)
5716     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5718     if (g_object_get_data (tbl, "size-block")) return;
5720     // If this is not from selecting a size in the list (in which case get_active will give the
5721     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5722     // process this event. This fixes GTK's stupid insistence on sending an activate change every
5723     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5724     if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5725         return;
5727     gdouble value = -1;
5728     {
5729         gchar *endptr;
5730         gchar *const text = gtk_combo_box_get_active_text(cbox);
5731         if (text) {
5732             value = g_strtod(text, &endptr);
5733             if (endptr == text) {  // Conversion failed, non-numeric input.
5734                 value = -1;
5735             }
5736             g_free(text);
5737         }
5738     }
5739     if (value <= 0) {
5740         return; // could not parse value
5741     }
5743     SPCSSAttr *css = sp_repr_css_attr_new ();
5744     Inkscape::CSSOStringStream osfs;
5745     osfs << value;
5746     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5748     SPStyle *query =
5749         sp_style_new (SP_ACTIVE_DOCUMENT);
5750     int result_numbers =
5751         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5753     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5754     if (result_numbers == QUERY_STYLE_NOTHING)
5755     {
5756         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5757     }
5759     sp_style_unref(query);
5761     sp_desktop_set_style (desktop, css, true, true);
5762     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5763                                    _("Text: Change font size"));
5764     sp_repr_css_attr_unref (css);
5766     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5769 gboolean
5770 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5772     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5773     if (!desktop) return FALSE;
5775     if (!g_object_get_data (tbl, "esc-pressed")) {
5776         g_object_set_data (tbl, "enter-pressed", gpointer(1));
5777         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5778         sp_text_toolbox_size_changed (cbox, tbl);
5779         g_object_set_data (tbl, "enter-pressed", gpointer(0));
5780     }
5781     return FALSE; // I consumed the event
5785 gboolean
5786 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5788     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5789     if (!desktop) return FALSE;
5791     switch (get_group0_keyval (event)) {
5792         case GDK_Escape: // defocus
5793             g_object_set_data (tbl, "esc-pressed", gpointer(1));
5794             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5795             g_object_set_data (tbl, "esc-pressed", gpointer(0));
5796             return TRUE; // I consumed the event
5797             break;
5798         case GDK_Return: // defocus
5799         case GDK_KP_Enter:
5800             g_object_set_data (tbl, "enter-pressed", gpointer(1));
5801             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5802             sp_text_toolbox_size_changed (cbox, tbl);
5803             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5804             g_object_set_data (tbl, "enter-pressed", gpointer(0));
5805             return TRUE; // I consumed the event
5806             break;
5807     }
5808     return FALSE;
5811 void
5812 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
5813                                          GObject            *tbl)
5815     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
5816     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5817     int x, y;
5819     if (!popdown_visible)
5820     {
5821         gdk_window_get_origin (widget->window, &x, &y);
5822         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
5823         gtk_widget_show_all (popdown);
5824         //sp_transientize (popdown);
5826         gdk_pointer_grab (widget->window, TRUE,
5827                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
5828                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
5829                                         GDK_POINTER_MOTION_MASK),
5830                           NULL, NULL, GDK_CURRENT_TIME);
5832         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
5834         popdown_visible = true;
5835     }
5836     else
5837     {
5838         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5839         gdk_pointer_ungrab (GDK_CURRENT_TIME);
5840         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5841         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5842         gtk_widget_hide (popdown);
5843         popdown_visible = false;
5844     }
5847 gboolean
5848 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
5849                                  GdkEventFocus    */*event*/,
5850                                  GObject          */*tbl*/)
5852     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
5853     return FALSE;
5856 gboolean
5857 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
5858                                    GdkEventFocus    */*event*/,
5859                                    GObject          */*tbl*/)
5861     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5863     if (popdown_hasfocus) {
5864         gtk_widget_hide (popdown);
5865         popdown_hasfocus = false;
5866         popdown_visible = false;
5867         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5868         return TRUE;
5869     }
5870     return FALSE;
5873 gboolean
5874 sp_text_toolbox_popdown_focus_in (GtkWidget        */*popdown*/,
5875                                    GdkEventFocus    */*event*/,
5876                                    GObject          */*tbl*/)
5878     popdown_hasfocus = true;
5879     return TRUE;
5883 void
5884 cell_data_func  (GtkTreeViewColumn */*column*/,
5885                  GtkCellRenderer   *cell,
5886                  GtkTreeModel      *tree_model,
5887                  GtkTreeIter       *iter,
5888                  gpointer           /*data*/)
5890     gchar *family;
5891     gtk_tree_model_get(tree_model, iter, 0, &family, -1);
5892     gchar *const family_escaped = g_markup_escape_text(family, -1);
5894     static char const *const sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
5895     gchar *const sample_escaped = g_markup_escape_text(sample, -1);
5897     std::stringstream markup;
5898     markup << family_escaped << "  <span foreground='darkgray' font_family='"
5899            << family_escaped << "'>" << sample_escaped << "</span>";
5900     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
5902     g_free(family);
5903     g_free(family_escaped);
5904     g_free(sample_escaped);
5907 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
5908     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
5909     if (completion) {
5910         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
5911         g_object_unref (completion);
5912     }
5915 GtkWidget*
5916 sp_text_toolbox_new (SPDesktop *desktop)
5918     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
5919     GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
5921     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
5922     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
5924     GtkTooltips *tt = gtk_tooltips_new();
5925     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
5927     ////////////Family
5928     //Window
5929     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5930     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
5932     //Entry
5933     GtkWidget           *entry = gtk_entry_new ();
5934     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
5935     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
5936     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
5937     gtk_entry_completion_set_text_column (completion, 0);
5938     gtk_entry_completion_set_minimum_key_length (completion, 1);
5939     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
5940     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
5941     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
5942     gtk_toolbar_append_widget( tbl, entry, "", "" );
5943     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
5945     //Button
5946     GtkWidget   *button = gtk_button_new ();
5947     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
5948     gtk_toolbar_append_widget( tbl, button, "", "");
5950     //Popdown
5951     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
5952     GtkWidget           *treeview = gtk_tree_view_new ();
5954     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
5955     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
5956     gtk_tree_view_column_pack_start (column, cell, FALSE);
5957     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
5958     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5959     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5961     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5962     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5963     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5965     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5967     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5968     gtk_container_add (GTK_CONTAINER (sw), treeview);
5970     gtk_container_add (GTK_CONTAINER (window), sw);
5971     gtk_widget_set_size_request (window, 300, 450);
5973     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5974     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5975     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5977     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5979     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5980     g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5981     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5983     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5984     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5986     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5987     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5988     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5989     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5990     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5992     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
5993     GtkWidget *box = gtk_event_box_new ();
5994     gtk_container_add (GTK_CONTAINER (box), image);
5995     gtk_toolbar_append_widget( tbl, box, "", "");
5996     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5997     GtkTooltips *tooltips = gtk_tooltips_new ();
5998     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5999     gtk_widget_hide (GTK_WIDGET (box));
6000     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
6002     ////////////Size
6003     gchar const *const sizes[] = {
6004         "4", "6", "8", "9", "10", "11", "12", "13", "14",
6005         "16", "18", "20", "22", "24", "28",
6006         "32", "36", "40", "48", "56", "64", "72", "144"
6007     };
6009     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
6010     for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
6011         gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
6012     }
6013     gtk_widget_set_size_request (cbox, 80, -1);
6014     gtk_toolbar_append_widget( tbl, cbox, "", "");
6015     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
6016     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
6017     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
6018     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
6020     ////////////Text anchor
6021     GtkWidget *group   = gtk_radio_button_new (NULL);
6022     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
6023     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
6025     // left
6026     GtkWidget *rbutton = group;
6027     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6028     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
6029     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6031     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6032     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
6033     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
6034     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
6036     // center
6037     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6038     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6039     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
6040     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6042     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6043     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
6044     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
6045     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
6047     // right
6048     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6049     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6050     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
6051     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6053     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6054     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
6055     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
6056     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
6058     // fill
6059     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6060     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6061     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
6062     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6064     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6065     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
6066     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
6067     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
6069     gtk_toolbar_append_widget( tbl, row, "", "");
6071     //spacer
6072     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6074     ////////////Text style
6075     row = gtk_hbox_new (FALSE, 4);
6077     // bold
6078     rbutton = gtk_toggle_button_new ();
6079     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6080     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
6081     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6082     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
6084     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6085     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
6086     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
6088     // italic
6089     rbutton = gtk_toggle_button_new ();
6090     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6091     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
6092     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6093     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
6095     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6096     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
6097     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
6099     gtk_toolbar_append_widget( tbl, row, "", "");
6101     //spacer
6102     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6104     ////////////Text orientation
6105     group   = gtk_radio_button_new (NULL);
6106     row     = gtk_hbox_new (FALSE, 4);
6107     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
6109     // horizontal
6110     rbutton = group;
6111     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6112     gtk_container_add           (GTK_CONTAINER (rbutton),
6113                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
6114     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6115     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
6117     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6118     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
6119     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
6121     // vertical
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),
6125                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
6126     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6127     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
6129     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6130     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
6131     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
6132     gtk_toolbar_append_widget( tbl, row, "", "" );
6135     //watch selection
6136     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
6138     sigc::connection *c_selection_changed =
6139         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
6140                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
6141     pool->add_connection ("selection-changed", c_selection_changed);
6143     sigc::connection *c_selection_modified =
6144         new sigc::connection (sp_desktop_selection (desktop)->connectModified
6145                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
6146     pool->add_connection ("selection-modified", c_selection_modified);
6148     sigc::connection *c_subselection_changed =
6149         new sigc::connection (desktop->connectToolSubselectionChanged
6150                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
6151     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
6153     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
6156     gtk_widget_show_all( GTK_WIDGET(tbl) );
6158     return GTK_WIDGET(tbl);
6159 } // end of sp_text_toolbox_new()
6161 }//<unnamed> namespace
6164 //#########################
6165 //##      Connector      ##
6166 //#########################
6168 static void sp_connector_path_set_avoid(void)
6170     cc_selection_set_avoid(true);
6174 static void sp_connector_path_set_ignore(void)
6176     cc_selection_set_avoid(false);
6181 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
6183     // quit if run by the _changed callbacks
6184     if (g_object_get_data( tbl, "freeze" )) {
6185         return;
6186     }
6188     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
6189     SPDocument *doc = sp_desktop_document(desktop);
6191     if (!sp_document_get_undo_sensitive(doc))
6192     {
6193         return;
6194     }
6196     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6198     if ( repr->attribute("inkscape:connector-spacing") ) {
6199         gdouble priorValue = gtk_adjustment_get_value(adj);
6200         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
6201         if ( priorValue == gtk_adjustment_get_value(adj) ) {
6202             return;
6203         }
6204     } else if ( adj->value == defaultConnSpacing ) {
6205         return;
6206     }
6208     // in turn, prevent callbacks from responding
6209     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6211     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
6212     SP_OBJECT(desktop->namedview)->updateRepr();
6214     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
6215     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
6216         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
6217         NR::Matrix m = NR::identity();
6218         avoid_item_move(&m, item);
6219     }
6221     if (items) {
6222         g_slist_free(items);
6223     }
6225     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
6226             _("Change connector spacing"));
6228     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6230     spinbutton_defocus(GTK_OBJECT(tbl));
6233 static void sp_connector_graph_layout(void)
6235     if (!SP_ACTIVE_DESKTOP) return;
6237     // hack for clones, see comment in align-and-distribute.cpp
6238     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
6239     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
6241     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
6243     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
6245     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
6248 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6250     if ( gtk_toggle_action_get_active( act ) ) {
6251         prefs_set_string_attribute("tools.connector", "directedlayout",
6252                 "true");
6253     } else {
6254         prefs_set_string_attribute("tools.connector", "directedlayout",
6255                 "false");
6256     }
6259 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6261     if ( gtk_toggle_action_get_active( act ) ) {
6262         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
6263                 "true");
6264     } else {
6265         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
6266                 "false");
6267     }
6271 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
6273     prefs_set_double_attribute("tools.connector", "length", adj->value);
6274     spinbutton_defocus(GTK_OBJECT(tbl));
6277 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
6278                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
6279                                             bool /*is_interactive*/, gpointer data)
6281     GtkWidget *tbl = GTK_WIDGET(data);
6283     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6284         return;
6285     }
6286     if (strcmp(name, "inkscape:connector-spacing") != 0) {
6287         return;
6288     }
6290     GtkAdjustment *adj = (GtkAdjustment*)
6291             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
6292     gdouble spacing = defaultConnSpacing;
6293     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
6295     gtk_adjustment_set_value(adj, spacing);
6299 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
6300     NULL, /* child_added */
6301     NULL, /* child_removed */
6302     connector_tb_event_attr_changed,
6303     NULL, /* content_changed */
6304     NULL  /* order_changed */
6305 };
6308 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
6310     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
6312     {
6313         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
6314                                           _("Avoid"),
6315                                           _("Make connectors avoid selected objects"),
6316                                           "connector_avoid",
6317                                           secondarySize );
6318         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
6319         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6320     }
6322     {
6323         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
6324                                           _("Ignore"),
6325                                           _("Make connectors ignore selected objects"),
6326                                           "connector_ignore",
6327                                           secondarySize );
6328         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
6329         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6330     }
6332     EgeAdjustmentAction* eact = 0;
6334     // Spacing spinbox
6335     eact = create_adjustment_action( "ConnectorSpacingAction",
6336                                      _("Connector Spacing"), _("Spacing:"),
6337                                      _("The amount of space left around objects by auto-routing connectors"),
6338                                      "tools.connector", "spacing", defaultConnSpacing,
6339                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
6340                                      0, 100, 1.0, 10.0,
6341                                      0, 0, 0,
6342                                      connector_spacing_changed, 1, 0 );
6343     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6345     // Graph (connector network) layout
6346     {
6347         InkAction* inky = ink_action_new( "ConnectorGraphAction",
6348                                           _("Graph"),
6349                                           _("Nicely arrange selected connector network"),
6350                                           "graph_layout",
6351                                           secondarySize );
6352         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
6353         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6354     }
6356     // Default connector length spinbox
6357     eact = create_adjustment_action( "ConnectorLengthAction",
6358                                      _("Connector Length"), _("Length:"),
6359                                      _("Ideal length for connectors when layout is applied"),
6360                                      "tools.connector", "length", 100,
6361                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
6362                                      10, 1000, 10.0, 100.0,
6363                                      0, 0, 0,
6364                                      connector_length_changed, 1, 0 );
6365     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6368     // Directed edges toggle button
6369     {
6370         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
6371                                                       _("Downwards"),
6372                                                       _("Make connectors with end-markers (arrows) point downwards"),
6373                                                       "directed_graph",
6374                                                       Inkscape::ICON_SIZE_DECORATION );
6375         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6377         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
6378         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6379                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6381         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
6382     }
6384     // Avoid overlaps toggle button
6385     {
6386         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
6387                                                       _("Remove overlaps"),
6388                                                       _("Do not allow overlapping shapes"),
6389                                                       "remove_overlaps",
6390                                                       Inkscape::ICON_SIZE_DECORATION );
6391         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6393         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
6394         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6395                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6397         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
6398     }
6400     // Code to watch for changes to the connector-spacing attribute in
6401     // the XML.
6402     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6403     g_assert(repr != NULL);
6405     purge_repr_listener( holder, holder );
6407     if (repr) {
6408         g_object_set_data( holder, "repr", repr );
6409         Inkscape::GC::anchor(repr);
6410         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
6411         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
6412     }
6413 } // end of sp_connector_toolbox_prep()
6416 //#########################
6417 //##     Paintbucket     ##
6418 //#########################
6420 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
6422     gint channels = ege_select_one_action_get_active( act );
6423     flood_channels_set_channels( channels );
6426 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
6428     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
6431 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
6433     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
6436 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
6438     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
6439     SPUnit const *unit = tracker->getActiveUnit();
6441     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
6443     prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
6446 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
6448     // FIXME: make defaults settable via Inkscape Options
6449     struct KeyValue {
6450         char const *key;
6451         double value;
6452     } const key_values[] = {
6453         {"threshold", 15},
6454         {"offset", 0.0}
6455     };
6457     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
6458         KeyValue const &kv = key_values[i];
6459         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
6460         if ( adj ) {
6461             gtk_adjustment_set_value(adj, kv.value);
6462         }
6463     }
6465     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
6466     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
6467     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
6468     ege_select_one_action_set_active( autogap_action, 0 );
6471 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6473     EgeAdjustmentAction* eact = 0;
6475     {
6476         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6478         GList* items = 0;
6479         gint count = 0;
6480         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
6481         {
6482             GtkTreeIter iter;
6483             gtk_list_store_append( model, &iter );
6484             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6485             count++;
6486         }
6487         g_list_free( items );
6488         items = 0;
6489         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
6490         g_object_set( act1, "short_label", _("Fill by:"), NULL );
6491         ege_select_one_action_set_appearance( act1, "compact" );
6492         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
6493         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
6494         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
6495         g_object_set_data( holder, "channels_action", act1 );
6496     }
6498     // Spacing spinbox
6499     {
6500         eact = create_adjustment_action(
6501             "ThresholdAction",
6502             _("Fill Threshold"), _("Threshold:"),
6503             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
6504             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
6505             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
6506             0, 0, 0,
6507             paintbucket_threshold_changed, 1, 0 );
6509         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6510         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6511     }
6513     // Create the units menu.
6514     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
6515     const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
6516     if (stored_unit)
6517         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
6518     g_object_set_data( holder, "tracker", tracker );
6519     {
6520         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
6521         gtk_action_group_add_action( mainActions, act );
6522     }
6524     // Offset spinbox
6525     {
6526         eact = create_adjustment_action(
6527             "OffsetAction",
6528             _("Grow/shrink by"), _("Grow/shrink by:"),
6529             _("The amount to grow (positive) or shrink (negative) the created fill path"),
6530             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
6531             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
6532             0, 0, 0,
6533             paintbucket_offset_changed, 1, 2);
6534         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
6536         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6537     }
6539     /* Auto Gap */
6540     {
6541         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6543         GList* items = 0;
6544         gint count = 0;
6545         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
6546         {
6547             GtkTreeIter iter;
6548             gtk_list_store_append( model, &iter );
6549             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6550             count++;
6551         }
6552         g_list_free( items );
6553         items = 0;
6554         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
6555         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
6556         ege_select_one_action_set_appearance( act2, "compact" );
6557         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
6558         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
6559         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
6560         g_object_set_data( holder, "autogap_action", act2 );
6561     }
6563     /* Reset */
6564     {
6565         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
6566                                           _("Defaults"),
6567                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
6568                                           GTK_STOCK_CLEAR );
6569         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
6570         gtk_action_group_add_action( mainActions, act );
6571         gtk_action_set_sensitive( act, TRUE );
6572     }
6576 /*
6577   Local Variables:
6578   mode:c++
6579   c-file-style:"stroustrup"
6580   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
6581   indent-tabs-mode:nil
6582   fill-column:99
6583   End:
6584 */
6585 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :