Code

Completed base code to flip toolbox orientation.
[inkscape.git] / src / widgets / toolbox.cpp
1 /** @file
2  * @brief Controls bars for some of Inkscape's tools (for some tools,
3  * they are in their own files)
4  */
5 /* Authors:
6  *   MenTaLguY <mental@rydia.net>
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   bulia byak <buliabyak@users.sf.net>
9  *   Frank Felfe <innerspace@iname.com>
10  *   John Cliff <simarilius@yahoo.com>
11  *   David Turner <novalis@gnu.org>
12  *   Josh Andler <scislac@users.sf.net>
13  *   Jon A. Cruz <jon@joncruz.org>
14  *   Maximilian Albert <maximilian.albert@gmail.com>
15  *
16  * Copyright (C) 2004 David Turner
17  * Copyright (C) 2003 MenTaLguY
18  * Copyright (C) 1999-2008 authors
19  * Copyright (C) 2001-2002 Ximian, Inc.
20  *
21  * Released under GNU GPL, read the file 'COPYING' for more information
22  */
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
28 #include <cstring>
29 #include <string>
31 #include <gtkmm.h>
32 #include <gtk/gtk.h>
33 #include <iostream>
34 #include <sstream>
35 #include <glibmm/i18n.h>
37 #include "../box3d-context.h"
38 #include "../box3d.h"
39 #include "../conn-avoid-ref.h"
40 #include "../connection-pool.h"
41 #include "../connector-context.h"
42 #include "../desktop.h"
43 #include "../desktop-handles.h"
44 #include "../desktop-style.h"
45 #include "../dialogs/dialog-events.h"
46 #include "../dialogs/text-edit.h"
47 #include "../document-private.h"
48 #include "../ege-adjustment-action.h"
49 #include "../ege-output-action.h"
50 #include "../ege-select-one-action.h"
51 #include "../flood-context.h"
52 #include "gradient-toolbar.h"
53 #include "../graphlayout/graphlayout.h"
54 #include "../helper/unit-menu.h"
55 #include "../helper/units.h"
56 #include "../helper/unit-tracker.h"
57 #include "icon.h"
58 #include "../ink-action.h"
59 #include "../inkscape.h"
60 #include "../interface.h"
61 #include "../libnrtype/font-instance.h"
62 #include "../libnrtype/font-lister.h"
63 #include "../live_effects/effect.h"
64 #include "../live_effects/lpe-angle_bisector.h"
65 #include "../live_effects/lpe-line_segment.h"
66 #include "../lpe-tool-context.h"
67 #include "../mod360.h"
68 #include "../node-context.h"
69 #include "../pen-context.h"
70 #include "../preferences.h"
71 #include "../selection-chemistry.h"
72 #include "../selection.h"
73 #include "select-toolbar.h"
74 #include "../shape-editor.h"
75 #include "../shortcuts.h"
76 #include "../sp-clippath.h"
77 #include "../sp-ellipse.h"
78 #include "../sp-flowtext.h"
79 #include "../sp-mask.h"
80 #include "../sp-namedview.h"
81 #include "../sp-rect.h"
82 #include "../sp-spiral.h"
83 #include "../sp-star.h"
84 #include "../sp-text.h"
85 #include "../style.h"
86 #include "../svg/css-ostringstream.h"
87 #include "../tools-switch.h"
88 #include "../tweak-context.h"
89 #include "../spray-context.h"
90 #include "../ui/dialog/calligraphic-profile-rename.h"
91 #include "../ui/icon-names.h"
92 #include "../ui/widget/style-swatch.h"
93 #include "../verbs.h"
94 #include "../widgets/button.h"
95 #include "../widgets/spinbutton-events.h"
96 #include "../widgets/spw-utilities.h"
97 #include "../widgets/widget-sizes.h"
98 #include "../xml/attribute-record.h"
99 #include "../xml/node-event-vector.h"
100 #include "../xml/repr.h"
101 #include "ui/uxmanager.h"
103 #include "toolbox.h"
105 using Inkscape::UnitTracker;
106 using Inkscape::UI::UXManager;
108 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
109 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
111 enum BarId {
112     BAR_TOOL = 0,
113     BAR_AUX,
114     BAR_COMMANDS,
115     BAR_SNAP,
116 };
118 #define BAR_ID_KEY "BarIdValue"
121 static void       sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
122 static void       sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
123 static void       sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
126 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
127 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
128 static void       box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
129 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
130 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
131 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
132 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
133 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
134 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
135 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
136 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
137 static void       sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
138 static void       sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
140 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
143 static void fireTaskChange( EgeSelectOneAction *act, SPDesktop *dt )
145     gint selected = ege_select_one_action_get_active( act );
146     UXManager::getInstance()->setTask(dt, selected);
149 using Inkscape::UI::ToolboxFactory;
152 Inkscape::IconSize ToolboxFactory::prefToSize( Glib::ustring const &path, int base ) {
153     static Inkscape::IconSize sizeChoices[] = {
154         Inkscape::ICON_SIZE_LARGE_TOOLBAR,
155         Inkscape::ICON_SIZE_SMALL_TOOLBAR,
156         Inkscape::ICON_SIZE_MENU
157     };
158     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
159     int index = prefs->getIntLimited( path, base, 0, G_N_ELEMENTS(sizeChoices) );
160     return sizeChoices[index];
163 static struct {
164     gchar const *type_name;
165     gchar const *data_name;
166     sp_verb_t verb;
167     sp_verb_t doubleclick_verb;
168 } const tools[] = {
169     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
170     { "SPNodeContext",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
171     { "SPTweakContext",    "tweak_tool",     SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
172     { "SPSprayContext",    "spray_tool",     SP_VERB_CONTEXT_SPRAY, SP_VERB_CONTEXT_SPRAY_PREFS },
173     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
174     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
175     { "Box3DContext",      "3dbox_tool",     SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
176     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
177     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
178     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
179     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
180     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
181     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
182     { "SPLPEToolContext",  "lpetool_tool",   SP_VERB_CONTEXT_LPETOOL, SP_VERB_CONTEXT_LPETOOL_PREFS },
183     { "SPEraserContext",   "eraser_tool",    SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
184     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
185     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
186     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
187     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
188     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
189     { NULL, NULL, 0, 0 }
190 };
192 static struct {
193     gchar const *type_name;
194     gchar const *data_name;
195     GtkWidget *(*create_func)(SPDesktop *desktop);
196     void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
197     gchar const *ui_name;
198     gint swatch_verb_id;
199     gchar const *swatch_tool;
200     gchar const *swatch_tip;
201 } const aux_toolboxes[] = {
202     { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep,            "SelectToolbar",
203       SP_VERB_INVALID, 0, 0},
204     { "SPNodeContext",   "node_toolbox",   0, sp_node_toolbox_prep,              "NodeToolbar",
205       SP_VERB_INVALID, 0, 0},
206     { "SPTweakContext",   "tweak_toolbox",   0, sp_tweak_toolbox_prep,              "TweakToolbar",
207       SP_VERB_CONTEXT_TWEAK_PREFS, "/tools/tweak", N_("Color/opacity used for color tweaking")},
208     { "SPSprayContext",   "spray_toolbox",   0, sp_spray_toolbox_prep,              "SprayToolbar",
209       SP_VERB_CONTEXT_SPRAY_PREFS, "/tools/spray", N_("Color/opacity used for color spraying")},
210     { "SPZoomContext",   "zoom_toolbox",   0, sp_zoom_toolbox_prep,              "ZoomToolbar",
211       SP_VERB_INVALID, 0, 0},
212     { "SPStarContext",   "star_toolbox",   0, sp_star_toolbox_prep,              "StarToolbar",
213       SP_VERB_CONTEXT_STAR_PREFS,   "/tools/shapes/star",     N_("Style of new stars")},
214     { "SPRectContext",   "rect_toolbox",   0, sp_rect_toolbox_prep,              "RectToolbar",
215       SP_VERB_CONTEXT_RECT_PREFS,   "/tools/shapes/rect",     N_("Style of new rectangles")},
216     { "Box3DContext",  "3dbox_toolbox",  0, box3d_toolbox_prep,             "3DBoxToolbar",
217       SP_VERB_CONTEXT_3DBOX_PREFS,  "/tools/shapes/3dbox",    N_("Style of new 3D boxes")},
218     { "SPArcContext",    "arc_toolbox",    0, sp_arc_toolbox_prep,               "ArcToolbar",
219       SP_VERB_CONTEXT_ARC_PREFS,    "/tools/shapes/arc",      N_("Style of new ellipses")},
220     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
221       SP_VERB_CONTEXT_SPIRAL_PREFS, "/tools/shapes/spiral",   N_("Style of new spirals")},
222     { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep,            "PencilToolbar",
223       SP_VERB_CONTEXT_PENCIL_PREFS, "/tools/freehand/pencil", N_("Style of new paths created by Pencil")},
224     { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep,                     "PenToolbar",
225       SP_VERB_CONTEXT_PEN_PREFS,    "/tools/freehand/pen",    N_("Style of new paths created by Pen")},
226     { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
227       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "/tools/calligraphic", N_("Style of new calligraphic strokes")},
228     { "SPEraserContext", "eraser_toolbox", 0, sp_eraser_toolbox_prep,"EraserToolbar",
229       SP_VERB_CONTEXT_ERASER_PREFS, "/tools/eraser", _("TBD")},
230     { "SPLPEToolContext", "lpetool_toolbox", 0, sp_lpetool_toolbox_prep, "LPEToolToolbar",
231       SP_VERB_CONTEXT_LPETOOL_PREFS, "/tools/lpetool", _("TBD")},
232     { "SPTextContext",   "text_toolbox",   sp_text_toolbox_new, 0,               0,
233       SP_VERB_INVALID, 0, 0},
234     { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep,         "DropperToolbar",
235       SP_VERB_INVALID, 0, 0},
236     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0,       0,
237       SP_VERB_INVALID, 0, 0},
238     { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep,   "ConnectorToolbar",
239       SP_VERB_INVALID, 0, 0},
240     { "SPFloodContext",  "paintbucket_toolbox",  0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
241       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "/tools/paintbucket", N_("Style of Paint Bucket fill objects")},
242     { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
243 };
245 #define TOOLBAR_SLIDER_HINT "full"
247 static gchar const * ui_descr =
248         "<ui>"
249         "  <toolbar name='SelectToolbar'>"
250         "    <toolitem action='EditSelectAll' />"
251         "    <toolitem action='EditSelectAllInAllLayers' />"
252         "    <toolitem action='EditDeselect' />"
253         "    <separator />"
254         "    <toolitem action='ObjectRotate90CCW' />"
255         "    <toolitem action='ObjectRotate90' />"
256         "    <toolitem action='ObjectFlipHorizontally' />"
257         "    <toolitem action='ObjectFlipVertically' />"
258         "    <separator />"
259         "    <toolitem action='SelectionToBack' />"
260         "    <toolitem action='SelectionLower' />"
261         "    <toolitem action='SelectionRaise' />"
262         "    <toolitem action='SelectionToFront' />"
263         "    <separator />"
264         "    <toolitem action='XAction' />"
265         "    <toolitem action='YAction' />"
266         "    <toolitem action='WidthAction' />"
267         "    <toolitem action='LockAction' />"
268         "    <toolitem action='HeightAction' />"
269         "    <toolitem action='UnitsAction' />"
270         "    <separator />"
271         "    <toolitem action='transform_affect_label' />"
272         "    <toolitem action='transform_stroke' />"
273         "    <toolitem action='transform_corners' />"
274         "    <toolitem action='transform_gradient' />"
275         "    <toolitem action='transform_pattern' />"
276         "  </toolbar>"
278         "  <toolbar name='NodeToolbar'>"
279         "    <toolitem action='NodeInsertAction' />"
280         "    <toolitem action='NodeDeleteAction' />"
281         "    <separator />"
282         "    <toolitem action='NodeJoinAction' />"
283         "    <toolitem action='NodeBreakAction' />"
284         "    <separator />"
285         "    <toolitem action='NodeJoinSegmentAction' />"
286         "    <toolitem action='NodeDeleteSegmentAction' />"
287         "    <separator />"
288         "    <toolitem action='NodeCuspAction' />"
289         "    <toolitem action='NodeSmoothAction' />"
290         "    <toolitem action='NodeSymmetricAction' />"
291         "    <toolitem action='NodeAutoAction' />"
292         "    <separator />"
293         "    <toolitem action='NodeLineAction' />"
294         "    <toolitem action='NodeCurveAction' />"
295         "    <separator />"
296         "    <toolitem action='ObjectToPath' />"
297         "    <toolitem action='StrokeToPath' />"
298         "    <separator />"
299         "    <toolitem action='NodeXAction' />"
300         "    <toolitem action='NodeYAction' />"
301         "    <toolitem action='NodeUnitsAction' />"
302         "    <separator />"
303         "    <toolitem action='ObjectEditClipPathAction' />"
304         "    <toolitem action='ObjectEditMaskPathAction' />"
305         "    <toolitem action='EditNextLPEParameterAction' />"
306         "    <separator />"
307         "    <toolitem action='NodesShowHandlesAction' />"
308         "    <toolitem action='NodesShowHelperpath' />"
309         "  </toolbar>"
311         "  <toolbar name='TweakToolbar'>"
312         "    <toolitem action='TweakWidthAction' />"
313         "    <separator />"
314         "    <toolitem action='TweakForceAction' />"
315         "    <toolitem action='TweakPressureAction' />"
316         "    <separator />"
317         "    <toolitem action='TweakModeAction' />"
318         "    <separator />"
319         "    <toolitem action='TweakFidelityAction' />"
320         "    <separator />"
321         "    <toolitem action='TweakChannelsLabel' />"
322         "    <toolitem action='TweakDoH' />"
323         "    <toolitem action='TweakDoS' />"
324         "    <toolitem action='TweakDoL' />"
325         "    <toolitem action='TweakDoO' />"
326         "  </toolbar>"
328         "  <toolbar name='SprayToolbar'>"
329         "    <toolitem action='SprayModeAction' />"
330         "    <separator />"
331         "    <separator />"
332         "    <toolitem action='SprayWidthAction' />"
333         "    <toolitem action='SprayPressureAction' />"
334         "    <toolitem action='SprayPopulationAction' />"
335         "    <separator />"
336         "    <toolitem action='SprayRotationAction' />"
337         "    <toolitem action='SprayScaleAction' />"
338         "    <separator />"
339         "    <toolitem action='SprayStandard_deviationAction' />"
340         "    <toolitem action='SprayMeanAction' />"
341         "  </toolbar>"
343         "  <toolbar name='ZoomToolbar'>"
344         "    <toolitem action='ZoomIn' />"
345         "    <toolitem action='ZoomOut' />"
346         "    <separator />"
347         "    <toolitem action='Zoom1:0' />"
348         "    <toolitem action='Zoom1:2' />"
349         "    <toolitem action='Zoom2:1' />"
350         "    <separator />"
351         "    <toolitem action='ZoomSelection' />"
352         "    <toolitem action='ZoomDrawing' />"
353         "    <toolitem action='ZoomPage' />"
354         "    <toolitem action='ZoomPageWidth' />"
355         "    <separator />"
356         "    <toolitem action='ZoomPrev' />"
357         "    <toolitem action='ZoomNext' />"
358         "  </toolbar>"
360         "  <toolbar name='StarToolbar'>"
361         "    <separator />"
362         "    <toolitem action='StarStateAction' />"
363         "    <separator />"
364         "    <toolitem action='FlatAction' />"
365         "    <separator />"
366         "    <toolitem action='MagnitudeAction' />"
367         "    <toolitem action='SpokeAction' />"
368         "    <toolitem action='RoundednessAction' />"
369         "    <toolitem action='RandomizationAction' />"
370         "    <separator />"
371         "    <toolitem action='StarResetAction' />"
372         "  </toolbar>"
374         "  <toolbar name='RectToolbar'>"
375         "    <toolitem action='RectStateAction' />"
376         "    <toolitem action='RectWidthAction' />"
377         "    <toolitem action='RectHeightAction' />"
378         "    <toolitem action='RadiusXAction' />"
379         "    <toolitem action='RadiusYAction' />"
380         "    <toolitem action='RectUnitsAction' />"
381         "    <separator />"
382         "    <toolitem action='RectResetAction' />"
383         "  </toolbar>"
385         "  <toolbar name='3DBoxToolbar'>"
386         "    <toolitem action='3DBoxAngleXAction' />"
387         "    <toolitem action='3DBoxVPXStateAction' />"
388         "    <separator />"
389         "    <toolitem action='3DBoxAngleYAction' />"
390         "    <toolitem action='3DBoxVPYStateAction' />"
391         "    <separator />"
392         "    <toolitem action='3DBoxAngleZAction' />"
393         "    <toolitem action='3DBoxVPZStateAction' />"
394         "  </toolbar>"
396         "  <toolbar name='SpiralToolbar'>"
397         "    <toolitem action='SpiralStateAction' />"
398         "    <toolitem action='SpiralRevolutionAction' />"
399         "    <toolitem action='SpiralExpansionAction' />"
400         "    <toolitem action='SpiralT0Action' />"
401         "    <separator />"
402         "    <toolitem action='SpiralResetAction' />"
403         "  </toolbar>"
405         "  <toolbar name='PenToolbar'>"
406         "    <toolitem action='FreehandModeActionPen' />"
407         "    <separator />"
408         "    <toolitem action='SetPenShapeAction'/>"
409         "  </toolbar>"
411         "  <toolbar name='PencilToolbar'>"
412         "    <toolitem action='FreehandModeActionPencil' />"
413         "    <separator />"
414         "    <toolitem action='PencilToleranceAction' />"
415         "    <separator />"
416         "    <toolitem action='PencilResetAction' />"
417         "    <separator />"
418         "    <toolitem action='SetPencilShapeAction'/>"
419         "  </toolbar>"
421         "  <toolbar name='CalligraphyToolbar'>"
422         "    <separator />"
423         "    <toolitem action='SetProfileAction'/>"
424         "    <separator />"
425         "    <toolitem action='CalligraphyWidthAction' />"
426         "    <toolitem action='PressureAction' />"
427         "    <toolitem action='TraceAction' />"
428         "    <toolitem action='ThinningAction' />"
429         "    <separator />"
430         "    <toolitem action='AngleAction' />"
431         "    <toolitem action='TiltAction' />"
432         "    <toolitem action='FixationAction' />"
433         "    <separator />"
434         "    <toolitem action='CapRoundingAction' />"
435         "    <separator />"
436         "    <toolitem action='TremorAction' />"
437         "    <toolitem action='WiggleAction' />"
438         "    <toolitem action='MassAction' />"
439         "    <separator />"
440         "  </toolbar>"
442         "  <toolbar name='ArcToolbar'>"
443         "    <toolitem action='ArcStateAction' />"
444         "    <separator />"
445         "    <toolitem action='ArcStartAction' />"
446         "    <toolitem action='ArcEndAction' />"
447         "    <separator />"
448         "    <toolitem action='ArcOpenAction' />"
449         "    <separator />"
450         "    <toolitem action='ArcResetAction' />"
451         "    <separator />"
452         "  </toolbar>"
454         "  <toolbar name='PaintbucketToolbar'>"
455         "    <toolitem action='ChannelsAction' />"
456         "    <separator />"
457         "    <toolitem action='ThresholdAction' />"
458         "    <separator />"
459         "    <toolitem action='OffsetAction' />"
460         "    <toolitem action='PaintbucketUnitsAction' />"
461         "    <separator />"
462         "    <toolitem action='AutoGapAction' />"
463         "    <separator />"
464         "    <toolitem action='PaintbucketResetAction' />"
465         "  </toolbar>"
467         "  <toolbar name='EraserToolbar'>"
468         "    <toolitem action='EraserWidthAction' />"
469         "    <separator />"
470         "    <toolitem action='EraserModeAction' />"
471         "  </toolbar>"
473         "  <toolbar name='LPEToolToolbar'>"
474         "    <toolitem action='LPEToolModeAction' />"
475         "    <separator />"
476         "    <toolitem action='LPEShowBBoxAction' />"
477         "    <toolitem action='LPEBBoxFromSelectionAction' />"
478         "    <separator />"
479         "    <toolitem action='LPELineSegmentAction' />"
480         "    <separator />"
481         "    <toolitem action='LPEMeasuringAction' />"
482         "    <toolitem action='LPEToolUnitsAction' />"
483         "    <separator />"
484         "    <toolitem action='LPEOpenLPEDialogAction' />"
485         "  </toolbar>"
487         "  <toolbar name='DropperToolbar'>"
488         "    <toolitem action='DropperOpacityAction' />"
489         "    <toolitem action='DropperPickAlphaAction' />"
490         "    <toolitem action='DropperSetAlphaAction' />"
491         "  </toolbar>"
493         "  <toolbar name='ConnectorToolbar'>"
494         "    <toolitem action='ConnectorEditModeAction' />"
495         "    <toolitem action='ConnectorAvoidAction' />"
496         "    <toolitem action='ConnectorIgnoreAction' />"
497         "    <toolitem action='ConnectorOrthogonalAction' />"
498         "    <toolitem action='ConnectorCurvatureAction' />"
499         "    <toolitem action='ConnectorSpacingAction' />"
500         "    <toolitem action='ConnectorGraphAction' />"
501         "    <toolitem action='ConnectorLengthAction' />"
502         "    <toolitem action='ConnectorDirectedAction' />"
503         "    <toolitem action='ConnectorOverlapAction' />"
504         "    <toolitem action='ConnectorNewConnPointAction' />"
505         "    <toolitem action='ConnectorRemoveConnPointAction' />"
506         "  </toolbar>"
508         "</ui>"
511 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
513 void setup_snap_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
515 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
516 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
518 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
519 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
521 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
522 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
524 GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick( GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
525                                                               Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
526                                                               Inkscape::UI::View::View *view, GtkTooltips *tt);
528 class VerbAction : public Gtk::Action {
529 public:
530     static Glib::RefPtr<VerbAction> create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
532     virtual ~VerbAction();
533     virtual void set_active(bool active = true);
535 protected:
536     virtual Gtk::Widget* create_menu_item_vfunc();
537     virtual Gtk::Widget* create_tool_item_vfunc();
539     virtual void connect_proxy_vfunc(Gtk::Widget* proxy);
540     virtual void disconnect_proxy_vfunc(Gtk::Widget* proxy);
542     virtual void on_activate();
544 private:
545     Inkscape::Verb* verb;
546     Inkscape::Verb* verb2;
547     Inkscape::UI::View::View *view;
548     GtkTooltips *tooltips;
549     bool active;
551     VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
552 };
555 Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips)
557     Glib::RefPtr<VerbAction> result;
558     SPAction *action = verb->get_action(view);
559     if ( action ) {
560         //SPAction* action2 = verb2 ? verb2->get_action(view) : 0;
561         result = Glib::RefPtr<VerbAction>(new VerbAction(verb, verb2, view, tooltips));
562     }
564     return result;
567 VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips) :
568     Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(verb->get_image()), Glib::ustring(_(verb->get_name())), Glib::ustring(_(verb->get_tip()))),
569     verb(verb),
570     verb2(verb2),
571     view(view),
572     tooltips(tooltips),
573     active(false)
577 VerbAction::~VerbAction()
581 Gtk::Widget* VerbAction::create_menu_item_vfunc()
583 // First call in to get the icon rendered if present in SVG
584     Gtk::Widget *widget = sp_icon_get_icon( property_stock_id().get_value().get_string(), Inkscape::ICON_SIZE_MENU );
585     delete widget;
586     widget = 0;
588     Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
589 //     g_message("create_menu_item_vfunc() = %p  for '%s'", widg, verb->get_id());
590     return widg;
593 Gtk::Widget* VerbAction::create_tool_item_vfunc()
595 //     Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
596     Inkscape::IconSize toolboxSize = ToolboxFactory::prefToSize("/toolbox/tools/small");
597     GtkWidget* toolbox = 0;
598     GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
599                                                                           SP_BUTTON_TYPE_TOGGLE,
600                                                                           verb,
601                                                                           verb2,
602                                                                           view,
603                                                                           tooltips );
604     if ( active ) {
605         sp_button_toggle_set_down( SP_BUTTON(button), active);
606     }
607     gtk_widget_show_all( button );
608     Gtk::Widget* wrapped = Glib::wrap(button);
609     Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
610     holder->add(*wrapped);
612 //     g_message("create_tool_item_vfunc() = %p  for '%s'", holder, verb->get_id());
613     return holder;
616 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
618 //     g_message("connect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
619     Gtk::Action::connect_proxy_vfunc(proxy);
622 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
624 //     g_message("disconnect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
625     Gtk::Action::disconnect_proxy_vfunc(proxy);
628 void VerbAction::set_active(bool active)
630     this->active = active;
631     Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
632     for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
633         Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
634         if (ti) {
635             // *should* have one child that is the SPButton
636             Gtk::Widget* child = ti->get_child();
637             if ( child && SP_IS_BUTTON(child->gobj()) ) {
638                 SPButton* button = SP_BUTTON(child->gobj());
639                 sp_button_toggle_set_down( button, active );
640             }
641         }
642     }
645 void VerbAction::on_activate()
647     if ( verb ) {
648         SPAction *action = verb->get_action(view);
649         if ( action ) {
650             sp_action_perform(action, 0);
651         }
652     }
655 /* Global text entry widgets necessary for update */
656 /* GtkWidget *dropper_rgb_entry,
657           *dropper_opacity_entry ; */
658 // should be made a private member once this is converted to class
660 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
661     connection->disconnect();
662     delete connection;
665 static void purge_repr_listener( GObject* obj, GObject* tbl )
667     (void)obj;
668     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
669     if (oldrepr) { // remove old listener
670         sp_repr_remove_listener_by_data(oldrepr, tbl);
671         Inkscape::GC::release(oldrepr);
672         oldrepr = 0;
673         g_object_set_data( tbl, "repr", NULL );
674     }
677 GtkWidget *
678 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
679                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
680                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
682     SPAction *action = verb->get_action(view);
683     if (!action) return NULL;
685     SPAction *doubleclick_action;
686     if (doubleclick_verb)
687         doubleclick_action = doubleclick_verb->get_action(view);
688     else
689         doubleclick_action = NULL;
691     /* fixme: Handle sensitive/unsensitive */
692     /* fixme: Implement sp_button_new_from_action */
693     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
694     gtk_widget_show(b);
697     unsigned int shortcut = sp_shortcut_get_primary(verb);
698     if (shortcut) {
699         gchar key[256];
700         sp_ui_shortcut_string(shortcut, key);
701         gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
702         if ( t ) {
703             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
704         }
705         g_free(tip);
706     } else {
707         if ( t ) {
708             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
709         }
710     }
712     return b;
716 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
718     SPAction* targetAction = SP_ACTION(user_data);
719     if ( targetAction ) {
720         sp_action_perform( targetAction, NULL );
721     }
724 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
726     if ( data ) {
727         GtkAction* act = GTK_ACTION(data);
728         gtk_action_set_sensitive( act, sensitive );
729     }
732 static SPActionEventVector action_event_vector = {
733     {NULL},
734     NULL,
735     NULL,
736     sp_action_action_set_sensitive,
737     NULL,
738     NULL
739 };
741 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
743     GtkAction* act = 0;
745     SPAction* targetAction = verb->get_action(view);
746     InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size  );
747     act = GTK_ACTION(inky);
748     gtk_action_set_sensitive( act, targetAction->sensitive );
750     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
752     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
753     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
755     return act;
758 Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
760     Inkscape::UI::View::View *view = desktop;
761     gint verbsToUse[] = {
762         // disabled until we have icons for them:
763         //find
764         //SP_VERB_EDIT_TILE,
765         //SP_VERB_EDIT_UNTILE,
766         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
767         SP_VERB_DIALOG_DISPLAY,
768         SP_VERB_DIALOG_FILL_STROKE,
769         SP_VERB_DIALOG_NAMEDVIEW,
770         SP_VERB_DIALOG_TEXT,
771         SP_VERB_DIALOG_XML_EDITOR,
772         SP_VERB_DIALOG_LAYERS,
773         SP_VERB_EDIT_CLONE,
774         SP_VERB_EDIT_COPY,
775         SP_VERB_EDIT_CUT,
776         SP_VERB_EDIT_DUPLICATE,
777         SP_VERB_EDIT_PASTE,
778         SP_VERB_EDIT_REDO,
779         SP_VERB_EDIT_UNDO,
780         SP_VERB_EDIT_UNLINK_CLONE,
781         SP_VERB_FILE_EXPORT,
782         SP_VERB_FILE_IMPORT,
783         SP_VERB_FILE_NEW,
784         SP_VERB_FILE_OPEN,
785         SP_VERB_FILE_PRINT,
786         SP_VERB_FILE_SAVE,
787         SP_VERB_OBJECT_TO_CURVE,
788         SP_VERB_SELECTION_GROUP,
789         SP_VERB_SELECTION_OUTLINE,
790         SP_VERB_SELECTION_UNGROUP,
791         SP_VERB_ZOOM_1_1,
792         SP_VERB_ZOOM_1_2,
793         SP_VERB_ZOOM_2_1,
794         SP_VERB_ZOOM_DRAWING,
795         SP_VERB_ZOOM_IN,
796         SP_VERB_ZOOM_NEXT,
797         SP_VERB_ZOOM_OUT,
798         SP_VERB_ZOOM_PAGE,
799         SP_VERB_ZOOM_PAGE_WIDTH,
800         SP_VERB_ZOOM_PREV,
801         SP_VERB_ZOOM_SELECTION,
802     };
804     Inkscape::IconSize toolboxSize = ToolboxFactory::prefToSize("/toolbox/small");
806     static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
807     Glib::RefPtr<Gtk::ActionGroup> mainActions;
808     if ( groups.find(desktop) != groups.end() ) {
809         mainActions = groups[desktop];
810     }
812     if ( !mainActions ) {
813         mainActions = Gtk::ActionGroup::create("main");
814         groups[desktop] = mainActions;
815     }
817     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
818         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
819         if ( verb ) {
820             if (!mainActions->get_action(verb->get_id())) {
821                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
822                 mainActions->add(Glib::wrap(act));
823             }
824         }
825     }
827     if ( !mainActions->get_action("ToolZoom") ) {
828         GtkTooltips *tt = gtk_tooltips_new();
829         for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
830             Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
831             if ( va ) {
832                 mainActions->add(va);
833                 if ( i == 0 ) {
834                     va->set_active(true);
835                 }
836             }
837         }
838     }
840     if ( !mainActions->get_action("TaskSetAction") ) {
841         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
843         GtkTreeIter iter;
844         gtk_list_store_append( model, &iter );
845         gtk_list_store_set( model, &iter,
846                             0, _("Default"),
847                             1, _("Default interface setup"),
848                             2, 0,
849                             -1 );
851         gtk_list_store_append( model, &iter );
852         gtk_list_store_set( model, &iter,
853                             0, _("Custom"),
854                             1, _("Set the custom task"),
855                             2, 0,
856                             -1 );
858         EgeSelectOneAction* act = ege_select_one_action_new( "TaskSetAction", _("Task"), (""), NULL, GTK_TREE_MODEL(model) );
859         g_object_set( act, "short_label", _("Task:"), NULL );
860         mainActions->add(Glib::wrap(GTK_ACTION(act)));
861         //g_object_set_data( holder, "mode_action", act );
863         ege_select_one_action_set_appearance( act, "minimal" );
864         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
865         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
866         ege_select_one_action_set_icon_column( act, 2 );
867         //ege_select_one_action_set_icon_size( act, secondarySize );
868         ege_select_one_action_set_tooltip_column( act, 1  );
870         //ege_select_one_action_set_active( act, mode );
871         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(fireTaskChange), desktop );
872     }
874     return mainActions;
878 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
880     gtk_widget_set_size_request( widget,
881                                  widget->allocation.width,
882                                  widget->allocation.height );
885 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
887     gtk_widget_set_size_request( widget, -1, -1 );
890 static GtkWidget* toolboxNewCommon( GtkWidget* tb, BarId id, GtkPositionType handlePos )
892     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
894     gtk_widget_set_sensitive(tb, FALSE);
896     GtkWidget *hb = gtk_handle_box_new();
897     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), handlePos);
898     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
899     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
901     gtk_container_add(GTK_CONTAINER(hb), tb);
902     gtk_widget_show(GTK_WIDGET(tb));
904     sigc::connection* conn = new sigc::connection;
905     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
907     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
908     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
910     gpointer val = GINT_TO_POINTER(id);
911     g_object_set_data(G_OBJECT(hb), BAR_ID_KEY, val);
913     return hb;
916 GtkWidget *ToolboxFactory::createToolToolbox()
918     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
920     return toolboxNewCommon( tb, BAR_TOOL, GTK_POS_TOP );
923 GtkWidget *ToolboxFactory::createAuxToolbox()
925     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
927     return toolboxNewCommon( tb, BAR_AUX, GTK_POS_LEFT );
930 //####################################
931 //# Commands Bar
932 //####################################
934 GtkWidget *ToolboxFactory::createCommandsToolbox()
936     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
938     return toolboxNewCommon( tb, BAR_COMMANDS, GTK_POS_LEFT );
941 GtkWidget *ToolboxFactory::createSnapToolbox()
943     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
945     return toolboxNewCommon( tb, BAR_SNAP, GTK_POS_LEFT );
948 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
949                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
950                                                        Glib::ustring const &path, gdouble def,
951                                                        GtkWidget *focusTarget,
952                                                        GtkWidget *us,
953                                                        GObject *dataKludge,
954                                                        gboolean altx, gchar const *altx_mark,
955                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
956                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
957                                                        void (*callback)(GtkAdjustment *, GObject *),
958                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
960     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
961     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs->getDouble(path, def) * factor,
962                                                              lower, upper, step, page, 0 ) );
963     if (us) {
964         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
965     }
967     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
969     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
970     if ( shortLabel ) {
971         g_object_set( act, "short_label", shortLabel, NULL );
972     }
974     if ( (descrCount > 0) && descrLabels && descrValues ) {
975         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
976     }
978     if ( focusTarget ) {
979         ege_adjustment_action_set_focuswidget( act, focusTarget );
980     }
982     if ( altx && altx_mark ) {
983         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
984     }
986     if ( dataKludge ) {
987         // Rather lame, but it's the only place where we need to get the entry name
988         // but we don't have an Entry
989         g_object_set_data( dataKludge, prefs->getEntry(path).getEntryName().data(), adj );
990     }
992     // Using a cast just to make sure we pass in the right kind of function pointer
993     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
995     return act;
999 //####################################
1000 //# node editing callbacks
1001 //####################################
1003 /**
1004  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
1005  */
1006 static ShapeEditor *get_current_shape_editor()
1008     if (!SP_ACTIVE_DESKTOP) {
1009         return NULL;
1010     }
1012     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
1014     if (!SP_IS_NODE_CONTEXT(event_context)) {
1015         return NULL;
1016     }
1018     return event_context->shape_editor;
1022 void
1023 sp_node_path_edit_add(void)
1025     ShapeEditor *shape_editor = get_current_shape_editor();
1026     if (shape_editor) shape_editor->add_node();
1029 void
1030 sp_node_path_edit_delete(void)
1032     ShapeEditor *shape_editor = get_current_shape_editor();
1033     if (shape_editor) shape_editor->delete_nodes_preserving_shape();
1036 void
1037 sp_node_path_edit_delete_segment(void)
1039     ShapeEditor *shape_editor = get_current_shape_editor();
1040     if (shape_editor) shape_editor->delete_segment();
1043 void
1044 sp_node_path_edit_break(void)
1046     ShapeEditor *shape_editor = get_current_shape_editor();
1047     if (shape_editor) shape_editor->break_at_nodes();
1050 void
1051 sp_node_path_edit_join(void)
1053     ShapeEditor *shape_editor = get_current_shape_editor();
1054     if (shape_editor) shape_editor->join_nodes();
1057 void
1058 sp_node_path_edit_join_segment(void)
1060     ShapeEditor *shape_editor = get_current_shape_editor();
1061     if (shape_editor) shape_editor->join_segments();
1064 void
1065 sp_node_path_edit_toline(void)
1067     ShapeEditor *shape_editor = get_current_shape_editor();
1068     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1071 void
1072 sp_node_path_edit_tocurve(void)
1074     ShapeEditor *shape_editor = get_current_shape_editor();
1075     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1078 void
1079 sp_node_path_edit_cusp(void)
1081     ShapeEditor *shape_editor = get_current_shape_editor();
1082     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1085 void
1086 sp_node_path_edit_smooth(void)
1088     ShapeEditor *shape_editor = get_current_shape_editor();
1089     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1092 void
1093 sp_node_path_edit_symmetrical(void)
1095     ShapeEditor *shape_editor = get_current_shape_editor();
1096     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1099 void
1100 sp_node_path_edit_auto(void)
1102     ShapeEditor *shape_editor = get_current_shape_editor();
1103     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_AUTO);
1106 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1107     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1108     bool show = gtk_toggle_action_get_active( act );
1109     prefs->setBool("/tools/nodes/show_handles",  show);
1110     ShapeEditor *shape_editor = get_current_shape_editor();
1111     if (shape_editor) shape_editor->show_handles(show);
1114 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1115     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1116     bool show = gtk_toggle_action_get_active( act );
1117     prefs->setBool("/tools/nodes/show_helperpath",  show);
1118     ShapeEditor *shape_editor = get_current_shape_editor();
1119     if (shape_editor) shape_editor->show_helperpath(show);
1122 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1123     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1126 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1127     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1130 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1131     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1134 /* is called when the node selection is modified */
1135 static void
1136 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1138     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1139     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1140     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1141     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1143     // quit if run by the attr_changed listener
1144     if (g_object_get_data( tbl, "freeze" )) {
1145         return;
1146     }
1148     // in turn, prevent listener from responding
1149     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1151     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1152     SPUnit const *unit = tracker->getActiveUnit();
1154     ShapeEditor *shape_editor = get_current_shape_editor();
1155     if (shape_editor && shape_editor->has_nodepath()) {
1156         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1157         int n_selected = 0;
1158         if (nodepath) {
1159             n_selected = nodepath->numSelected();
1160         }
1162         if (n_selected == 0) {
1163             gtk_action_set_sensitive(xact, FALSE);
1164             gtk_action_set_sensitive(yact, FALSE);
1165         } else {
1166             gtk_action_set_sensitive(xact, TRUE);
1167             gtk_action_set_sensitive(yact, TRUE);
1168             Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1169             Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1171             if (n_selected == 1) {
1172                 Geom::Point sel_node = nodepath->singleSelectedCoords();
1173                 if (oldx != sel_node[Geom::X] || oldy != sel_node[Geom::Y]) {
1174                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[Geom::X], *unit));
1175                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[Geom::Y], *unit));
1176                 }
1177             } else {
1178                 boost::optional<Geom::Coord> x = sp_node_selected_common_coord(nodepath, Geom::X);
1179                 boost::optional<Geom::Coord> y = sp_node_selected_common_coord(nodepath, Geom::Y);
1180                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1181                     /* Note: Currently x and y will always have a value, even if the coordinates of the
1182                        selected nodes don't coincide (in this case we use the coordinates of the center
1183                        of the bounding box). So the entries are never set to zero. */
1184                     // FIXME: Maybe we should clear the entry if several nodes are selected
1185                     //        instead of providing a kind of average value
1186                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1187                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1188                 }
1189             }
1190         }
1191     } else {
1192         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1193         gtk_action_set_sensitive(xact, FALSE);
1194         gtk_action_set_sensitive(yact, FALSE);
1195     }
1197     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1200 static void
1201 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1203     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1204     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1206     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1207     SPUnit const *unit = tracker->getActiveUnit();
1209     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1210         prefs->setDouble(Glib::ustring("/tools/nodes/") + value_name, sp_units_get_pixels(adj->value, *unit));
1211     }
1213     // quit if run by the attr_changed listener
1214     if (g_object_get_data( tbl, "freeze" )) {
1215         return;
1216     }
1218     // in turn, prevent listener from responding
1219     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1221     ShapeEditor *shape_editor = get_current_shape_editor();
1222     if (shape_editor && shape_editor->has_nodepath()) {
1223         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1224         if (!strcmp(value_name, "x")) {
1225             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::X);
1226         }
1227         if (!strcmp(value_name, "y")) {
1228             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::Y);
1229         }
1230     }
1232     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1235 static void
1236 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1238     sp_node_path_value_changed(adj, tbl, "x");
1241 static void
1242 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1244     sp_node_path_value_changed(adj, tbl, "y");
1247 void
1248 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1250     {
1251     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1252     SPItem *item = selection->singleItem();
1253     if (item && SP_IS_LPE_ITEM(item)) {
1254        if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1255            gtk_action_set_sensitive(w, TRUE);
1256        } else {
1257            gtk_action_set_sensitive(w, FALSE);
1258        }
1259     } else {
1260        gtk_action_set_sensitive(w, FALSE);
1261     }
1262     }
1264     {
1265     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1266     SPItem *item = selection->singleItem();
1267     if (item && item->clip_ref && item->clip_ref->getObject()) {
1268        gtk_action_set_sensitive(w, TRUE);
1269     } else {
1270        gtk_action_set_sensitive(w, FALSE);
1271     }
1272     }
1274     {
1275     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1276     SPItem *item = selection->singleItem();
1277     if (item && item->mask_ref && item->mask_ref->getObject()) {
1278        gtk_action_set_sensitive(w, TRUE);
1279     } else {
1280        gtk_action_set_sensitive(w, FALSE);
1281     }
1282     }
1285 void
1286 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1288     sp_node_toolbox_sel_changed (selection, tbl);
1293 //################################
1294 //##    Node Editing Toolbox    ##
1295 //################################
1297 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1299     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1300     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1301     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1302     g_object_set_data( holder, "tracker", tracker );
1304     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
1306     {
1307         InkAction* inky = ink_action_new( "NodeInsertAction",
1308                                           _("Insert node"),
1309                                           _("Insert new nodes into selected segments"),
1310                                           INKSCAPE_ICON_NODE_ADD,
1311                                           secondarySize );
1312         g_object_set( inky, "short_label", _("Insert"), NULL );
1313         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1314         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1315     }
1317     {
1318         InkAction* inky = ink_action_new( "NodeDeleteAction",
1319                                           _("Delete node"),
1320                                           _("Delete selected nodes"),
1321                                           INKSCAPE_ICON_NODE_DELETE,
1322                                           secondarySize );
1323         g_object_set( inky, "short_label", _("Delete"), NULL );
1324         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1325         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1326     }
1328     {
1329         InkAction* inky = ink_action_new( "NodeJoinAction",
1330                                           _("Join endnodes"),
1331                                           _("Join selected endnodes"),
1332                                           INKSCAPE_ICON_NODE_JOIN,
1333                                           secondarySize );
1334         g_object_set( inky, "short_label", _("Join"), NULL );
1335         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1336         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1337     }
1339     {
1340         InkAction* inky = ink_action_new( "NodeBreakAction",
1341                                           _("Break nodes"),
1342                                           _("Break path at selected nodes"),
1343                                           INKSCAPE_ICON_NODE_BREAK,
1344                                           secondarySize );
1345         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1346         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1347     }
1350     {
1351         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1352                                           _("Join with segment"),
1353                                           _("Join selected endnodes with a new segment"),
1354                                           INKSCAPE_ICON_NODE_JOIN_SEGMENT,
1355                                           secondarySize );
1356         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1357         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1358     }
1360     {
1361         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1362                                           _("Delete segment"),
1363                                           _("Delete segment between two non-endpoint nodes"),
1364                                           INKSCAPE_ICON_NODE_DELETE_SEGMENT,
1365                                           secondarySize );
1366         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1367         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1368     }
1370     {
1371         InkAction* inky = ink_action_new( "NodeCuspAction",
1372                                           _("Node Cusp"),
1373                                           _("Make selected nodes corner"),
1374                                           INKSCAPE_ICON_NODE_TYPE_CUSP,
1375                                           secondarySize );
1376         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1377         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1378     }
1380     {
1381         InkAction* inky = ink_action_new( "NodeSmoothAction",
1382                                           _("Node Smooth"),
1383                                           _("Make selected nodes smooth"),
1384                                           INKSCAPE_ICON_NODE_TYPE_SMOOTH,
1385                                           secondarySize );
1386         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1387         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1388     }
1390     {
1391         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1392                                           _("Node Symmetric"),
1393                                           _("Make selected nodes symmetric"),
1394                                           INKSCAPE_ICON_NODE_TYPE_SYMMETRIC,
1395                                           secondarySize );
1396         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1397         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1398     }
1400     {
1401         InkAction* inky = ink_action_new( "NodeAutoAction",
1402                                           _("Node Auto"),
1403                                           _("Make selected nodes auto-smooth"),
1404                                           INKSCAPE_ICON_NODE_TYPE_AUTO_SMOOTH,
1405                                           secondarySize );
1406         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_auto), 0 );
1407         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1408     }
1410     {
1411         InkAction* inky = ink_action_new( "NodeLineAction",
1412                                           _("Node Line"),
1413                                           _("Make selected segments lines"),
1414                                           INKSCAPE_ICON_NODE_SEGMENT_LINE,
1415                                           secondarySize );
1416         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1417         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1418     }
1420     {
1421         InkAction* inky = ink_action_new( "NodeCurveAction",
1422                                           _("Node Curve"),
1423                                           _("Make selected segments curves"),
1424                                           INKSCAPE_ICON_NODE_SEGMENT_CURVE,
1425                                           secondarySize );
1426         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1427         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1428     }
1430     {
1431         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1432                                                       _("Show Handles"),
1433                                                       _("Show the Bezier handles of selected nodes"),
1434                                                       INKSCAPE_ICON_SHOW_NODE_HANDLES,
1435                                                       Inkscape::ICON_SIZE_DECORATION );
1436         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1437         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1438         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_handles", true) );
1439     }
1441     {
1442         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1443                                                       _("Show Outline"),
1444                                                       _("Show the outline of the path"),
1445                                                       INKSCAPE_ICON_SHOW_PATH_OUTLINE,
1446                                                       Inkscape::ICON_SIZE_DECORATION );
1447         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1448         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1449         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_helperpath", false) );
1450     }
1452     {
1453         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1454                                           _("Next path effect parameter"),
1455                                           _("Show next path effect parameter for editing"),
1456                                           INKSCAPE_ICON_PATH_EFFECT_PARAMETER_NEXT,
1457                                           Inkscape::ICON_SIZE_DECORATION );
1458         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1459         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1460         g_object_set_data( holder, "nodes_lpeedit", inky);
1461     }
1463     {
1464         InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1465                                           _("Edit clipping path"),
1466                                           _("Edit the clipping path of the object"),
1467                                           INKSCAPE_ICON_PATH_CLIP_EDIT,
1468                                           Inkscape::ICON_SIZE_DECORATION );
1469         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1470         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1471         g_object_set_data( holder, "nodes_clippathedit", inky);
1472     }
1474     {
1475         InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1476                                           _("Edit mask path"),
1477                                           _("Edit the mask of the object"),
1478                                           INKSCAPE_ICON_PATH_MASK_EDIT,
1479                                           Inkscape::ICON_SIZE_DECORATION );
1480         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1481         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1482         g_object_set_data( holder, "nodes_maskedit", inky);
1483     }
1485     /* X coord of selected node(s) */
1486     {
1487         EgeAdjustmentAction* eact = 0;
1488         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1489         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1490         eact = create_adjustment_action( "NodeXAction",
1491                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1492                                          "/tools/nodes/Xcoord", 0,
1493                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1494                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1495                                          labels, values, G_N_ELEMENTS(labels),
1496                                          sp_node_path_x_value_changed );
1497         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1498         g_object_set_data( holder, "nodes_x_action", eact );
1499         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1500         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1501     }
1503     /* Y coord of selected node(s) */
1504     {
1505         EgeAdjustmentAction* eact = 0;
1506         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1507         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1508         eact = create_adjustment_action( "NodeYAction",
1509                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1510                                          "/tools/nodes/Ycoord", 0,
1511                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1512                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1513                                          labels, values, G_N_ELEMENTS(labels),
1514                                          sp_node_path_y_value_changed );
1515         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1516         g_object_set_data( holder, "nodes_y_action", eact );
1517         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1518         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1519     }
1521     // add the units menu
1522     {
1523         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1524         gtk_action_group_add_action( mainActions, act );
1525     }
1528     sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1530     //watch selection
1531     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1533     sigc::connection *c_selection_changed =
1534         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1535                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1536     pool->add_connection ("selection-changed", c_selection_changed);
1538     sigc::connection *c_selection_modified =
1539         new sigc::connection (sp_desktop_selection (desktop)->connectModified
1540                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1541     pool->add_connection ("selection-modified", c_selection_modified);
1543     sigc::connection *c_subselection_changed =
1544         new sigc::connection (desktop->connectToolSubselectionChanged
1545                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1546     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1548     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1550     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1551 } // end of sp_node_toolbox_prep()
1554 //########################
1555 //##    Zoom Toolbox    ##
1556 //########################
1558 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1560     // no custom GtkAction setup needed
1561 } // end of sp_zoom_toolbox_prep()
1563 void ToolboxFactory::setToolboxDesktop(GtkWidget *toolbox, SPDesktop *desktop)
1565     sigc::connection *conn = static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1566                                                                               "event_context_connection"));
1568     BarId id = static_cast<BarId>( GPOINTER_TO_INT(g_object_get_data(G_OBJECT(toolbox), BAR_ID_KEY)) );
1570     SetupFunction setup_func = 0;
1571     UpdateFunction update_func = 0;
1573     switch (id) {
1574         case BAR_TOOL:
1575             setup_func = setup_tool_toolbox;
1576             update_func = update_tool_toolbox;
1577             break;
1579         case BAR_AUX:
1580             toolbox = gtk_bin_get_child(GTK_BIN(toolbox));
1581             setup_func = setup_aux_toolbox;
1582             update_func = update_aux_toolbox;
1583             break;
1585         case BAR_COMMANDS:
1586             setup_func = setup_commands_toolbox;
1587             update_func = update_commands_toolbox;
1588             break;
1590         case BAR_SNAP:
1591             setup_func = setup_snap_toolbox;
1592             update_func = updateSnapToolbox;
1593             break;
1594         default:
1595             g_warning("Unexpected toolbox id encountered.");
1596     }
1598     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1599     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1601     if (old_desktop) {
1602         GList *children, *iter;
1604         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1605         for ( iter = children ; iter ; iter = iter->next ) {
1606             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1607         }
1608         g_list_free(children);
1609     }
1611     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1613     if (desktop && setup_func && update_func) {
1614         gtk_widget_set_sensitive(toolbox, TRUE);
1615         setup_func(toolbox, desktop);
1616         update_func(desktop, desktop->event_context, toolbox);
1617         *conn = desktop->connectEventContextChanged(sigc::bind (sigc::ptr_fun(update_func), toolbox));
1618     } else {
1619         gtk_widget_set_sensitive(toolbox, FALSE);
1620     }
1622 } // end of sp_toolbox_set_desktop()
1625 static void setupToolboxCommon( GtkWidget *toolbox,
1626                                 SPDesktop *desktop,
1627                                 gchar const *descr,
1628                                 gchar const* toolbarName,
1629                                 gchar const* sizePref )
1631     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1632     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1634     GtkUIManager* mgr = gtk_ui_manager_new();
1635     GError* errVal = 0;
1637     GtkOrientation orientation = GTK_ORIENTATION_HORIZONTAL;
1639     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1640     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1642     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, toolbarName );
1643     if ( prefs->getBool("/toolbox/icononly", true) ) {
1644         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1645     }
1647     Inkscape::IconSize toolboxSize = ToolboxFactory::prefToSize(sizePref);
1648     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1650     if (GTK_IS_HANDLE_BOX(toolbox)) {
1651         // g_message("GRABBING ORIENTATION   [%s]", toolbarName);
1652         GtkPositionType pos = gtk_handle_box_get_handle_position(GTK_HANDLE_BOX(toolbox));
1653         orientation = ((pos == GTK_POS_LEFT) || (pos == GTK_POS_RIGHT)) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
1654     }
1655     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), orientation);
1656     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1658     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1660     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1661     if ( child ) {
1662         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1663     }
1665     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1668 void ToolboxFactory::setOrientation(GtkWidget* toolbox, GtkOrientation orientation)
1670     //g_message("Set orientation for %p to be %d", toolbox, orientation);
1671     //GType type = GTK_WIDGET_TYPE(toolbox);
1672     //g_message("        [%s]", g_type_name(type));
1673     //g_message("             %p", g_object_get_data(G_OBJECT(toolbox), BAR_ID_KEY));
1675     GtkPositionType pos = (orientation == GTK_ORIENTATION_HORIZONTAL) ? GTK_POS_LEFT : GTK_POS_TOP;
1676     GtkHandleBox* handleBox = 0;
1678     if (GTK_IS_BIN(toolbox)) {
1679         //g_message("            is a BIN");
1680         GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1681         if (child) {
1682             //GType type2 = GTK_WIDGET_TYPE(child);
1683             //g_message("            child    [%s]", g_type_name(type2));
1685             if (GTK_IS_BOX(child)) {
1686                 //g_message("                is a BOX");
1688                 GList* children = gtk_container_get_children(GTK_CONTAINER(child));
1689                 if (children && children->data) {
1690                     //GtkWidget* child2 = GTK_WIDGET(children->data);
1691                     //GType type3 = GTK_WIDGET_TYPE(child2);
1692                     //g_message("                child    [%s]", g_type_name(type3));
1693                     g_message("need to add dynamic switch");
1694                     for (GList* curr = children; curr; curr = g_list_next(curr)) {
1695                         GtkWidget* child2 = GTK_WIDGET(curr->data);
1697                         if (GTK_IS_TOOLBAR(child2)) {
1698                             GtkToolbar* childBar = GTK_TOOLBAR(child2);
1699                             gtk_toolbar_set_orientation(childBar, orientation);
1700                             if (GTK_IS_HANDLE_BOX(toolbox)) {
1701                                 handleBox = GTK_HANDLE_BOX(toolbox);
1702                             }
1703                         }
1704                     }
1705                     g_list_free(children);
1706                 } else {
1707                     if (GTK_IS_HANDLE_BOX(toolbox)) {
1708                         handleBox = GTK_HANDLE_BOX(toolbox);
1709                     }
1710                     // The call is being made before the toolbox proper has been setup.
1711                 }
1712             } else if (GTK_IS_TOOLBAR(child)) {
1713                 GtkToolbar* toolbar = GTK_TOOLBAR(child);
1714                 gtk_toolbar_set_orientation( toolbar, orientation );
1715                 if (GTK_IS_HANDLE_BOX(toolbox)) {
1716                     handleBox = GTK_HANDLE_BOX(toolbox);
1717                 }
1718             }
1719         }
1720     }
1722     if (handleBox) {
1723         gtk_handle_box_set_handle_position(handleBox, pos);
1724     }
1727 static void
1728 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1730     gchar const * descr =
1731         "<ui>"
1732         "  <toolbar name='ToolToolbar'>"
1733         "    <toolitem action='ToolSelector' />"
1734         "    <toolitem action='ToolNode' />"
1735         "    <toolitem action='ToolTweak' />"
1736         "    <toolitem action='ToolSpray' />"
1737         "    <toolitem action='ToolZoom' />"
1738         "    <toolitem action='ToolRect' />"
1739         "    <toolitem action='Tool3DBox' />"
1740         "    <toolitem action='ToolArc' />"
1741         "    <toolitem action='ToolStar' />"
1742         "    <toolitem action='ToolSpiral' />"
1743         "    <toolitem action='ToolPencil' />"
1744         "    <toolitem action='ToolPen' />"
1745         "    <toolitem action='ToolCalligraphic' />"
1746         "    <toolitem action='ToolEraser' />"
1747 //        "    <toolitem action='ToolLPETool' />"
1748         "    <toolitem action='ToolPaintBucket' />"
1749         "    <toolitem action='ToolText' />"
1750         "    <toolitem action='ToolConnector' />"
1751         "    <toolitem action='ToolGradient' />"
1752         "    <toolitem action='ToolDropper' />"
1753         "  </toolbar>"
1754         "</ui>";
1756     setupToolboxCommon( toolbox, desktop, descr,
1757                         "/ui/ToolToolbar",
1758                         "/toolbox/tools/small");
1761 static void
1762 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1764     gchar const *const tname = ( eventcontext
1765                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1766                                  : NULL );
1767     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1769     for (int i = 0 ; tools[i].type_name ; i++ ) {
1770         Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1771         if ( act ) {
1772             bool setActive = tname && !strcmp(tname, tools[i].type_name);
1773             Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1774             if ( verbAct ) {
1775                 verbAct->set_active(setActive);
1776             }
1777         }
1778     }
1781 static void
1782 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1784     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1785     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1786     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1787     GtkUIManager* mgr = gtk_ui_manager_new();
1788     GError* errVal = 0;
1789     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1790     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1792     std::map<std::string, GtkWidget*> dataHolders;
1794     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1795         if ( aux_toolboxes[i].prep_func ) {
1796             // converted to GtkActions and UIManager
1798             GtkWidget* kludge = gtk_toolbar_new();
1799             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1800             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1801             dataHolders[aux_toolboxes[i].type_name] = kludge;
1802             aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1803         } else {
1805             GtkWidget *sub_toolbox = 0;
1806             if (aux_toolboxes[i].create_func == NULL) {
1807                 sub_toolbox = sp_empty_toolbox_new(desktop);
1808             } else {
1809                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1810             }
1812             gtk_size_group_add_widget( grouper, sub_toolbox );
1814             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1815             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1817         }
1818     }
1820     // Second pass to create toolbars *after* all GtkActions are created
1821     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1822         if ( aux_toolboxes[i].prep_func ) {
1823             // converted to GtkActions and UIManager
1825             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1827             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1828             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1830             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1831             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1832             g_free( tmp );
1833             tmp = 0;
1835             if ( prefs->getBool( "/toolbox/icononly", true) ) {
1836                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1837             }
1839             Inkscape::IconSize toolboxSize = ToolboxFactory::prefToSize("/toolbox/small");
1840             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1842             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1844             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1845                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1846                 swatch->setDesktop( desktop );
1847                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1848                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1849                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1850                 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 );
1851             }
1853             gtk_widget_show_all( holder );
1854             sp_set_font_size_smaller( holder );
1856             gtk_size_group_add_widget( grouper, holder );
1858             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1859             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1860         }
1861     }
1863     g_object_unref( G_OBJECT(grouper) );
1866 static void
1867 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1869     gchar const *tname = ( eventcontext
1870                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1871                            : NULL );
1872     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1873         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1874         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1875             gtk_widget_show_all(sub_toolbox);
1876             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1877         } else {
1878             gtk_widget_hide(sub_toolbox);
1879         }
1880     }
1883 static void
1884 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1886     gchar const * descr =
1887         "<ui>"
1888         "  <toolbar name='CommandsToolbar'>"
1889         "    <toolitem action='FileNew' />"
1890         "    <toolitem action='FileOpen' />"
1891         "    <toolitem action='FileSave' />"
1892         "    <toolitem action='FilePrint' />"
1893         "    <separator />"
1894         "    <toolitem action='FileImport' />"
1895         "    <toolitem action='FileExport' />"
1896         "    <separator />"
1897         "    <toolitem action='EditUndo' />"
1898         "    <toolitem action='EditRedo' />"
1899         "    <separator />"
1900         "    <toolitem action='EditCopy' />"
1901         "    <toolitem action='EditCut' />"
1902         "    <toolitem action='EditPaste' />"
1903         "    <separator />"
1904         "    <toolitem action='ZoomSelection' />"
1905         "    <toolitem action='ZoomDrawing' />"
1906         "    <toolitem action='ZoomPage' />"
1907         "    <separator />"
1908         "    <toolitem action='EditDuplicate' />"
1909         "    <toolitem action='EditClone' />"
1910         "    <toolitem action='EditUnlinkClone' />"
1911         "    <separator />"
1912         "    <toolitem action='SelectionGroup' />"
1913         "    <toolitem action='SelectionUnGroup' />"
1914         "    <separator />"
1915         "    <toolitem action='DialogFillStroke' />"
1916         "    <toolitem action='DialogText' />"
1917         "    <toolitem action='DialogLayers' />"
1918         "    <toolitem action='DialogXMLEditor' />"
1919         "    <toolitem action='DialogAlignDistribute' />"
1920         "    <separator />"
1921         "    <toolitem action='DialogPreferences' />"
1922         "    <toolitem action='DialogDocumentProperties' />"
1923         "    <separator />"
1924         "    <toolitem action='TaskSetAction' />"
1925         "  </toolbar>"
1926         "</ui>";
1928     setupToolboxCommon( toolbox, desktop, descr,
1929                         "/ui/CommandsToolbar",
1930                         "/toolbox/small" );
1933 static void
1934 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1938 void toggle_snap_callback (GtkToggleAction *act, gpointer data) { //data points to the toolbox
1940     if (g_object_get_data(G_OBJECT(data), "freeze" )) {
1941         return;
1942     }
1944     gpointer ptr = g_object_get_data(G_OBJECT(data), "desktop");
1945     g_assert(ptr != NULL);
1947     SPDesktop *dt = reinterpret_cast<SPDesktop*>(ptr);
1948     SPNamedView *nv = sp_desktop_namedview(dt);
1949     SPDocument *doc = SP_OBJECT_DOCUMENT(nv);
1951     if (dt == NULL || nv == NULL) {
1952         g_warning("No desktop or namedview specified (in toggle_snap_callback)!");
1953         return;
1954     }
1956     Inkscape::XML::Node *repr = SP_OBJECT_REPR(nv);
1958     if (repr == NULL) {
1959         g_warning("This namedview doesn't have a xml representation attached!");
1960         return;
1961     }
1963     bool saved = sp_document_get_undo_sensitive(doc);
1964     sp_document_set_undo_sensitive(doc, false);
1966     bool v = false;
1967     SPAttributeEnum attr = (SPAttributeEnum) GPOINTER_TO_INT(g_object_get_data(G_OBJECT(act), "SP_ATTR_INKSCAPE"));
1969     switch (attr) {
1970         case SP_ATTR_INKSCAPE_SNAP_GLOBAL:
1971             dt->toggleSnapGlobal();
1972             break;
1973         case SP_ATTR_INKSCAPE_SNAP_BBOX:
1974             v = nv->snap_manager.snapprefs.getSnapModeBBox();
1975             sp_repr_set_boolean(repr, "inkscape:snap-bbox", !v);
1976             break;
1977         case SP_ATTR_INKSCAPE_BBOX_PATHS:
1978             v = nv->snap_manager.snapprefs.getSnapToBBoxPath();
1979             sp_repr_set_boolean(repr, "inkscape:bbox-paths", !v);
1980             break;
1981         case SP_ATTR_INKSCAPE_BBOX_NODES:
1982             v = nv->snap_manager.snapprefs.getSnapToBBoxNode();
1983             sp_repr_set_boolean(repr, "inkscape:bbox-nodes", !v);
1984             break;
1985         case SP_ATTR_INKSCAPE_SNAP_NODES:
1986             v = nv->snap_manager.snapprefs.getSnapModeNode();
1987             sp_repr_set_boolean(repr, "inkscape:snap-nodes", !v);
1988             break;
1989         case SP_ATTR_INKSCAPE_OBJECT_PATHS:
1990             v = nv->snap_manager.snapprefs.getSnapToItemPath();
1991             sp_repr_set_boolean(repr, "inkscape:object-paths", !v);
1992             break;
1993         case SP_ATTR_INKSCAPE_OBJECT_NODES:
1994             v = nv->snap_manager.snapprefs.getSnapToItemNode();
1995             sp_repr_set_boolean(repr, "inkscape:object-nodes", !v);
1996             break;
1997         case SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES:
1998             v = nv->snap_manager.snapprefs.getSnapSmoothNodes();
1999             sp_repr_set_boolean(repr, "inkscape:snap-smooth-nodes", !v);
2000             break;
2001         case SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS:
2002             v = nv->snap_manager.snapprefs.getSnapIntersectionCS();
2003             sp_repr_set_boolean(repr, "inkscape:snap-intersection-paths", !v);
2004             break;
2005         case SP_ATTR_INKSCAPE_SNAP_CENTER:
2006             v = nv->snap_manager.snapprefs.getIncludeItemCenter();
2007             sp_repr_set_boolean(repr, "inkscape:snap-center", !v);
2008             break;
2009         case SP_ATTR_INKSCAPE_SNAP_GRIDS:
2010             v = nv->snap_manager.snapprefs.getSnapToGrids();
2011             sp_repr_set_boolean(repr, "inkscape:snap-grids", !v);
2012             break;
2013         case SP_ATTR_INKSCAPE_SNAP_TO_GUIDES:
2014             v = nv->snap_manager.snapprefs.getSnapToGuides();
2015             sp_repr_set_boolean(repr, "inkscape:snap-to-guides", !v);
2016             break;
2017         case SP_ATTR_INKSCAPE_SNAP_PAGE:
2018             v = nv->snap_manager.snapprefs.getSnapToPageBorder();
2019             sp_repr_set_boolean(repr, "inkscape:snap-page", !v);
2020             break;
2021             /*case SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE:
2022               v = nv->snap_manager.snapprefs.getSnapIntersectionGG();
2023               sp_repr_set_boolean(repr, "inkscape:snap-intersection-grid-guide", !v);
2024               break;*/
2025         case SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS:
2026             v = nv->snap_manager.snapprefs.getSnapLineMidpoints();
2027             sp_repr_set_boolean(repr, "inkscape:snap-midpoints", !v);
2028             break;
2029         case SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS:
2030             v = nv->snap_manager.snapprefs.getSnapObjectMidpoints();
2031             sp_repr_set_boolean(repr, "inkscape:snap-object-midpoints", !v);
2032             break;
2033         case SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS:
2034             v = nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints();
2035             sp_repr_set_boolean(repr, "inkscape:snap-bbox-edge-midpoints", !v);
2036             break;
2037         case SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS:
2038             v = nv->snap_manager.snapprefs.getSnapBBoxMidpoints();
2039             sp_repr_set_boolean(repr, "inkscape:snap-bbox-midpoints", !v);
2040             break;
2041         default:
2042             g_warning("toggle_snap_callback has been called with an ID for which no action has been defined");
2043             break;
2044     }
2046     // The snapping preferences are stored in the document, and therefore toggling makes the document dirty
2047     doc->setModifiedSinceSave();
2049     sp_document_set_undo_sensitive(doc, saved);
2052 void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
2054     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2056     gchar const * descr =
2057         "<ui>"
2058         "  <toolbar name='SnapToolbar'>"
2059         "    <toolitem action='ToggleSnapGlobal' />"
2060         "    <separator />"
2061         "    <toolitem action='ToggleSnapFromBBoxCorner' />"
2062         "    <toolitem action='ToggleSnapToBBoxPath' />"
2063         "    <toolitem action='ToggleSnapToBBoxNode' />"
2064         "    <toolitem action='ToggleSnapToFromBBoxEdgeMidpoints' />"
2065         "    <toolitem action='ToggleSnapToFromBBoxCenters' />"
2066         "    <separator />"
2067         "    <toolitem action='ToggleSnapFromNode' />"
2068         "    <toolitem action='ToggleSnapToItemPath' />"
2069         "    <toolitem action='ToggleSnapToPathIntersections' />"
2070         "    <toolitem action='ToggleSnapToItemNode' />"
2071         "    <toolitem action='ToggleSnapToSmoothNodes' />"
2072         "    <toolitem action='ToggleSnapToFromLineMidpoints' />"
2073         "    <toolitem action='ToggleSnapToFromObjectCenters' />"
2074         "    <toolitem action='ToggleSnapToFromRotationCenter' />"
2075         "    <separator />"
2076         "    <toolitem action='ToggleSnapToPageBorder' />"
2077         "    <toolitem action='ToggleSnapToGrids' />"
2078         "    <toolitem action='ToggleSnapToGuides' />"
2079         //"    <toolitem action='ToggleSnapToGridGuideIntersections' />"
2080         "  </toolbar>"
2081         "</ui>";
2083     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
2085     {
2086         InkToggleAction* act = ink_toggle_action_new("ToggleSnapGlobal",
2087                                                      _("Snap"), _("Enable snapping"), INKSCAPE_ICON_SNAP, secondarySize,
2088                                                      SP_ATTR_INKSCAPE_SNAP_GLOBAL);
2090         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2091         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2092     }
2094     {
2095         InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromBBoxCorner",
2096                                                      _("Bounding box"), _("Snap bounding box corners"), INKSCAPE_ICON_SNAP_BOUNDING_BOX,
2097                                                      secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX);
2099         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2100         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2101     }
2103     {
2104         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxPath",
2105                                                      _("Bounding box edges"), _("Snap to edges of a bounding box"),
2106                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_EDGES, secondarySize, SP_ATTR_INKSCAPE_BBOX_PATHS);
2108         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2109         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2110     }
2112     {
2113         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxNode",
2114                                                      _("Bounding box corners"), _("Snap to bounding box corners"),
2115                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_CORNERS, secondarySize, SP_ATTR_INKSCAPE_BBOX_NODES);
2117         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2118         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2119     }
2121     {
2122         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxEdgeMidpoints",
2123                                                      _("BBox Edge Midpoints"), _("Snap from and to midpoints of bounding box edges"),
2124                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_MIDPOINTS, secondarySize,
2125                                                      SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS);
2127         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2128         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2129     }
2131     {
2132         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxCenters",
2133                                                      _("BBox Centers"), _("Snapping from and to centers of bounding boxes"),
2134                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS);
2136         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2137         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2138     }
2140     {
2141         InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromNode",
2142                                                      _("Nodes"), _("Snap nodes or handles"), INKSCAPE_ICON_SNAP_NODES, secondarySize, SP_ATTR_INKSCAPE_SNAP_NODES);
2144         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2145         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2146     }
2148     {
2149         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemPath",
2150                                                      _("Paths"), _("Snap to paths"), INKSCAPE_ICON_SNAP_NODES_PATH, secondarySize,
2151                                                      SP_ATTR_INKSCAPE_OBJECT_PATHS);
2153         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2154         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2155     }
2157     {
2158         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPathIntersections",
2159                                                      _("Path intersections"), _("Snap to path intersections"),
2160                                                      INKSCAPE_ICON_SNAP_NODES_INTERSECTION, secondarySize, SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS);
2162         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2163         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2164     }
2166     {
2167         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemNode",
2168                                                      _("To nodes"), _("Snap to cusp nodes"), INKSCAPE_ICON_SNAP_NODES_CUSP, secondarySize,
2169                                                      SP_ATTR_INKSCAPE_OBJECT_NODES);
2171         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2172         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2173     }
2175     {
2176         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToSmoothNodes",
2177                                                      _("Smooth nodes"), _("Snap to smooth nodes"), INKSCAPE_ICON_SNAP_NODES_SMOOTH,
2178                                                      secondarySize, SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES);
2180         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2181         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2182     }
2184     {
2185         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromLineMidpoints",
2186                                                      _("Line Midpoints"), _("Snap from and to midpoints of line segments"),
2187                                                      INKSCAPE_ICON_SNAP_NODES_MIDPOINT, secondarySize, SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS);
2189         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2190         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2191     }
2193     {
2194         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromObjectCenters",
2195                                                      _("Object Centers"), _("Snap from and to centers of objects"),
2196                                                      INKSCAPE_ICON_SNAP_NODES_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS);
2198         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2199         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2200     }
2202     {
2203         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromRotationCenter",
2204                                                      _("Rotation Centers"), _("Snap from and to an item's rotation center"),
2205                                                      INKSCAPE_ICON_SNAP_NODES_ROTATION_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_CENTER);
2207         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2208         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2209     }
2211     {
2212         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPageBorder",
2213                                                      _("Page border"), _("Snap to the page border"), INKSCAPE_ICON_SNAP_PAGE,
2214                                                      secondarySize, SP_ATTR_INKSCAPE_SNAP_PAGE);
2216         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2217         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2218     }
2220     {
2221         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGrids",
2222                                                      _("Grids"), _("Snap to grids"), INKSCAPE_ICON_GRID_RECTANGULAR, secondarySize,
2223                                                      SP_ATTR_INKSCAPE_SNAP_GRIDS);
2225         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2226         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2227     }
2229     {
2230         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGuides",
2231                                                      _("Guides"), _("Snap to guides"), INKSCAPE_ICON_GUIDES, secondarySize,
2232                                                      SP_ATTR_INKSCAPE_SNAP_TO_GUIDES);
2234         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2235         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2236     }
2238     /*{
2239       InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGridGuideIntersections",
2240       _("Grid/guide intersections"), _("Snap to intersections of a grid with a guide"),
2241       INKSCAPE_ICON_SNAP_GRID_GUIDE_INTERSECTIONS, secondarySize,
2242       SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE);
2244       gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2245       g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2246       }*/
2248     setupToolboxCommon( toolbox, desktop, descr,
2249                         "/ui/SnapToolbar",
2250                         "/toolbox/secondary" );
2253 void ToolboxFactory::updateSnapToolbox(SPDesktop *desktop, SPEventContext */*eventcontext*/, GtkWidget *toolbox)
2255     g_assert(desktop != NULL);
2256     g_assert(toolbox != NULL);
2258     SPNamedView *nv = sp_desktop_namedview(desktop);
2259     if (nv == NULL) {
2260         g_warning("Namedview cannot be retrieved (in updateSnapToolbox)!");
2261         return;
2262     }
2264     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2266     Glib::RefPtr<Gtk::Action> act1 = mainActions->get_action("ToggleSnapGlobal");
2267     Glib::RefPtr<Gtk::Action> act2 = mainActions->get_action("ToggleSnapFromBBoxCorner");
2268     Glib::RefPtr<Gtk::Action> act3 = mainActions->get_action("ToggleSnapToBBoxPath");
2269     Glib::RefPtr<Gtk::Action> act4 = mainActions->get_action("ToggleSnapToBBoxNode");
2270     Glib::RefPtr<Gtk::Action> act4b = mainActions->get_action("ToggleSnapToFromBBoxEdgeMidpoints");
2271     Glib::RefPtr<Gtk::Action> act4c = mainActions->get_action("ToggleSnapToFromBBoxCenters");
2272     Glib::RefPtr<Gtk::Action> act5 = mainActions->get_action("ToggleSnapFromNode");
2273     Glib::RefPtr<Gtk::Action> act6 = mainActions->get_action("ToggleSnapToItemPath");
2274     Glib::RefPtr<Gtk::Action> act6b = mainActions->get_action("ToggleSnapToPathIntersections");
2275     Glib::RefPtr<Gtk::Action> act7 = mainActions->get_action("ToggleSnapToItemNode");
2276     Glib::RefPtr<Gtk::Action> act8 = mainActions->get_action("ToggleSnapToSmoothNodes");
2277     Glib::RefPtr<Gtk::Action> act9 = mainActions->get_action("ToggleSnapToFromLineMidpoints");
2278     Glib::RefPtr<Gtk::Action> act10 = mainActions->get_action("ToggleSnapToFromObjectCenters");
2279     Glib::RefPtr<Gtk::Action> act11 = mainActions->get_action("ToggleSnapToFromRotationCenter");
2280     Glib::RefPtr<Gtk::Action> act12 = mainActions->get_action("ToggleSnapToPageBorder");
2281     //Glib::RefPtr<Gtk::Action> act13 = mainActions->get_action("ToggleSnapToGridGuideIntersections");
2282     Glib::RefPtr<Gtk::Action> act14 = mainActions->get_action("ToggleSnapToGrids");
2283     Glib::RefPtr<Gtk::Action> act15 = mainActions->get_action("ToggleSnapToGuides");
2286     if (!act1) {
2287         return; // The snap actions haven't been defined yet (might be the case during startup)
2288     }
2290     // The ..._set_active calls below will toggle the buttons, but this shouldn't lead to
2291     // changes in our document because we're only updating the UI;
2292     // Setting the "freeze" parameter to true will block the code in toggle_snap_callback()
2293     g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(TRUE));
2295     bool const c1 = nv->snap_manager.snapprefs.getSnapEnabledGlobally();
2296     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act1->gobj()), c1);
2298     bool const c2 = nv->snap_manager.snapprefs.getSnapModeBBox();
2299     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act2->gobj()), c2);
2300     gtk_action_set_sensitive(GTK_ACTION(act2->gobj()), c1);
2302     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act3->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxPath());
2303     gtk_action_set_sensitive(GTK_ACTION(act3->gobj()), c1 && c2);
2304     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxNode());
2305     gtk_action_set_sensitive(GTK_ACTION(act4->gobj()), c1 && c2);
2306     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4b->gobj()), nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints());
2307     gtk_action_set_sensitive(GTK_ACTION(act4b->gobj()), c1 && c2);
2308     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4c->gobj()), nv->snap_manager.snapprefs.getSnapBBoxMidpoints());
2309     gtk_action_set_sensitive(GTK_ACTION(act4c->gobj()), c1 && c2);
2311     bool const c3 = nv->snap_manager.snapprefs.getSnapModeNode();
2312     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act5->gobj()), c3);
2313     gtk_action_set_sensitive(GTK_ACTION(act5->gobj()), c1);
2315     bool const c4 = nv->snap_manager.snapprefs.getSnapToItemPath();
2316     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6->gobj()), c4);
2317     gtk_action_set_sensitive(GTK_ACTION(act6->gobj()), c1 && c3);
2318     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6b->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionCS());
2319     gtk_action_set_sensitive(GTK_ACTION(act6b->gobj()), c1 && c3 && c4);
2320     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act7->gobj()), nv->snap_manager.snapprefs.getSnapToItemNode());
2321     gtk_action_set_sensitive(GTK_ACTION(act7->gobj()), c1 && c3);
2322     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act8->gobj()), nv->snap_manager.snapprefs.getSnapSmoothNodes());
2323     gtk_action_set_sensitive(GTK_ACTION(act8->gobj()), c1 && c3);
2324     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act9->gobj()), nv->snap_manager.snapprefs.getSnapLineMidpoints());
2325     gtk_action_set_sensitive(GTK_ACTION(act9->gobj()), c1 && c3);
2326     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act10->gobj()), nv->snap_manager.snapprefs.getSnapObjectMidpoints());
2327     gtk_action_set_sensitive(GTK_ACTION(act10->gobj()), c1 && c3);
2328     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act11->gobj()), nv->snap_manager.snapprefs.getIncludeItemCenter());
2329     gtk_action_set_sensitive(GTK_ACTION(act11->gobj()), c1 && c3);
2331     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act12->gobj()), nv->snap_manager.snapprefs.getSnapToPageBorder());
2332     gtk_action_set_sensitive(GTK_ACTION(act12->gobj()), c1);
2333     //gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act13->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionGG());
2334     //gtk_action_set_sensitive(GTK_ACTION(act13->gobj()), c1);
2336     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act14->gobj()), nv->snap_manager.snapprefs.getSnapToGrids());
2337     gtk_action_set_sensitive(GTK_ACTION(act14->gobj()), c1);
2338     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act15->gobj()), nv->snap_manager.snapprefs.getSnapToGuides());
2339     gtk_action_set_sensitive(GTK_ACTION(act15->gobj()), c1);
2342     g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(FALSE)); // unfreeze (see above)
2345 void ToolboxFactory::showAuxToolbox(GtkWidget *toolbox_toplevel)
2347     gtk_widget_show(toolbox_toplevel);
2348     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
2350     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
2351     if (!shown_toolbox) {
2352         return;
2353     }
2354     gtk_widget_show(toolbox);
2356     gtk_widget_show_all(shown_toolbox);
2359 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop)
2361     GtkWidget *tbl = gtk_toolbar_new();
2362     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
2363     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
2365     gtk_widget_show_all(tbl);
2366     sp_set_font_size_smaller (tbl);
2368     return tbl;
2371 #define MODE_LABEL_WIDTH 70
2373 //########################
2374 //##       Star         ##
2375 //########################
2377 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2379     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2381     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2382         // do not remember prefs if this call is initiated by an undo change, because undoing object
2383         // creation sets bogus values to its attributes before it is deleted
2384         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2385         prefs->setInt("/tools/shapes/star/magnitude", (gint)adj->value);
2386     }
2388     // quit if run by the attr_changed listener
2389     if (g_object_get_data( dataKludge, "freeze" )) {
2390         return;
2391     }
2393     // in turn, prevent listener from responding
2394     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2396     bool modmade = false;
2398     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2399     GSList const *items = selection->itemList();
2400     for (; items != NULL; items = items->next) {
2401         if (SP_IS_STAR((SPItem *) items->data)) {
2402             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2403             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
2404             sp_repr_set_svg_double(repr, "sodipodi:arg2",
2405                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
2406                                     + M_PI / (gint)adj->value));
2407             SP_OBJECT((SPItem *) items->data)->updateRepr();
2408             modmade = true;
2409         }
2410     }
2411     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2412                                    _("Star: Change number of corners"));
2414     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2417 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2419     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2421     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2422         if (!IS_NAN(adj->value)) {
2423             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2424             prefs->setDouble("/tools/shapes/star/proportion", adj->value);
2425         }
2426     }
2428     // quit if run by the attr_changed listener
2429     if (g_object_get_data( dataKludge, "freeze" )) {
2430         return;
2431     }
2433     // in turn, prevent listener from responding
2434     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2436     bool modmade = false;
2437     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2438     GSList const *items = selection->itemList();
2439     for (; items != NULL; items = items->next) {
2440         if (SP_IS_STAR((SPItem *) items->data)) {
2441             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2443             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2444             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2445             if (r2 < r1) {
2446                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
2447             } else {
2448                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
2449             }
2451             SP_OBJECT((SPItem *) items->data)->updateRepr();
2452             modmade = true;
2453         }
2454     }
2456     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2457                                    _("Star: Change spoke ratio"));
2459     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2462 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
2464     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2465     bool flat = ege_select_one_action_get_active( act ) == 0;
2467     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2468         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2469         prefs->setBool( "/tools/shapes/star/isflatsided", flat);
2470     }
2472     // quit if run by the attr_changed listener
2473     if (g_object_get_data( dataKludge, "freeze" )) {
2474         return;
2475     }
2477     // in turn, prevent listener from responding
2478     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2480     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2481     GSList const *items = selection->itemList();
2482     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2483     bool modmade = false;
2485     if ( prop_action ) {
2486         gtk_action_set_sensitive( prop_action, !flat );
2487     }
2489     for (; items != NULL; items = items->next) {
2490         if (SP_IS_STAR((SPItem *) items->data)) {
2491             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2492             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
2493             SP_OBJECT((SPItem *) items->data)->updateRepr();
2494             modmade = true;
2495         }
2496     }
2498     if (modmade) {
2499         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2500                          flat ? _("Make polygon") : _("Make star"));
2501     }
2503     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2506 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2508     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2510     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2511         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2512         prefs->setDouble("/tools/shapes/star/rounded", (gdouble) adj->value);
2513     }
2515     // quit if run by the attr_changed listener
2516     if (g_object_get_data( dataKludge, "freeze" )) {
2517         return;
2518     }
2520     // in turn, prevent listener from responding
2521     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2523     bool modmade = false;
2525     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2526     GSList const *items = selection->itemList();
2527     for (; items != NULL; items = items->next) {
2528         if (SP_IS_STAR((SPItem *) items->data)) {
2529             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2530             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2531             SP_OBJECT(items->data)->updateRepr();
2532             modmade = true;
2533         }
2534     }
2535     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2536                                    _("Star: Change rounding"));
2538     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2541 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2543     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2545     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2546         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2547         prefs->setDouble("/tools/shapes/star/randomized", (gdouble) adj->value);
2548     }
2550     // quit if run by the attr_changed listener
2551     if (g_object_get_data( dataKludge, "freeze" )) {
2552         return;
2553     }
2555     // in turn, prevent listener from responding
2556     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2558     bool modmade = false;
2560     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2561     GSList const *items = selection->itemList();
2562     for (; items != NULL; items = items->next) {
2563         if (SP_IS_STAR((SPItem *) items->data)) {
2564             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2565             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2566             SP_OBJECT(items->data)->updateRepr();
2567             modmade = true;
2568         }
2569     }
2570     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2571                                    _("Star: Change randomization"));
2573     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2577 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2578                                        gchar const */*old_value*/, gchar const */*new_value*/,
2579                                        bool /*is_interactive*/, gpointer data)
2581     GtkWidget *tbl = GTK_WIDGET(data);
2583     // quit if run by the _changed callbacks
2584     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2585         return;
2586     }
2588     // in turn, prevent callbacks from responding
2589     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2591     GtkAdjustment *adj = 0;
2593     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2594     bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2596     if (!strcmp(name, "inkscape:randomized")) {
2597         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2598         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2599     } else if (!strcmp(name, "inkscape:rounded")) {
2600         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2601         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2602     } else if (!strcmp(name, "inkscape:flatsided")) {
2603         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2604         char const *flatsides = repr->attribute("inkscape:flatsided");
2605         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2606         if ( flatsides && !strcmp(flatsides,"false") ) {
2607             ege_select_one_action_set_active( flat_action, 1 );
2608             gtk_action_set_sensitive( prop_action, TRUE );
2609         } else {
2610             ege_select_one_action_set_active( flat_action, 0 );
2611             gtk_action_set_sensitive( prop_action, FALSE );
2612         }
2613     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2614         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2615         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2616         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2617         if (r2 < r1) {
2618             gtk_adjustment_set_value(adj, r2/r1);
2619         } else {
2620             gtk_adjustment_set_value(adj, r1/r2);
2621         }
2622     } else if (!strcmp(name, "sodipodi:sides")) {
2623         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2624         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2625     }
2627     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2631 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2633     NULL, /* child_added */
2634     NULL, /* child_removed */
2635     star_tb_event_attr_changed,
2636     NULL, /* content_changed */
2637     NULL  /* order_changed */
2638 };
2641 /**
2642  *  \param selection Should not be NULL.
2643  */
2644 static void
2645 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2647     int n_selected = 0;
2648     Inkscape::XML::Node *repr = NULL;
2650     purge_repr_listener( tbl, tbl );
2652     for (GSList const *items = selection->itemList();
2653          items != NULL;
2654          items = items->next)
2655     {
2656         if (SP_IS_STAR((SPItem *) items->data)) {
2657             n_selected++;
2658             repr = SP_OBJECT_REPR((SPItem *) items->data);
2659         }
2660     }
2662     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2664     if (n_selected == 0) {
2665         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2666     } else if (n_selected == 1) {
2667         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2669         if (repr) {
2670             g_object_set_data( tbl, "repr", repr );
2671             Inkscape::GC::anchor(repr);
2672             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2673             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2674         }
2675     } else {
2676         // FIXME: implement averaging of all parameters for multiple selected stars
2677         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2678         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2679     }
2683 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2685     // FIXME: in this and all other _default functions, set some flag telling the value_changed
2686     // callbacks to lump all the changes for all selected objects in one undo step
2688     GtkAdjustment *adj = 0;
2690     // fixme: make settable in prefs!
2691     gint mag = 5;
2692     gdouble prop = 0.5;
2693     gboolean flat = FALSE;
2694     gdouble randomized = 0;
2695     gdouble rounded = 0;
2697     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2698     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2700     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2701     gtk_action_set_sensitive( sb2, !flat );
2703     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2704     gtk_adjustment_set_value(adj, mag);
2705     gtk_adjustment_value_changed(adj);
2707     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2708     gtk_adjustment_set_value(adj, prop);
2709     gtk_adjustment_value_changed(adj);
2711     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2712     gtk_adjustment_set_value(adj, rounded);
2713     gtk_adjustment_value_changed(adj);
2715     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2716     gtk_adjustment_set_value(adj, randomized);
2717     gtk_adjustment_value_changed(adj);
2721 void
2722 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2724     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2725     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2726     GtkWidget *l = gtk_label_new(NULL);
2727     gtk_label_set_markup(GTK_LABEL(l), title);
2728     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2729     if ( GTK_IS_TOOLBAR(tbl) ) {
2730         gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2731     } else {
2732         gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2733     }
2734     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2738 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2740     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
2742     {
2743         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2744         ege_output_action_set_use_markup( act, TRUE );
2745         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2746         g_object_set_data( holder, "mode_action", act );
2747     }
2749     {
2750         EgeAdjustmentAction* eact = 0;
2751         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2752         bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2754         /* Flatsided checkbox */
2755         {
2756             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2758             GtkTreeIter iter;
2759             gtk_list_store_append( model, &iter );
2760             gtk_list_store_set( model, &iter,
2761                                 0, _("Polygon"),
2762                                 1, _("Regular polygon (with one handle) instead of a star"),
2763                                 2, INKSCAPE_ICON_DRAW_POLYGON,
2764                                 -1 );
2766             gtk_list_store_append( model, &iter );
2767             gtk_list_store_set( model, &iter,
2768                                 0, _("Star"),
2769                                 1, _("Star instead of a regular polygon (with one handle)"),
2770                                 2, INKSCAPE_ICON_DRAW_STAR,
2771                                 -1 );
2773             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2774             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2775             g_object_set_data( holder, "flat_action", act );
2777             ege_select_one_action_set_appearance( act, "full" );
2778             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2779             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2780             ege_select_one_action_set_icon_column( act, 2 );
2781             ege_select_one_action_set_icon_size( act, secondarySize );
2782             ege_select_one_action_set_tooltip_column( act, 1  );
2784             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2785             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2786         }
2788         /* Magnitude */
2789         {
2790         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2791         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2792         eact = create_adjustment_action( "MagnitudeAction",
2793                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2794                                          "/tools/shapes/star/magnitude", 3,
2795                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2796                                          3, 1024, 1, 5,
2797                                          labels, values, G_N_ELEMENTS(labels),
2798                                          sp_stb_magnitude_value_changed,
2799                                          1.0, 0 );
2800         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2801         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2802         }
2804         /* Spoke ratio */
2805         {
2806         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2807         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2808         eact = create_adjustment_action( "SpokeAction",
2809                                          _("Spoke ratio"), _("Spoke ratio:"),
2810                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2811                                          // Base radius is the same for the closest handle.
2812                                          _("Base radius to tip radius ratio"),
2813                                          "/tools/shapes/star/proportion", 0.5,
2814                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2815                                          0.01, 1.0, 0.01, 0.1,
2816                                          labels, values, G_N_ELEMENTS(labels),
2817                                          sp_stb_proportion_value_changed );
2818         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2819         g_object_set_data( holder, "prop_action", eact );
2820         }
2822         if ( !isFlatSided ) {
2823             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2824         } else {
2825             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2826         }
2828         /* Roundedness */
2829         {
2830         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2831         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2832         eact = create_adjustment_action( "RoundednessAction",
2833                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2834                                          "/tools/shapes/star/rounded", 0.0,
2835                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2836                                          -10.0, 10.0, 0.01, 0.1,
2837                                          labels, values, G_N_ELEMENTS(labels),
2838                                          sp_stb_rounded_value_changed );
2839         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2840         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2841         }
2843         /* Randomization */
2844         {
2845         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2846         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2847         eact = create_adjustment_action( "RandomizationAction",
2848                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2849                                          "/tools/shapes/star/randomized", 0.0,
2850                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2851                                          -10.0, 10.0, 0.001, 0.01,
2852                                          labels, values, G_N_ELEMENTS(labels),
2853                                          sp_stb_randomized_value_changed, 0.1, 3 );
2854         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2855         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2856         }
2857     }
2859     {
2860         /* Reset */
2861         {
2862             GtkAction* act = gtk_action_new( "StarResetAction",
2863                                              _("Defaults"),
2864                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2865                                              GTK_STOCK_CLEAR );
2866             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2867             gtk_action_group_add_action( mainActions, act );
2868             gtk_action_set_sensitive( act, TRUE );
2869         }
2870     }
2872     sigc::connection *connection = new sigc::connection(
2873         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2874         );
2875     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2876     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2880 //########################
2881 //##       Rect         ##
2882 //########################
2884 static void sp_rtb_sensitivize( GObject *tbl )
2886     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2887     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2888     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2890     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2891         gtk_action_set_sensitive( not_rounded, FALSE );
2892     } else {
2893         gtk_action_set_sensitive( not_rounded, TRUE );
2894     }
2898 static void
2899 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2900                           void (*setter)(SPRect *, gdouble))
2902     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2904     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2905     SPUnit const *unit = tracker->getActiveUnit();
2907     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2908         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2909         prefs->setDouble(Glib::ustring("/tools/shapes/rect/") + value_name, sp_units_get_pixels(adj->value, *unit));
2910     }
2912     // quit if run by the attr_changed listener
2913     if (g_object_get_data( tbl, "freeze" )) {
2914         return;
2915     }
2917     // in turn, prevent listener from responding
2918     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2920     bool modmade = false;
2921     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2922     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2923         if (SP_IS_RECT(items->data)) {
2924             if (adj->value != 0) {
2925                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2926             } else {
2927                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2928             }
2929             modmade = true;
2930         }
2931     }
2933     sp_rtb_sensitivize( tbl );
2935     if (modmade) {
2936         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2937                                    _("Change rectangle"));
2938     }
2940     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2943 static void
2944 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2946     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2949 static void
2950 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2952     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2955 static void
2956 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2958     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2961 static void
2962 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2964     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2969 static void
2970 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2972     GtkAdjustment *adj = 0;
2974     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2975     gtk_adjustment_set_value(adj, 0.0);
2976     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2977     gtk_adjustment_value_changed(adj);
2979     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2980     gtk_adjustment_set_value(adj, 0.0);
2981     gtk_adjustment_value_changed(adj);
2983     sp_rtb_sensitivize( obj );
2986 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2987                                        gchar const */*old_value*/, gchar const */*new_value*/,
2988                                        bool /*is_interactive*/, gpointer data)
2990     GObject *tbl = G_OBJECT(data);
2992     // quit if run by the _changed callbacks
2993     if (g_object_get_data( tbl, "freeze" )) {
2994         return;
2995     }
2997     // in turn, prevent callbacks from responding
2998     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3000     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
3001     SPUnit const *unit = tracker->getActiveUnit();
3003     gpointer item = g_object_get_data( tbl, "item" );
3004     if (item && SP_IS_RECT(item)) {
3005         {
3006             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
3007             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
3008             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
3009         }
3011         {
3012             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
3013             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
3014             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
3015         }
3017         {
3018             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
3019             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
3020             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
3021         }
3023         {
3024             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
3025             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
3026             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
3027         }
3028     }
3030     sp_rtb_sensitivize( tbl );
3032     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3036 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
3037     NULL, /* child_added */
3038     NULL, /* child_removed */
3039     rect_tb_event_attr_changed,
3040     NULL, /* content_changed */
3041     NULL  /* order_changed */
3042 };
3044 /**
3045  *  \param selection should not be NULL.
3046  */
3047 static void
3048 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3050     int n_selected = 0;
3051     Inkscape::XML::Node *repr = NULL;
3052     SPItem *item = NULL;
3054     if ( g_object_get_data( tbl, "repr" ) ) {
3055         g_object_set_data( tbl, "item", NULL );
3056     }
3057     purge_repr_listener( tbl, tbl );
3059     for (GSList const *items = selection->itemList();
3060          items != NULL;
3061          items = items->next) {
3062         if (SP_IS_RECT((SPItem *) items->data)) {
3063             n_selected++;
3064             item = (SPItem *) items->data;
3065             repr = SP_OBJECT_REPR(item);
3066         }
3067     }
3069     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3071     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3073     if (n_selected == 0) {
3074         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3076         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3077         gtk_action_set_sensitive(w, FALSE);
3078         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3079         gtk_action_set_sensitive(h, FALSE);
3081     } else if (n_selected == 1) {
3082         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3083         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3085         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3086         gtk_action_set_sensitive(w, TRUE);
3087         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3088         gtk_action_set_sensitive(h, TRUE);
3090         if (repr) {
3091             g_object_set_data( tbl, "repr", repr );
3092             g_object_set_data( tbl, "item", item );
3093             Inkscape::GC::anchor(repr);
3094             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
3095             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
3096         }
3097     } else {
3098         // FIXME: implement averaging of all parameters for multiple selected
3099         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3100         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3101         sp_rtb_sensitivize( tbl );
3102     }
3106 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3108     EgeAdjustmentAction* eact = 0;
3109     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
3111     {
3112         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
3113         ege_output_action_set_use_markup( act, TRUE );
3114         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3115         g_object_set_data( holder, "mode_action", act );
3116     }
3118     // rx/ry units menu: create
3119     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
3120     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
3121     // fixme: add % meaning per cent of the width/height
3122     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
3123     g_object_set_data( holder, "tracker", tracker );
3125     /* W */
3126     {
3127         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3128         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3129         eact = create_adjustment_action( "RectWidthAction",
3130                                          _("Width"), _("W:"), _("Width of rectangle"),
3131                                          "/tools/shapes/rect/width", 0,
3132                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
3133                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3134                                          labels, values, G_N_ELEMENTS(labels),
3135                                          sp_rtb_width_value_changed );
3136         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3137         g_object_set_data( holder, "width_action", eact );
3138         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3139         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3140     }
3142     /* H */
3143     {
3144         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3145         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3146         eact = create_adjustment_action( "RectHeightAction",
3147                                          _("Height"), _("H:"), _("Height of rectangle"),
3148                                          "/tools/shapes/rect/height", 0,
3149                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3150                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3151                                          labels, values, G_N_ELEMENTS(labels),
3152                                          sp_rtb_height_value_changed );
3153         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3154         g_object_set_data( holder, "height_action", eact );
3155         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3156         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3157     }
3159     /* rx */
3160     {
3161         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3162         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3163         eact = create_adjustment_action( "RadiusXAction",
3164                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
3165                                          "/tools/shapes/rect/rx", 0,
3166                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3167                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3168                                          labels, values, G_N_ELEMENTS(labels),
3169                                          sp_rtb_rx_value_changed);
3170         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3171         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3172     }
3174     /* ry */
3175     {
3176         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3177         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3178         eact = create_adjustment_action( "RadiusYAction",
3179                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
3180                                          "/tools/shapes/rect/ry", 0,
3181                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3182                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3183                                          labels, values, G_N_ELEMENTS(labels),
3184                                          sp_rtb_ry_value_changed);
3185         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3186         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3187     }
3189     // add the units menu
3190     {
3191         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
3192         gtk_action_group_add_action( mainActions, act );
3193     }
3195     /* Reset */
3196     {
3197         InkAction* inky = ink_action_new( "RectResetAction",
3198                                           _("Not rounded"),
3199                                           _("Make corners sharp"),
3200                                           INKSCAPE_ICON_RECTANGLE_MAKE_CORNERS_SHARP,
3201                                           secondarySize );
3202         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
3203         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3204         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3205         g_object_set_data( holder, "not_rounded", inky );
3206     }
3208     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
3209     sp_rtb_sensitivize( holder );
3211     sigc::connection *connection = new sigc::connection(
3212         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
3213         );
3214     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3215     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3218 //########################
3219 //##       3D Box       ##
3220 //########################
3222 // normalize angle so that it lies in the interval [0,360]
3223 static double box3d_normalize_angle (double a) {
3224     double angle = a + ((int) (a/360.0))*360;
3225     if (angle < 0) {
3226         angle += 360.0;
3227     }
3228     return angle;
3231 static void
3232 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
3233                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
3234     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
3235     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
3236     //       are reset).
3237     bool is_infinite = !persp3d_VP_is_finite(persp->perspective_impl, axis);
3239     if (is_infinite) {
3240         gtk_toggle_action_set_active(tact, TRUE);
3241         gtk_action_set_sensitive(act, TRUE);
3243         double angle = persp3d_get_infinite_angle(persp, axis);
3244         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
3245             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
3246         }
3247     } else {
3248         gtk_toggle_action_set_active(tact, FALSE);
3249         gtk_action_set_sensitive(act, FALSE);
3250     }
3253 static void
3254 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
3255     if (!persp_repr) {
3256         g_print ("No perspective given to box3d_resync_toolbar().\n");
3257         return;
3258     }
3260     GtkWidget *tbl = GTK_WIDGET(data);
3261     GtkAdjustment *adj = 0;
3262     GtkAction *act = 0;
3263     GtkToggleAction *tact = 0;
3264     Persp3D *persp = persp3d_get_from_repr(persp_repr);
3265     if (!persp) {
3266         // Hmm, is it an error if this happens?
3267         return;
3268     }
3269     {
3270         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
3271         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
3272         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
3274         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
3275     }
3276     {
3277         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
3278         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
3279         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
3281         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
3282     }
3283     {
3284         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
3285         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
3286         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
3288         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
3289     }
3292 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3293                                                   gchar const */*old_value*/, gchar const */*new_value*/,
3294                                                   bool /*is_interactive*/, gpointer data)
3296     GtkWidget *tbl = GTK_WIDGET(data);
3298     // quit if run by the attr_changed or selection changed listener
3299     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3300         return;
3301     }
3303     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
3304     // sp_document_maybe_done() when the document is undo insensitive)
3305     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3307     // TODO: Only update the appropriate part of the toolbar
3308 //    if (!strcmp(name, "inkscape:vp_z")) {
3309         box3d_resync_toolbar(repr, G_OBJECT(tbl));
3310 //    }
3312     Persp3D *persp = persp3d_get_from_repr(repr);
3313     persp3d_update_box_reprs(persp);
3315     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3318 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
3320     NULL, /* child_added */
3321     NULL, /* child_removed */
3322     box3d_persp_tb_event_attr_changed,
3323     NULL, /* content_changed */
3324     NULL  /* order_changed */
3325 };
3327 /**
3328  *  \param selection Should not be NULL.
3329  */
3330 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
3331 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
3332 static void
3333 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3335     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
3336     // disable the angle entry fields for this direction (otherwise entering a value in them should only
3337     // update the perspectives with infinite VPs and leave the other ones untouched).
3339     Inkscape::XML::Node *persp_repr = NULL;
3340     purge_repr_listener(tbl, tbl);
3342     SPItem *item = selection->singleItem();
3343     if (item && SP_IS_BOX3D(item)) {
3344         // FIXME: Also deal with multiple selected boxes
3345         SPBox3D *box = SP_BOX3D(item);
3346         Persp3D *persp = box3d_get_perspective(box);
3347         persp_repr = SP_OBJECT_REPR(persp);
3348         if (persp_repr) {
3349             g_object_set_data(tbl, "repr", persp_repr);
3350             Inkscape::GC::anchor(persp_repr);
3351             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
3352             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
3353         }
3355         inkscape_active_document()->setCurrentPersp3D(persp3d_get_from_repr(persp_repr));
3356         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3357         prefs->setString("/tools/shapes/3dbox/persp", persp_repr->attribute("id"));
3359         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
3360         box3d_resync_toolbar(persp_repr, tbl);
3361         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
3362     }
3365 static void
3366 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
3368     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3369     SPDocument *document = sp_desktop_document(desktop);
3371     // quit if run by the attr_changed or selection changed listener
3372     if (g_object_get_data( dataKludge, "freeze" )) {
3373         return;
3374     }
3376     // in turn, prevent listener from responding
3377     g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
3379     std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
3380     if (sel_persps.empty()) {
3381         // this can happen when the document is created; we silently ignore it
3382         return;
3383     }
3384     Persp3D *persp = sel_persps.front();
3386     persp->perspective_impl->tmat.set_infinite_direction (axis, adj->value);
3387     SP_OBJECT(persp)->updateRepr();
3389     // TODO: use the correct axis here, too
3390     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
3392     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
3396 static void
3397 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3399     box3d_angle_value_changed(adj, dataKludge, Proj::X);
3402 static void
3403 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3405     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
3408 static void
3409 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3411     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
3415 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
3417     // TODO: Take all selected perspectives into account
3418     std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
3419     if (sel_persps.empty()) {
3420         // this can happen when the document is created; we silently ignore it
3421         return;
3422     }
3423     Persp3D *persp = sel_persps.front();
3425     bool set_infinite = gtk_toggle_action_get_active(act);
3426     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
3429 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3431     box3d_vp_state_changed(act, box3d_angle, Proj::X);
3434 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3436     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
3439 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3441     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
3444 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3446     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3447     EgeAdjustmentAction* eact = 0;
3448     SPDocument *document = sp_desktop_document (desktop);
3449     Persp3DImpl *persp_impl = document->getCurrentPersp3DImpl();
3451     EgeAdjustmentAction* box3d_angle_x = 0;
3452     EgeAdjustmentAction* box3d_angle_y = 0;
3453     EgeAdjustmentAction* box3d_angle_z = 0;
3455     /* Angle X */
3456     {
3457         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3458         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3459         eact = create_adjustment_action( "3DBoxAngleXAction",
3460                                          _("Angle in X direction"), _("Angle X:"),
3461                                          // Translators: PL is short for 'perspective line'
3462                                          _("Angle of PLs in X direction"),
3463                                          "/tools/shapes/3dbox/box3d_angle_x", 30,
3464                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
3465                                          -360.0, 360.0, 1.0, 10.0,
3466                                          labels, values, G_N_ELEMENTS(labels),
3467                                          box3d_angle_x_value_changed );
3468         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3469         g_object_set_data( holder, "box3d_angle_x_action", eact );
3470         box3d_angle_x = eact;
3471     }
3473     if (!persp_impl || !persp3d_VP_is_finite(persp_impl, Proj::X)) {
3474         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3475     } else {
3476         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3477     }
3480     /* VP X state */
3481     {
3482         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
3483                                                       // Translators: VP is short for 'vanishing point'
3484                                                       _("State of VP in X direction"),
3485                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
3486                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3487                                                       Inkscape::ICON_SIZE_DECORATION );
3488         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3489         g_object_set_data( holder, "box3d_vp_x_state_action", act );
3490         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
3491         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3492         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3493     }
3495     /* Angle Y */
3496     {
3497         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3498         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3499         eact = create_adjustment_action( "3DBoxAngleYAction",
3500                                          _("Angle in Y direction"), _("Angle Y:"),
3501                                          // Translators: PL is short for 'perspective line'
3502                                          _("Angle of PLs in Y direction"),
3503                                          "/tools/shapes/3dbox/box3d_angle_y", 30,
3504                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3505                                          -360.0, 360.0, 1.0, 10.0,
3506                                          labels, values, G_N_ELEMENTS(labels),
3507                                          box3d_angle_y_value_changed );
3508         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3509         g_object_set_data( holder, "box3d_angle_y_action", eact );
3510         box3d_angle_y = eact;
3511     }
3513     if (!persp_impl || !persp3d_VP_is_finite(persp_impl, Proj::Y)) {
3514         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3515     } else {
3516         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3517     }
3519     /* VP Y state */
3520     {
3521         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
3522                                                       // Translators: VP is short for 'vanishing point'
3523                                                       _("State of VP in Y direction"),
3524                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
3525                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3526                                                       Inkscape::ICON_SIZE_DECORATION );
3527         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3528         g_object_set_data( holder, "box3d_vp_y_state_action", act );
3529         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
3530         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3531         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3532     }
3534     /* Angle Z */
3535     {
3536         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3537         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3538         eact = create_adjustment_action( "3DBoxAngleZAction",
3539                                          _("Angle in Z direction"), _("Angle Z:"),
3540                                          // Translators: PL is short for 'perspective line'
3541                                          _("Angle of PLs in Z direction"),
3542                                          "/tools/shapes/3dbox/box3d_angle_z", 30,
3543                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3544                                          -360.0, 360.0, 1.0, 10.0,
3545                                          labels, values, G_N_ELEMENTS(labels),
3546                                          box3d_angle_z_value_changed );
3547         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3548         g_object_set_data( holder, "box3d_angle_z_action", eact );
3549         box3d_angle_z = eact;
3550     }
3552     if (!persp_impl || !persp3d_VP_is_finite(persp_impl, Proj::Z)) {
3553         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3554     } else {
3555         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3556     }
3558     /* VP Z state */
3559     {
3560         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3561                                                       // Translators: VP is short for 'vanishing point'
3562                                                       _("State of VP in Z direction"),
3563                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3564                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3565                                                       Inkscape::ICON_SIZE_DECORATION );
3566         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3567         g_object_set_data( holder, "box3d_vp_z_state_action", act );
3568         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3569         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3570         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3571     }
3573     sigc::connection *connection = new sigc::connection(
3574         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3575        );
3576     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3577     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3580 //########################
3581 //##       Spiral       ##
3582 //########################
3584 static void
3585 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, Glib::ustring const &value_name)
3587     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3589     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3590         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3591         prefs->setDouble("/tools/shapes/spiral/" + value_name, adj->value);
3592     }
3594     // quit if run by the attr_changed listener
3595     if (g_object_get_data( tbl, "freeze" )) {
3596         return;
3597     }
3599     // in turn, prevent listener from responding
3600     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3602     gchar* namespaced_name = g_strconcat("sodipodi:", value_name.data(), NULL);
3604     bool modmade = false;
3605     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3606          items != NULL;
3607          items = items->next)
3608     {
3609         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3610             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3611             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3612             SP_OBJECT((SPItem *) items->data)->updateRepr();
3613             modmade = true;
3614         }
3615     }
3617     g_free(namespaced_name);
3619     if (modmade) {
3620         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3621                                    _("Change spiral"));
3622     }
3624     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3627 static void
3628 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3630     sp_spl_tb_value_changed(adj, tbl, "revolution");
3633 static void
3634 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3636     sp_spl_tb_value_changed(adj, tbl, "expansion");
3639 static void
3640 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3642     sp_spl_tb_value_changed(adj, tbl, "t0");
3645 static void
3646 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3648     GtkWidget *tbl = GTK_WIDGET(obj);
3650     GtkAdjustment *adj;
3652     // fixme: make settable
3653     gdouble rev = 5;
3654     gdouble exp = 1.0;
3655     gdouble t0 = 0.0;
3657     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3658     gtk_adjustment_set_value(adj, rev);
3659     gtk_adjustment_value_changed(adj);
3661     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3662     gtk_adjustment_set_value(adj, exp);
3663     gtk_adjustment_value_changed(adj);
3665     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3666     gtk_adjustment_set_value(adj, t0);
3667     gtk_adjustment_value_changed(adj);
3669     spinbutton_defocus(GTK_OBJECT(tbl));
3673 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3674                                          gchar const */*old_value*/, gchar const */*new_value*/,
3675                                          bool /*is_interactive*/, gpointer data)
3677     GtkWidget *tbl = GTK_WIDGET(data);
3679     // quit if run by the _changed callbacks
3680     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3681         return;
3682     }
3684     // in turn, prevent callbacks from responding
3685     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3687     GtkAdjustment *adj;
3688     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3689     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3691     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3692     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3694     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3695     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3697     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3701 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3702     NULL, /* child_added */
3703     NULL, /* child_removed */
3704     spiral_tb_event_attr_changed,
3705     NULL, /* content_changed */
3706     NULL  /* order_changed */
3707 };
3709 static void
3710 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3712     int n_selected = 0;
3713     Inkscape::XML::Node *repr = NULL;
3715     purge_repr_listener( tbl, tbl );
3717     for (GSList const *items = selection->itemList();
3718          items != NULL;
3719          items = items->next)
3720     {
3721         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3722             n_selected++;
3723             repr = SP_OBJECT_REPR((SPItem *) items->data);
3724         }
3725     }
3727     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3729     if (n_selected == 0) {
3730         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3731     } else if (n_selected == 1) {
3732         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3734         if (repr) {
3735             g_object_set_data( tbl, "repr", repr );
3736             Inkscape::GC::anchor(repr);
3737             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3738             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3739         }
3740     } else {
3741         // FIXME: implement averaging of all parameters for multiple selected
3742         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3743         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3744     }
3748 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3750     EgeAdjustmentAction* eact = 0;
3751     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
3753     {
3754         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3755         ege_output_action_set_use_markup( act, TRUE );
3756         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3757         g_object_set_data( holder, "mode_action", act );
3758     }
3760     /* Revolution */
3761     {
3762         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3763         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3764         eact = create_adjustment_action( "SpiralRevolutionAction",
3765                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3766                                          "/tools/shapes/spiral/revolution", 3.0,
3767                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3768                                          0.01, 1024.0, 0.1, 1.0,
3769                                          labels, values, G_N_ELEMENTS(labels),
3770                                          sp_spl_tb_revolution_value_changed, 1, 2);
3771         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3772     }
3774     /* Expansion */
3775     {
3776         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3777         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3778         eact = create_adjustment_action( "SpiralExpansionAction",
3779                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3780                                          "/tools/shapes/spiral/expansion", 1.0,
3781                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3782                                          0.0, 1000.0, 0.01, 1.0,
3783                                          labels, values, G_N_ELEMENTS(labels),
3784                                          sp_spl_tb_expansion_value_changed);
3785         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3786     }
3788     /* T0 */
3789     {
3790         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3791         gdouble values[] = {0, 0.5, 0.9};
3792         eact = create_adjustment_action( "SpiralT0Action",
3793                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3794                                          "/tools/shapes/spiral/t0", 0.0,
3795                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3796                                          0.0, 0.999, 0.01, 1.0,
3797                                          labels, values, G_N_ELEMENTS(labels),
3798                                          sp_spl_tb_t0_value_changed);
3799         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3800     }
3802     /* Reset */
3803     {
3804         InkAction* inky = ink_action_new( "SpiralResetAction",
3805                                           _("Defaults"),
3806                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3807                                           GTK_STOCK_CLEAR,
3808                                           secondarySize );
3809         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3810         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3811     }
3814     sigc::connection *connection = new sigc::connection(
3815         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3816         );
3817     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3818     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3821 //########################
3822 //##     Pen/Pencil     ##
3823 //########################
3825 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3826 static Glib::ustring const
3827 freehand_tool_name(GObject *dataKludge)
3829     SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3830     return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3831              ? "/tools/freehand/pen"
3832              : "/tools/freehand/pencil" );
3835 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3837     gint mode = ege_select_one_action_get_active(act);
3839     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3840     prefs->setInt(freehand_tool_name(tbl) + "/freehand-mode", mode);
3842     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3844     // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3845     // preparatory work here
3846     if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3847         SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3848         sp_pen_context_set_polyline_mode(pc);
3849     }
3852 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3854     /* Freehand mode toggle buttons */
3855     {
3856         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3857         guint freehandMode = prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/freehand-mode" : "/tools/freehand/pen/freehand-mode" ), 0);
3858         Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
3860         {
3861             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3863             GtkTreeIter iter;
3864             gtk_list_store_append( model, &iter );
3865             gtk_list_store_set( model, &iter,
3866                                 0, _("Bezier"),
3867                                 1, _("Create regular Bezier path"),
3868                                 2, INKSCAPE_ICON_PATH_MODE_BEZIER,
3869                                 -1 );
3871             gtk_list_store_append( model, &iter );
3872             gtk_list_store_set( model, &iter,
3873                                 0, _("Spiro"),
3874                                 1, _("Create Spiro path"),
3875                                 2, INKSCAPE_ICON_PATH_MODE_SPIRO,
3876                                 -1 );
3878             if (!tool_is_pencil) {
3879                 gtk_list_store_append( model, &iter );
3880                 gtk_list_store_set( model, &iter,
3881                                     0, _("Zigzag"),
3882                                     1, _("Create a sequence of straight line segments"),
3883                                     2, INKSCAPE_ICON_PATH_MODE_POLYLINE,
3884                                     -1 );
3886                 gtk_list_store_append( model, &iter );
3887                 gtk_list_store_set( model, &iter,
3888                                     0, _("Paraxial"),
3889                                     1, _("Create a sequence of paraxial line segments"),
3890                                     2, INKSCAPE_ICON_PATH_MODE_POLYLINE_PARAXIAL,
3891                                     -1 );
3892             }
3894             EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3895                                                                 "FreehandModeActionPencil" :
3896                                                                 "FreehandModeActionPen",
3897                                                                 (_("Mode:")), (_("Mode of new lines drawn by this tool")), NULL, GTK_TREE_MODEL(model) );
3898             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3900             ege_select_one_action_set_appearance( act, "full" );
3901             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3902             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3903             ege_select_one_action_set_icon_column( act, 2 );
3904             ege_select_one_action_set_icon_size( act, secondarySize );
3905             ege_select_one_action_set_tooltip_column( act, 1  );
3907             ege_select_one_action_set_active( act, freehandMode);
3908             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3909         }
3910     }
3913 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3914     gint shape = ege_select_one_action_get_active( act );
3915     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3916     prefs->setInt(freehand_tool_name(dataKludge) + "/shape", shape);
3919 /**
3920  * \brief Generate the list of freehand advanced shape option entries.
3921  */
3922 GList * freehand_shape_dropdown_items_list() {
3923     GList *glist = NULL;
3925     glist = g_list_append (glist, _("None"));
3926     glist = g_list_append (glist, _("Triangle in"));
3927     glist = g_list_append (glist, _("Triangle out"));
3928     glist = g_list_append (glist, _("Ellipse"));
3929     glist = g_list_append (glist, _("From clipboard"));
3931     return glist;
3934 static void
3935 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3936     /*advanced shape options */
3937     {
3938         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3939         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3941         GList* items = 0;
3942         gint count = 0;
3943         for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3944         {
3945             GtkTreeIter iter;
3946             gtk_list_store_append( model, &iter );
3947             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3948             count++;
3949         }
3950         g_list_free( items );
3951         items = 0;
3952         EgeSelectOneAction* act1 = ege_select_one_action_new(
3953             tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3954             _("Shape:"), (_("Shape of new paths drawn by this tool")), NULL, GTK_TREE_MODEL(model));
3955         g_object_set( act1, "short_label", _("Shape:"), NULL );
3956         ege_select_one_action_set_appearance( act1, "compact" );
3957         ege_select_one_action_set_active( act1, prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/shape" : "/tools/freehand/pen/shape" ), 0) );
3958         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3959         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3960         g_object_set_data( holder, "shape_action", act1 );
3961     }
3964 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3966     sp_add_freehand_mode_toggle(mainActions, holder, false);
3967     freehand_add_advanced_shape_options(mainActions, holder, false);
3971 static void
3972 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3974     GtkWidget *tbl = GTK_WIDGET(obj);
3976     GtkAdjustment *adj;
3978     // fixme: make settable
3979     gdouble tolerance = 4;
3981     adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3982     gtk_adjustment_set_value(adj, tolerance);
3983     gtk_adjustment_value_changed(adj);
3985     spinbutton_defocus(GTK_OBJECT(tbl));
3988 static void
3989 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3991     // quit if run by the attr_changed listener
3992     if (g_object_get_data( tbl, "freeze" )) {
3993         return;
3994     }
3995     // in turn, prevent listener from responding
3996     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3997     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3998     prefs->setDouble("/tools/freehand/pencil/tolerance", adj->value);
3999     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4002 /*
4003 class PencilToleranceObserver : public Inkscape::Preferences::Observer {
4004 public:
4005     PencilToleranceObserver(Glib::ustring const &path, GObject *x) : Observer(path), _obj(x)
4006     {
4007         g_object_set_data(_obj, "prefobserver", this);
4008     }
4009     virtual ~PencilToleranceObserver() {
4010         if (g_object_get_data(_obj, "prefobserver") == this) {
4011             g_object_set_data(_obj, "prefobserver", NULL);
4012         }
4013     }
4014     virtual void notify(Inkscape::Preferences::Entry const &val) {
4015         GObject* tbl = _obj;
4016         if (g_object_get_data( tbl, "freeze" )) {
4017             return;
4018         }
4019         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4021         GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl, "tolerance");
4023         double v = val.getDouble(adj->value);
4024         gtk_adjustment_set_value(adj, v);
4025         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4026     }
4027 private:
4028     GObject *_obj;
4029 };
4030 */
4032 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4034     sp_add_freehand_mode_toggle(mainActions, holder, true);
4036     EgeAdjustmentAction* eact = 0;
4038     /* Tolerance */
4039     {
4040         gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
4041         gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
4042         eact = create_adjustment_action( "PencilToleranceAction",
4043                                          _("Smoothing:"), _("Smoothing: "),
4044                  _("How much smoothing (simplifying) is applied to the line"),
4045                                          "/tools/freehand/pencil/tolerance",
4046                                          3.0,
4047                                          GTK_WIDGET(desktop->canvas), NULL,
4048                                          holder, TRUE, "altx-pencil",
4049                                          1, 100.0, 0.5, 1.0,
4050                                          labels, values, G_N_ELEMENTS(labels),
4051                                          sp_pencil_tb_tolerance_value_changed,
4052                                          1, 2);
4053         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4054         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4055     }
4057     /* advanced shape options */
4058     freehand_add_advanced_shape_options(mainActions, holder, true);
4060     /* Reset */
4061     {
4062         InkAction* inky = ink_action_new( "PencilResetAction",
4063                                           _("Defaults"),
4064                                           _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
4065                                           GTK_STOCK_CLEAR,
4066                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4067         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
4068         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4069     }
4071     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4076 //########################
4077 //##       Tweak        ##
4078 //########################
4080 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4082     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4083     prefs->setDouble( "/tools/tweak/width", adj->value * 0.01 );
4086 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4088     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4089     prefs->setDouble( "/tools/tweak/force", adj->value * 0.01 );
4092 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4094     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4095     prefs->setBool("/tools/tweak/usepressure", gtk_toggle_action_get_active(act));
4098 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4100     int mode = ege_select_one_action_get_active( act );
4101     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4102     prefs->setInt("/tools/tweak/mode", mode);
4104     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
4105     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
4106     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
4107     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
4108     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
4109     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
4110     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
4111         if (doh) gtk_action_set_sensitive (doh, TRUE);
4112         if (dos) gtk_action_set_sensitive (dos, TRUE);
4113         if (dol) gtk_action_set_sensitive (dol, TRUE);
4114         if (doo) gtk_action_set_sensitive (doo, TRUE);
4115         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
4116         if (fid) gtk_action_set_sensitive (fid, FALSE);
4117     } else {
4118         if (doh) gtk_action_set_sensitive (doh, FALSE);
4119         if (dos) gtk_action_set_sensitive (dos, FALSE);
4120         if (dol) gtk_action_set_sensitive (dol, FALSE);
4121         if (doo) gtk_action_set_sensitive (doo, FALSE);
4122         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
4123         if (fid) gtk_action_set_sensitive (fid, TRUE);
4124     }
4127 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4129     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4130     prefs->setDouble( "/tools/tweak/fidelity", adj->value * 0.01 );
4133 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
4134     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4135     prefs->setBool("/tools/tweak/doh", gtk_toggle_action_get_active(act));
4137 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
4138     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4139     prefs->setBool("/tools/tweak/dos", gtk_toggle_action_get_active(act));
4141 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
4142     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4143     prefs->setBool("/tools/tweak/dol", gtk_toggle_action_get_active(act));
4145 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
4146     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4147     prefs->setBool("/tools/tweak/doo", gtk_toggle_action_get_active(act));
4150 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4152     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
4153     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4155     {
4156         /* Width */
4157         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
4158         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4159         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
4160                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
4161                                                               "/tools/tweak/width", 15,
4162                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
4163                                                               1, 100, 1.0, 10.0,
4164                                                               labels, values, G_N_ELEMENTS(labels),
4165                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
4166         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4167         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4168         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4169     }
4172     {
4173         /* Force */
4174         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
4175         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4176         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
4177                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
4178                                                               "/tools/tweak/force", 20,
4179                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
4180                                                               1, 100, 1.0, 10.0,
4181                                                               labels, values, G_N_ELEMENTS(labels),
4182                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
4183         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4184         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4185         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4186     }
4188     /* Mode */
4189     {
4190         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4192         GtkTreeIter iter;
4193         gtk_list_store_append( model, &iter );
4194         gtk_list_store_set( model, &iter,
4195                             0, _("Move mode"),
4196                             1, _("Move objects in any direction"),
4197                             2, INKSCAPE_ICON_OBJECT_TWEAK_PUSH,
4198                             -1 );
4200         gtk_list_store_append( model, &iter );
4201         gtk_list_store_set( model, &iter,
4202                             0, _("Move in/out mode"),
4203                             1, _("Move objects towards cursor; with Shift from cursor"),
4204                             2, INKSCAPE_ICON_OBJECT_TWEAK_ATTRACT,
4205                             -1 );
4207         gtk_list_store_append( model, &iter );
4208         gtk_list_store_set( model, &iter,
4209                             0, _("Move jitter mode"),
4210                             1, _("Move objects in random directions"),
4211                             2, INKSCAPE_ICON_OBJECT_TWEAK_RANDOMIZE,
4212                             -1 );
4214         gtk_list_store_append( model, &iter );
4215         gtk_list_store_set( model, &iter,
4216                             0, _("Scale mode"),
4217                             1, _("Shrink objects, with Shift enlarge"),
4218                             2, INKSCAPE_ICON_OBJECT_TWEAK_SHRINK,
4219                             -1 );
4221         gtk_list_store_append( model, &iter );
4222         gtk_list_store_set( model, &iter,
4223                             0, _("Rotate mode"),
4224                             1, _("Rotate objects, with Shift counterclockwise"),
4225                             2, INKSCAPE_ICON_OBJECT_TWEAK_ROTATE,
4226                             -1 );
4228         gtk_list_store_append( model, &iter );
4229         gtk_list_store_set( model, &iter,
4230                             0, _("Duplicate/delete mode"),
4231                             1, _("Duplicate objects, with Shift delete"),
4232                             2, INKSCAPE_ICON_OBJECT_TWEAK_DUPLICATE,
4233                             -1 );
4235         gtk_list_store_append( model, &iter );
4236         gtk_list_store_set( model, &iter,
4237                             0, _("Push mode"),
4238                             1, _("Push parts of paths in any direction"),
4239                             2, INKSCAPE_ICON_PATH_TWEAK_PUSH,
4240                             -1 );
4242         gtk_list_store_append( model, &iter );
4243         gtk_list_store_set( model, &iter,
4244                             0, _("Shrink/grow mode"),
4245                             1, _("Shrink (inset) parts of paths; with Shift grow (outset)"),
4246                             2, INKSCAPE_ICON_PATH_TWEAK_SHRINK,
4247                             -1 );
4249         gtk_list_store_append( model, &iter );
4250         gtk_list_store_set( model, &iter,
4251                             0, _("Attract/repel mode"),
4252                             1, _("Attract parts of paths towards cursor; with Shift from cursor"),
4253                             2, INKSCAPE_ICON_PATH_TWEAK_ATTRACT,
4254                             -1 );
4256         gtk_list_store_append( model, &iter );
4257         gtk_list_store_set( model, &iter,
4258                             0, _("Roughen mode"),
4259                             1, _("Roughen parts of paths"),
4260                             2, INKSCAPE_ICON_PATH_TWEAK_ROUGHEN,
4261                             -1 );
4263         gtk_list_store_append( model, &iter );
4264         gtk_list_store_set( model, &iter,
4265                             0, _("Color paint mode"),
4266                             1, _("Paint the tool's color upon selected objects"),
4267                             2, INKSCAPE_ICON_OBJECT_TWEAK_PAINT,
4268                             -1 );
4270         gtk_list_store_append( model, &iter );
4271         gtk_list_store_set( model, &iter,
4272                             0, _("Color jitter mode"),
4273                             1, _("Jitter the colors of selected objects"),
4274                             2, INKSCAPE_ICON_OBJECT_TWEAK_JITTER_COLOR,
4275                             -1 );
4277         gtk_list_store_append( model, &iter );
4278         gtk_list_store_set( model, &iter,
4279                             0, _("Blur mode"),
4280                             1, _("Blur selected objects more; with Shift, blur less"),
4281                             2, INKSCAPE_ICON_OBJECT_TWEAK_BLUR,
4282                             -1 );
4285         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4286         g_object_set( act, "short_label", _("Mode:"), NULL );
4287         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4288         g_object_set_data( holder, "mode_action", act );
4290         ege_select_one_action_set_appearance( act, "full" );
4291         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4292         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4293         ege_select_one_action_set_icon_column( act, 2 );
4294         ege_select_one_action_set_icon_size( act, secondarySize );
4295         ege_select_one_action_set_tooltip_column( act, 1  );
4297         gint mode = prefs->getInt("/tools/tweak/mode", 0);
4298         ege_select_one_action_set_active( act, mode );
4299         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
4301         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
4302     }
4304     guint mode = prefs->getInt("/tools/tweak/mode", 0);
4306     {
4307         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
4308         ege_output_action_set_use_markup( act, TRUE );
4309         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4310         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4311             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4312         g_object_set_data( holder, "tweak_channels_label", act);
4313     }
4315     {
4316         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
4317                                                       _("Hue"),
4318                                                       _("In color mode, act on objects' hue"),
4319                                                       NULL,
4320                                                       Inkscape::ICON_SIZE_DECORATION );
4321         //TRANSLATORS:  "H" here stands for hue
4322         g_object_set( act, "short_label", _("H"), NULL );
4323         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4324         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
4325         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doh", true) );
4326         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4327             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4328         g_object_set_data( holder, "tweak_doh", act);
4329     }
4330     {
4331         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
4332                                                       _("Saturation"),
4333                                                       _("In color mode, act on objects' saturation"),
4334                                                       NULL,
4335                                                       Inkscape::ICON_SIZE_DECORATION );
4336         //TRANSLATORS: "S" here stands for Saturation
4337         g_object_set( act, "short_label", _("S"), NULL );
4338         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4339         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
4340         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dos", true) );
4341         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4342             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4343         g_object_set_data( holder, "tweak_dos", act );
4344     }
4345     {
4346         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
4347                                                       _("Lightness"),
4348                                                       _("In color mode, act on objects' lightness"),
4349                                                       NULL,
4350                                                       Inkscape::ICON_SIZE_DECORATION );
4351         //TRANSLATORS: "L" here stands for Lightness
4352         g_object_set( act, "short_label", _("L"), NULL );
4353         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4354         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
4355         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dol", true) );
4356         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4357             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4358         g_object_set_data( holder, "tweak_dol", act );
4359     }
4360     {
4361         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
4362                                                       _("Opacity"),
4363                                                       _("In color mode, act on objects' opacity"),
4364                                                       NULL,
4365                                                       Inkscape::ICON_SIZE_DECORATION );
4366         //TRANSLATORS: "O" here stands for Opacity
4367         g_object_set( act, "short_label", _("O"), NULL );
4368         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4369         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
4370         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doo", true) );
4371         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4372             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4373         g_object_set_data( holder, "tweak_doo", act );
4374     }
4376     {   /* Fidelity */
4377         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
4378         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4379         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
4380                                                               _("Fidelity"), _("Fidelity:"),
4381                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
4382                                                               "/tools/tweak/fidelity", 50,
4383                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
4384                                                               1, 100, 1.0, 10.0,
4385                                                               labels, values, G_N_ELEMENTS(labels),
4386                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
4387         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4388         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4389         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
4390             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
4391         g_object_set_data( holder, "tweak_fidelity", eact );
4392     }
4395     /* Use Pressure button */
4396     {
4397         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
4398                                                       _("Pressure"),
4399                                                       _("Use the pressure of the input device to alter the force of tweak action"),
4400                                                       INKSCAPE_ICON_DRAW_USE_PRESSURE,
4401                                                       Inkscape::ICON_SIZE_DECORATION );
4402         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4403         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
4404         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/usepressure", true) );
4405     }
4410 //########################
4411 //##       Spray        ##
4412 //########################
4414 static void sp_spray_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4416     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4417     prefs->setDouble( "/tools/spray/width", adj->value );
4420 static void sp_spray_mean_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4422     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4423     prefs->setDouble( "/tools/spray/mean", adj->value );
4426 static void sp_spray_standard_deviation_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4428     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4429     prefs->setDouble( "/tools/spray/standard_deviation", adj->value );
4432 static void sp_spray_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4434     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4435     prefs->setBool("/tools/spray/usepressure", gtk_toggle_action_get_active(act));
4438 static void sp_spray_mode_changed( EgeSelectOneAction *act, GObject */*tbl*/ )
4440     int mode = ege_select_one_action_get_active( act );
4441     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4442     prefs->setInt("/tools/spray/mode", mode);
4445 static void sp_spray_population_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4447     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4448     prefs->setDouble( "/tools/spray/population", adj->value );
4451 static void sp_spray_rotation_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4453     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4454     prefs->setDouble( "/tools/spray/rotation_variation", adj->value );
4457 static void sp_spray_scale_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4459     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4460     prefs->setDouble( "/tools/spray/scale_variation", adj->value );
4464 static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4466     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
4467     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4469     {
4470         /* Width */
4471         gchar const* labels[] = {_("(narrow spray)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad spray)")};
4472         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4473         EgeAdjustmentAction *eact = create_adjustment_action( "SprayWidthAction",
4474                                                               _("Width"), _("Width:"), _("The width of the spray area (relative to the visible canvas area)"),
4475                                                               "/tools/spray/width", 15,
4476                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spray",
4477                                                               1, 100, 1.0, 10.0,
4478                                                               labels, values, G_N_ELEMENTS(labels),
4479                                                               sp_spray_width_value_changed,  1, 0 );
4480         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4481         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4482         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4483     }
4485     {
4486         /* Mean */
4487         gchar const* labels[] = {_("(minimum mean)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum mean)")};
4488         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4489         EgeAdjustmentAction *eact = create_adjustment_action( "SprayMeanAction",
4490                                                               _("Focus"), _("Focus:"), _("0 to spray a spot. Increase to enlarge the ring radius."),
4491                                                               "/tools/spray/mean", 0,
4492                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-mean",
4493                                                               0, 100, 1.0, 10.0,
4494                                                               labels, values, G_N_ELEMENTS(labels),
4495                                                               sp_spray_mean_value_changed,  1, 0 );
4496         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4497         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4498         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4499     }
4501     {
4502         /* Standard_deviation */
4503         gchar const* labels[] = {_("(minimum scatter)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum scatter)")};
4504         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4505         EgeAdjustmentAction *eact = create_adjustment_action( "SprayStandard_deviationAction",
4506                                                               _("Scatter"), _("Scatter:"), _("Increase to scatter sprayed objects."),
4507                                                               "/tools/spray/standard_deviation", 70,
4508                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-standard_deviation",
4509                                                               1, 100, 1.0, 10.0,
4510                                                               labels, values, G_N_ELEMENTS(labels),
4511                                                               sp_spray_standard_deviation_value_changed,  1, 0 );
4512         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4513         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4514         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4515     }
4517     /* Mode */
4518     {
4519         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4521         GtkTreeIter iter;
4522         gtk_list_store_append( model, &iter );
4523         gtk_list_store_set( model, &iter,
4524                             0, _("Spray with copies"),
4525                             1, _("Spray copies of the initial selection"),
4526                             2, INKSCAPE_ICON_SPRAY_COPY_MODE,
4527                             -1 );
4529         gtk_list_store_append( model, &iter );
4530         gtk_list_store_set( model, &iter,
4531                             0, _("Spray with clones"),
4532                             1, _("Spray clones of the initial selection"),
4533                             2, INKSCAPE_ICON_SPRAY_CLONE_MODE,
4534                             -1 );
4536         gtk_list_store_append( model, &iter );
4537         gtk_list_store_set( model, &iter,
4538                             0, _("Spray single path"),
4539                             1, _("Spray objects in a single path"),
4540                             2, INKSCAPE_ICON_SPRAY_UNION_MODE,
4541                             -1 );
4543         EgeSelectOneAction* act = ege_select_one_action_new( "SprayModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4544         g_object_set( act, "short_label", _("Mode:"), NULL );
4545         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4546         g_object_set_data( holder, "mode_action", act );
4548         ege_select_one_action_set_appearance( act, "full" );
4549         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4550         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4551         ege_select_one_action_set_icon_column( act, 2 );
4552         ege_select_one_action_set_icon_size( act, secondarySize );
4553         ege_select_one_action_set_tooltip_column( act, 1  );
4555         gint mode = prefs->getInt("/tools/spray/mode", 1);
4556         ege_select_one_action_set_active( act, mode );
4557         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_spray_mode_changed), holder );
4559         g_object_set_data( G_OBJECT(holder), "spray_tool_mode", act);
4560     }
4562     {   /* Population */
4563         gchar const* labels[] = {_("(low population)"), 0, 0, _("(default)"), 0, 0, _("(high population)")};
4564         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4565         EgeAdjustmentAction *eact = create_adjustment_action( "SprayPopulationAction",
4566                                                               _("Amount"), _("Amount:"),
4567                                                               _("Adjusts the number of items sprayed per clic."),
4568                                                               "/tools/spray/population", 70,
4569                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-population",
4570                                                               1, 100, 1.0, 10.0,
4571                                                               labels, values, G_N_ELEMENTS(labels),
4572                                                               sp_spray_population_value_changed,  1, 0 );
4573         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4574         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4575         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4576         g_object_set_data( holder, "spray_population", eact );
4577     }
4579     /* Use Pressure button */
4580     {
4581         InkToggleAction* act = ink_toggle_action_new( "SprayPressureAction",
4582                                                       _("Pressure"),
4583                                                       _("Use the pressure of the input device to alter the amount of sprayed objects."),
4584                                                       "use_pressure",
4585                                                       Inkscape::ICON_SIZE_DECORATION );
4586         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4587         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_spray_pressure_state_changed), NULL);
4588         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/spray/usepressure", true) );
4589     }
4591     {   /* Rotation */
4592         gchar const* labels[] = {_("(low rotation variation)"), 0, 0, _("(default)"), 0, 0, _("(high rotation variation)")};
4593         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4594         EgeAdjustmentAction *eact = create_adjustment_action( "SprayRotationAction",
4595                                                               _("Rotation"), _("Rotation:"),
4596                                                               // xgettext:no-c-format
4597                                                               _("Variation of the rotation of the sprayed objects. 0% for the same rotation than the original object."),
4598                                                               "/tools/spray/rotation_variation", 0,
4599                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-rotation",
4600                                                               0, 100, 1.0, 10.0,
4601                                                               labels, values, G_N_ELEMENTS(labels),
4602                                                               sp_spray_rotation_value_changed,  1, 0 );
4603         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4604         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4605         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4606         g_object_set_data( holder, "spray_rotation", eact );
4607     }
4609     {   /* Scale */
4610         gchar const* labels[] = {_("(low scale variation)"), 0, 0, _("(default)"), 0, 0, _("(high scale variation)")};
4611         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4612         EgeAdjustmentAction *eact = create_adjustment_action( "SprayScaleAction",
4613                                                               _("Scale"), _("Scale:"),
4614                                                               // xgettext:no-c-format
4615                                                               _("Variation in the scale of the sprayed objects. 0% for the same scale than the original object."),
4616                                                               "/tools/spray/scale_variation", 0,
4617                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-scale",
4618                                                               0, 100, 1.0, 10.0,
4619                                                               labels, values, G_N_ELEMENTS(labels),
4620                                                               sp_spray_scale_value_changed,  1, 0 );
4621         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4622         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4623         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4624         g_object_set_data( holder, "spray_scale", eact );
4625     }
4632 //########################
4633 //##     Calligraphy    ##
4634 //########################
4635 static void update_presets_list (GObject *tbl)
4637     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4638     if (g_object_get_data(tbl, "presets_blocked"))
4639         return;
4641     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4642     if (!sel) {
4643         // WTF!? This will cause a segfault if ever reached
4644         //ege_select_one_action_set_active(sel, 0);
4645         return;
4646     }
4648     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4650     int ege_index = 1;
4651     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++ege_index) {
4652         bool match = true;
4654         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(*i);
4655         for (std::vector<Inkscape::Preferences::Entry>::iterator j = preset.begin(); j != preset.end(); ++j) {
4656             Glib::ustring entry_name = j->getEntryName();
4657             if (entry_name == "id" || entry_name == "name") continue;
4659             void *widget = g_object_get_data(tbl, entry_name.data());
4660             if (widget) {
4661                 if (GTK_IS_ADJUSTMENT(widget)) {
4662                     double v = j->getDouble();
4663                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4664                     //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
4665                     if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
4666                         match = false;
4667                         break;
4668                     }
4669                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4670                     bool v = j->getBool();
4671                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4672                     //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
4673                     if ( static_cast<bool>(gtk_toggle_action_get_active(toggle)) != v ) {
4674                         match = false;
4675                         break;
4676                     }
4677                 }
4678             }
4679         }
4681         if (match) {
4682             // newly added item is at the same index as the
4683             // save command, so we need to change twice for it to take effect
4684             ege_select_one_action_set_active(sel, 0);
4685             ege_select_one_action_set_active(sel, ege_index); // one-based index
4686             return;
4687         }
4688     }
4690     // no match found
4691     ege_select_one_action_set_active(sel, 0);
4694 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
4696     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4697     prefs->setDouble( "/tools/calligraphic/mass", adj->value );
4698     update_presets_list(tbl);
4701 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
4703     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4704     prefs->setDouble( "/tools/calligraphic/wiggle", adj->value );
4705     update_presets_list(tbl);
4708 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
4710     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4711     prefs->setDouble( "/tools/calligraphic/angle", adj->value );
4712     update_presets_list(tbl);
4715 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
4717     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4718     prefs->setDouble( "/tools/calligraphic/width", adj->value );
4719     update_presets_list(tbl);
4722 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
4724     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4725     prefs->setDouble("/tools/calligraphic/thinning", adj->value );
4726     update_presets_list(tbl);
4729 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
4731     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4732     prefs->setDouble( "/tools/calligraphic/flatness", adj->value );
4733     update_presets_list(tbl);
4736 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
4738     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4739     prefs->setDouble( "/tools/calligraphic/tremor", adj->value );
4740     update_presets_list(tbl);
4743 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
4745     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4746     prefs->setDouble( "/tools/calligraphic/cap_rounding", adj->value );
4747     update_presets_list(tbl);
4750 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
4752     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4753     prefs->setBool("/tools/calligraphic/usepressure", gtk_toggle_action_get_active( act ));
4754     update_presets_list(tbl);
4757 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
4759     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4760     prefs->setBool("/tools/calligraphic/tracebackground", gtk_toggle_action_get_active( act ));
4761     update_presets_list(tbl);
4764 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
4766     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4767     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
4768     prefs->setBool("/tools/calligraphic/usetilt", gtk_toggle_action_get_active( act ));
4769     update_presets_list(tbl);
4770     if (calligraphy_angle )
4771         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
4775 static gchar const *const widget_names[] = {
4776     "width",
4777     "mass",
4778     "wiggle",
4779     "angle",
4780     "thinning",
4781     "tremor",
4782     "flatness",
4783     "cap_rounding",
4784     "usepressure",
4785     "tracebackground",
4786     "usetilt"
4787 };
4790 static void sp_dcc_build_presets_list(GObject *tbl)
4792     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4794     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4795     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4796     gtk_list_store_clear (model);
4798     {
4799         GtkTreeIter iter;
4800         gtk_list_store_append( model, &iter );
4801         gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4802     }
4804     // iterate over all presets to populate the list
4805     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4806     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4807     int ii=1;
4809     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i) {
4810         GtkTreeIter iter;
4811         Glib::ustring preset_name = prefs->getString(*i + "/name");
4812         gtk_list_store_append( model, &iter );
4813         gtk_list_store_set( model, &iter, 0, _(preset_name.data()), 1, ii++, -1 );
4814     }
4816     {
4817         GtkTreeIter iter;
4818         gtk_list_store_append( model, &iter );
4819         gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4820         g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4821     }
4823     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4825     update_presets_list (tbl);
4828 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4830     using Inkscape::UI::Dialog::CalligraphicProfileRename;
4831     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4832     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4833     if (! desktop) return;
4835     if (g_object_get_data(tbl, "presets_blocked"))
4836         return;
4838     CalligraphicProfileRename::show(desktop);
4839     if ( !CalligraphicProfileRename::applied()) {
4840         // dialog cancelled
4841         update_presets_list (tbl);
4842         return;
4843     }
4844     Glib::ustring profile_name = CalligraphicProfileRename::getProfileName();
4846     if (profile_name.empty()) {
4847         // empty name entered
4848         update_presets_list (tbl);
4849         return;
4850     }
4852     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4854     // If there's a preset with the given name, find it and set save_path appropriately
4855     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4856     int total_presets = presets.size();
4857     int new_index = -1;
4858     Glib::ustring save_path; // profile pref path without a trailing slash
4860     int temp_index = 0;
4861     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++temp_index) {
4862         Glib::ustring name = prefs->getString(*i + "/name");
4863         if (!name.empty() && profile_name == name) {
4864             new_index = temp_index;
4865             save_path = *i;
4866             break;
4867         }
4868     }
4870     if (new_index == -1) {
4871         // no preset with this name, create
4872         new_index = total_presets + 1;
4873         gchar *profile_id = g_strdup_printf("/dcc%d", new_index);
4874         save_path = Glib::ustring("/tools/calligraphic/preset") + profile_id;
4875         g_free(profile_id);
4876     }
4878     for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4879         gchar const *const widget_name = widget_names[i];
4880         void *widget = g_object_get_data(tbl, widget_name);
4881         if (widget) {
4882             if (GTK_IS_ADJUSTMENT(widget)) {
4883                 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4884                 prefs->setDouble(save_path + "/" + widget_name, gtk_adjustment_get_value(adj));
4885                 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4886             } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4887                 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4888                 prefs->setBool(save_path + "/" + widget_name, gtk_toggle_action_get_active(toggle));
4889                 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4890             } else {
4891                 g_warning("Unknown widget type for preset: %s\n", widget_name);
4892             }
4893         } else {
4894             g_warning("Bad key when writing preset: %s\n", widget_name);
4895         }
4896     }
4897     prefs->setString(save_path + "/name", profile_name);
4899     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4900     sp_dcc_build_presets_list (tbl);
4904 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4906     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4908     gint preset_index = ege_select_one_action_get_active( act );
4909     // This is necessary because EgeSelectOneAction spams us with GObject "changed" signal calls
4910     // even when the preset is not changed. It would be good to replace it with something more
4911     // modern. Index 0 means "No preset", so we don't do anything.
4912     if (preset_index == 0) return;
4914     gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4916     if (preset_index == save_presets_index) {
4917         // this is the Save command
4918         sp_dcc_save_profile(NULL, tbl);
4919         return;
4920     }
4922     if (g_object_get_data(tbl, "presets_blocked"))
4923         return;
4925     // preset_index is one-based so we subtract 1
4926     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4927     Glib::ustring preset_path = presets.at(preset_index - 1);
4929     if (!preset_path.empty()) {
4930         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
4932         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(preset_path);
4934         // Shouldn't this be std::map?
4935         for (std::vector<Inkscape::Preferences::Entry>::iterator i = preset.begin(); i != preset.end(); ++i) {
4936             Glib::ustring entry_name = i->getEntryName();
4937             if (entry_name == "id" || entry_name == "name") continue;
4938             void *widget = g_object_get_data(tbl, entry_name.data());
4939             if (widget) {
4940                 if (GTK_IS_ADJUSTMENT(widget)) {
4941                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4942                     gtk_adjustment_set_value(adj, i->getDouble());
4943                     //std::cout << "set adj " << attr_name << " to " << v << "\n";
4944                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4945                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4946                     gtk_toggle_action_set_active(toggle, i->getBool());
4947                     //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4948                 } else {
4949                     g_warning("Unknown widget type for preset: %s\n", entry_name.data());
4950                 }
4951             } else {
4952                 g_warning("Bad key found in a preset record: %s\n", entry_name.data());
4953             }
4954         }
4955         g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4956     }
4960 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4962     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4963     {
4964         g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4966         EgeAdjustmentAction* calligraphy_angle = 0;
4968         {
4969         /* Width */
4970         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4971         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4972         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4973                                                               _("Pen Width"), _("Width:"),
4974                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
4975                                                               "/tools/calligraphic/width", 15,
4976                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4977                                                               1, 100, 1.0, 10.0,
4978                                                               labels, values, G_N_ELEMENTS(labels),
4979                                                               sp_ddc_width_value_changed,  1, 0 );
4980         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4981         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4982         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4983         }
4985         {
4986         /* Thinning */
4987             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4988             gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4989         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4990                                                               _("Stroke Thinning"), _("Thinning:"),
4991                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4992                                                               "/tools/calligraphic/thinning", 10,
4993                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4994                                                               -100, 100, 1, 10.0,
4995                                                               labels, values, G_N_ELEMENTS(labels),
4996                                                               sp_ddc_velthin_value_changed, 1, 0);
4997         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4998         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4999         }
5001         {
5002         /* Angle */
5003         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
5004         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
5005         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
5006                                                               _("Pen Angle"), _("Angle:"),
5007                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
5008                                                               "/tools/calligraphic/angle", 30,
5009                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
5010                                                               -90.0, 90.0, 1.0, 10.0,
5011                                                               labels, values, G_N_ELEMENTS(labels),
5012                                                               sp_ddc_angle_value_changed, 1, 0 );
5013         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5014         g_object_set_data( holder, "angle_action", eact );
5015         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5016         calligraphy_angle = eact;
5017         }
5019         {
5020         /* Fixation */
5021             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
5022         gdouble values[] = {0, 20, 40, 60, 90, 100};
5023         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
5024                                                               _("Fixation"), _("Fixation:"),
5025                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"),
5026                                                               "/tools/calligraphic/flatness", 90,
5027                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5028                                                               0.0, 100, 1.0, 10.0,
5029                                                               labels, values, G_N_ELEMENTS(labels),
5030                                                               sp_ddc_flatness_value_changed, 1, 0);
5031         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5032         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5033         }
5035         {
5036         /* Cap Rounding */
5037             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
5038         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
5039         // TRANSLATORS: "cap" means "end" (both start and finish) here
5040         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
5041                                                               _("Cap rounding"), _("Caps:"),
5042                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
5043                                                               "/tools/calligraphic/cap_rounding", 0.0,
5044                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5045                                                               0.0, 5.0, 0.01, 0.1,
5046                                                               labels, values, G_N_ELEMENTS(labels),
5047                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
5048         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5049         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5050         }
5052         {
5053         /* Tremor */
5054             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
5055         gdouble values[] = {0, 10, 20, 40, 60, 100};
5056         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
5057                                                               _("Stroke Tremor"), _("Tremor:"),
5058                                                               _("Increase to make strokes rugged and trembling"),
5059                                                               "/tools/calligraphic/tremor", 0.0,
5060                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5061                                                               0.0, 100, 1, 10.0,
5062                                                               labels, values, G_N_ELEMENTS(labels),
5063                                                               sp_ddc_tremor_value_changed, 1, 0);
5065         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5066         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5067         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5068         }
5070         {
5071         /* Wiggle */
5072         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
5073         gdouble values[] = {0, 20, 40, 60, 100};
5074         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
5075                                                               _("Pen Wiggle"), _("Wiggle:"),
5076                                                               _("Increase to make the pen waver and wiggle"),
5077                                                               "/tools/calligraphic/wiggle", 0.0,
5078                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5079                                                               0.0, 100, 1, 10.0,
5080                                                               labels, values, G_N_ELEMENTS(labels),
5081                                                               sp_ddc_wiggle_value_changed, 1, 0);
5082         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5083         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5084         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5085         }
5087         {
5088         /* Mass */
5089             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
5090         gdouble values[] = {0.0, 2, 10, 20, 50, 100};
5091         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
5092                                                               _("Pen Mass"), _("Mass:"),
5093                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
5094                                                               "/tools/calligraphic/mass", 2.0,
5095                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5096                                                               0.0, 100, 1, 10.0,
5097                                                               labels, values, G_N_ELEMENTS(labels),
5098                                                               sp_ddc_mass_value_changed, 1, 0);
5099         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5100         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5101         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5102         }
5105         /* Trace Background button */
5106         {
5107             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
5108                                                           _("Trace Background"),
5109                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
5110                                                           INKSCAPE_ICON_DRAW_TRACE_BACKGROUND,
5111                                                           Inkscape::ICON_SIZE_DECORATION );
5112             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5113             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
5114             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/tracebackground", false) );
5115             g_object_set_data( holder, "tracebackground", act );
5116         }
5118         /* Use Pressure button */
5119         {
5120             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
5121                                                           _("Pressure"),
5122                                                           _("Use the pressure of the input device to alter the width of the pen"),
5123                                                           INKSCAPE_ICON_DRAW_USE_PRESSURE,
5124                                                           Inkscape::ICON_SIZE_DECORATION );
5125             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5126             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
5127             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usepressure", true) );
5128             g_object_set_data( holder, "usepressure", act );
5129         }
5131         /* Use Tilt button */
5132         {
5133             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
5134                                                           _("Tilt"),
5135                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
5136                                                           INKSCAPE_ICON_DRAW_USE_TILT,
5137                                                           Inkscape::ICON_SIZE_DECORATION );
5138             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5139             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
5140             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs->getBool("/tools/calligraphic/usetilt", true) );
5141             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usetilt", true) );
5142             g_object_set_data( holder, "usetilt", act );
5143         }
5145         /*calligraphic profile */
5146         {
5147             GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5148             EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
5149             ege_select_one_action_set_appearance (act1, "compact");
5150             g_object_set_data (holder, "profile_selector", act1 );
5152             g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
5154             sp_dcc_build_presets_list (holder);
5156             g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
5157             gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
5158         }
5159     }
5163 //########################
5164 //##    Circle / Arc    ##
5165 //########################
5167 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
5169     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
5170     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
5172     if (v1 == 0 && v2 == 0) {
5173         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
5174             gtk_action_set_sensitive( ocb, FALSE );
5175             gtk_action_set_sensitive( make_whole, FALSE );
5176         }
5177     } else {
5178         gtk_action_set_sensitive( ocb, TRUE );
5179         gtk_action_set_sensitive( make_whole, TRUE );
5180     }
5183 static void
5184 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
5186     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5188     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5189         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5190         prefs->setDouble(Glib::ustring("/tools/shapes/arc/") + value_name, adj->value);
5191     }
5193     // quit if run by the attr_changed listener
5194     if (g_object_get_data( tbl, "freeze" )) {
5195         return;
5196     }
5198     // in turn, prevent listener from responding
5199     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5201     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
5203     bool modmade = false;
5204     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5205          items != NULL;
5206          items = items->next)
5207     {
5208         SPItem *item = SP_ITEM(items->data);
5210         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
5212             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
5213             SPArc *arc = SP_ARC(item);
5215             if (!strcmp(value_name, "start"))
5216                 ge->start = (adj->value * M_PI)/ 180;
5217             else
5218                 ge->end = (adj->value * M_PI)/ 180;
5220             sp_genericellipse_normalize(ge);
5221             ((SPObject *)arc)->updateRepr();
5222             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
5224             modmade = true;
5225         }
5226     }
5228     g_free(namespaced_name);
5230     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
5232     sp_arctb_sensitivize( tbl, adj->value, other->value );
5234     if (modmade) {
5235         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
5236                                    _("Arc: Change start/end"));
5237     }
5239     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5243 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
5245     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
5248 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
5250     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
5254 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
5256     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5257     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5258         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5259         prefs->setBool("/tools/shapes/arc/open", ege_select_one_action_get_active(act) != 0);
5260     }
5262     // quit if run by the attr_changed listener
5263     if (g_object_get_data( tbl, "freeze" )) {
5264         return;
5265     }
5267     // in turn, prevent listener from responding
5268     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5270     bool modmade = false;
5272     if ( ege_select_one_action_get_active(act) != 0 ) {
5273         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5274              items != NULL;
5275              items = items->next)
5276         {
5277             if (SP_IS_ARC((SPItem *) items->data)) {
5278                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5279                 repr->setAttribute("sodipodi:open", "true");
5280                 SP_OBJECT((SPItem *) items->data)->updateRepr();
5281                 modmade = true;
5282             }
5283         }
5284     } else {
5285         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5286              items != NULL;
5287              items = items->next)
5288         {
5289             if (SP_IS_ARC((SPItem *) items->data))    {
5290                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5291                 repr->setAttribute("sodipodi:open", NULL);
5292                 SP_OBJECT((SPItem *) items->data)->updateRepr();
5293                 modmade = true;
5294             }
5295         }
5296     }
5298     if (modmade) {
5299         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
5300                                    _("Arc: Change open/closed"));
5301     }
5303     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5306 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
5308     GtkAdjustment *adj;
5309     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
5310     gtk_adjustment_set_value(adj, 0.0);
5311     gtk_adjustment_value_changed(adj);
5313     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
5314     gtk_adjustment_set_value(adj, 0.0);
5315     gtk_adjustment_value_changed(adj);
5317     spinbutton_defocus( GTK_OBJECT(obj) );
5320 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
5321                                       gchar const */*old_value*/, gchar const */*new_value*/,
5322                                       bool /*is_interactive*/, gpointer data)
5324     GObject *tbl = G_OBJECT(data);
5326     // quit if run by the _changed callbacks
5327     if (g_object_get_data( tbl, "freeze" )) {
5328         return;
5329     }
5331     // in turn, prevent callbacks from responding
5332     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5334     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
5335     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
5337     GtkAdjustment *adj1,*adj2;
5338     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
5339     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
5340     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
5341     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
5343     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
5345     char const *openstr = NULL;
5346     openstr = repr->attribute("sodipodi:open");
5347     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
5349     if (openstr) {
5350         ege_select_one_action_set_active( ocb, 1 );
5351     } else {
5352         ege_select_one_action_set_active( ocb, 0 );
5353     }
5355     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5358 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
5359     NULL, /* child_added */
5360     NULL, /* child_removed */
5361     arc_tb_event_attr_changed,
5362     NULL, /* content_changed */
5363     NULL  /* order_changed */
5364 };
5367 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
5369     int n_selected = 0;
5370     Inkscape::XML::Node *repr = NULL;
5372     purge_repr_listener( tbl, tbl );
5374     for (GSList const *items = selection->itemList();
5375          items != NULL;
5376          items = items->next)
5377     {
5378         if (SP_IS_ARC((SPItem *) items->data)) {
5379             n_selected++;
5380             repr = SP_OBJECT_REPR((SPItem *) items->data);
5381         }
5382     }
5384     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
5386     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
5387     if (n_selected == 0) {
5388         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
5389     } else if (n_selected == 1) {
5390         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
5391         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5393         if (repr) {
5394             g_object_set_data( tbl, "repr", repr );
5395             Inkscape::GC::anchor(repr);
5396             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
5397             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
5398         }
5399     } else {
5400         // FIXME: implement averaging of all parameters for multiple selected
5401         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
5402         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5403         sp_arctb_sensitivize( tbl, 1, 0 );
5404     }
5408 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5410     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5412     EgeAdjustmentAction* eact = 0;
5413     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
5416     {
5417         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
5418         ege_output_action_set_use_markup( act, TRUE );
5419         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5420         g_object_set_data( holder, "mode_action", act );
5421     }
5423     /* Start */
5424     {
5425         eact = create_adjustment_action( "ArcStartAction",
5426                                          _("Start"), _("Start:"),
5427                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
5428                                          "/tools/shapes/arc/start", 0.0,
5429                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
5430                                          -360.0, 360.0, 1.0, 10.0,
5431                                          0, 0, 0,
5432                                          sp_arctb_start_value_changed);
5433         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5434     }
5436     /* End */
5437     {
5438         eact = create_adjustment_action( "ArcEndAction",
5439                                          _("End"), _("End:"),
5440                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
5441                                          "/tools/shapes/arc/end", 0.0,
5442                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
5443                                          -360.0, 360.0, 1.0, 10.0,
5444                                          0, 0, 0,
5445                                          sp_arctb_end_value_changed);
5446         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5447     }
5449     /* Segments / Pie checkbox */
5450     {
5451         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5453         GtkTreeIter iter;
5454         gtk_list_store_append( model, &iter );
5455         gtk_list_store_set( model, &iter,
5456                             0, _("Closed arc"),
5457                             1, _("Switch to segment (closed shape with two radii)"),
5458                             2, INKSCAPE_ICON_DRAW_ELLIPSE_SEGMENT,
5459                             -1 );
5461         gtk_list_store_append( model, &iter );
5462         gtk_list_store_set( model, &iter,
5463                             0, _("Open Arc"),
5464                             1, _("Switch to arc (unclosed shape)"),
5465                             2, INKSCAPE_ICON_DRAW_ELLIPSE_ARC,
5466                             -1 );
5468         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5469         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5470         g_object_set_data( holder, "open_action", act );
5472         ege_select_one_action_set_appearance( act, "full" );
5473         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5474         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5475         ege_select_one_action_set_icon_column( act, 2 );
5476         ege_select_one_action_set_icon_size( act, secondarySize );
5477         ege_select_one_action_set_tooltip_column( act, 1  );
5479         bool isClosed = !prefs->getBool("/tools/shapes/arc/open", false);
5480         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
5481         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
5482     }
5484     /* Make Whole */
5485     {
5486         InkAction* inky = ink_action_new( "ArcResetAction",
5487                                           _("Make whole"),
5488                                           _("Make the shape a whole ellipse, not arc or segment"),
5489                                           INKSCAPE_ICON_DRAW_ELLIPSE_WHOLE,
5490                                           secondarySize );
5491         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
5492         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5493         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
5494         g_object_set_data( holder, "make_whole", inky );
5495     }
5497     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
5498     // sensitivize make whole and open checkbox
5499     {
5500         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
5501         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
5502         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
5503     }
5506     sigc::connection *connection = new sigc::connection(
5507         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
5508         );
5509     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
5510     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
5516 // toggle button callbacks and updaters
5518 //########################
5519 //##      Dropper       ##
5520 //########################
5522 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
5523     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5524     prefs->setInt( "/tools/dropper/pick", gtk_toggle_action_get_active( act ) );
5525     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
5526     if ( set_action ) {
5527         if ( gtk_toggle_action_get_active( act ) ) {
5528             gtk_action_set_sensitive( set_action, TRUE );
5529         } else {
5530             gtk_action_set_sensitive( set_action, FALSE );
5531         }
5532     }
5534     spinbutton_defocus(GTK_OBJECT(tbl));
5537 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
5538     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5539     prefs->setBool( "/tools/dropper/setalpha", gtk_toggle_action_get_active( act ) );
5540     spinbutton_defocus(GTK_OBJECT(tbl));
5544 /**
5545  * Dropper auxiliary toolbar construction and setup.
5546  *
5547  * TODO: Would like to add swatch of current color.
5548  * TODO: Add queue of last 5 or so colors selected with new swatches so that
5549  *       can drag and drop places. Will provide a nice mixing palette.
5550  */
5551 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
5553     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5554     gint pickAlpha = prefs->getInt( "/tools/dropper/pick", 1 );
5556     {
5557         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
5558         ege_output_action_set_use_markup( act, TRUE );
5559         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5560     }
5562     {
5563         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
5564                                                       _("Pick opacity"),
5565                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
5566                                                       NULL,
5567                                                       Inkscape::ICON_SIZE_DECORATION );
5568         g_object_set( act, "short_label", _("Pick"), NULL );
5569         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5570         g_object_set_data( holder, "pick_action", act );
5571         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
5572         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
5573     }
5575     {
5576         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
5577                                                       _("Assign opacity"),
5578                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
5579                                                       NULL,
5580                                                       Inkscape::ICON_SIZE_DECORATION );
5581         g_object_set( act, "short_label", _("Assign"), NULL );
5582         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5583         g_object_set_data( holder, "set_action", act );
5584         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/dropper/setalpha", true) );
5585         // make sure it's disabled if we're not picking alpha
5586         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
5587         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
5588     }
5592 //########################
5593 //##      LPETool       ##
5594 //########################
5596 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
5598 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
5599 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
5601     using namespace Inkscape::LivePathEffect;
5603     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
5604     SPEventContext *ec = desktop->event_context;
5605     if (!SP_IS_LPETOOL_CONTEXT(ec)) {
5606         return;
5607     }
5609     // only take action if run by the attr_changed listener
5610     if (!g_object_get_data(tbl, "freeze")) {
5611         // in turn, prevent listener from responding
5612         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5614         gint mode = ege_select_one_action_get_active(act);
5615         EffectType type = lpesubtools[mode].type;
5617         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5618         bool success = lpetool_try_construction(lc, type);
5619         if (success) {
5620             // since the construction was already performed, we set the state back to inactive
5621             ege_select_one_action_set_active(act, 0);
5622             mode = 0;
5623         } else {
5624             // switch to the chosen subtool
5625             SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
5626         }
5628         if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5629             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5630             prefs->setInt( "/tools/lpetool/mode", mode );
5631         }
5633         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
5634     }
5637 void sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject */*tbl*/)
5639     SPEventContext *ec = selection->desktop()->event_context;
5640     if (!SP_IS_LPETOOL_CONTEXT(ec))
5641         return;
5643     lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
5646 void
5647 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
5649     using namespace Inkscape::LivePathEffect;
5650     SPEventContext *ec = selection->desktop()->event_context;
5651     if (!SP_IS_LPETOOL_CONTEXT(ec))
5652         return;
5653     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
5655     lpetool_delete_measuring_items(lc);
5656     lpetool_create_measuring_items(lc, selection);
5658     // activate line segment combo box if a single item with LPELineSegment is selected
5659     GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
5660     SPItem *item = selection->singleItem();
5661     if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
5662         SPLPEItem *lpeitem = SP_LPE_ITEM(item);
5663         Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
5664         if (lpe && lpe->effectType() == LINE_SEGMENT) {
5665             LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
5666             g_object_set_data(tbl, "currentlpe", lpe);
5667             g_object_set_data(tbl, "currentlpeitem", lpeitem);
5668             gtk_action_set_sensitive(w, TRUE);
5669             ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
5670         } else {
5671             g_object_set_data(tbl, "currentlpe", NULL);
5672             g_object_set_data(tbl, "currentlpeitem", NULL);
5673             gtk_action_set_sensitive(w, FALSE);
5674         }
5675     } else {
5676         g_object_set_data(tbl, "currentlpe", NULL);
5677         g_object_set_data(tbl, "currentlpeitem", NULL);
5678         gtk_action_set_sensitive(w, FALSE);
5679     }
5682 static void
5683 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
5684     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5685     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5687     bool show = gtk_toggle_action_get_active( act );
5688     prefs->setBool("/tools/lpetool/show_bbox",  show);
5690     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5691         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5692         lpetool_context_reset_limiting_bbox(lc);
5693     }
5696 static void
5697 lpetool_toggle_show_measuring_info (GtkToggleAction *act, GObject *tbl) {
5698     SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
5699     if (!tools_isactive(desktop, TOOLS_LPETOOL))
5700         return;
5702     GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
5703     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5704     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5705         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5706         bool show = gtk_toggle_action_get_active( act );
5707         prefs->setBool("/tools/lpetool/show_measuring_info",  show);
5708         lpetool_show_measuring_info(lc, show);
5709         gtk_action_set_sensitive(GTK_ACTION(unitact), show);
5710     }
5713 static void lpetool_unit_changed(GtkAction* /*act*/, GObject* tbl) {
5714     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
5715     SPUnit const *unit = tracker->getActiveUnit();
5716     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5717     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5719     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5720     if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
5721         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5722         lpetool_delete_measuring_items(lc);
5723         lpetool_create_measuring_items(lc);
5724     }
5727 static void
5728 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
5729     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5730     Inkscape::Selection *selection = desktop->selection;
5732     Geom::OptRect bbox = selection->bounds();
5734     if (bbox) {
5735         Geom::Point A(bbox->min());
5736         Geom::Point B(bbox->max());
5738         A *= desktop->doc2dt();
5739         B *= desktop->doc2dt();
5741         // TODO: should we provide a way to store points in prefs?
5742         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5743         prefs->setDouble("/tools/lpetool/bbox_upperleftx", A[Geom::X]);
5744         prefs->setDouble("/tools/lpetool/bbox_upperlefty", A[Geom::Y]);
5745         prefs->setDouble("/tools/lpetool/bbox_lowerrightx", B[Geom::X]);
5746         prefs->setDouble("/tools/lpetool/bbox_lowerrighty", B[Geom::Y]);
5748         lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
5749     }
5751     gtk_toggle_action_set_active(act, false);
5754 static void
5755 sp_line_segment_build_list(GObject *tbl)
5757     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
5759     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
5760     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
5761     gtk_list_store_clear (model);
5763     // TODO: we add the entries of rht combo box manually; later this should be done automatically
5764     {
5765         GtkTreeIter iter;
5766         gtk_list_store_append( model, &iter );
5767         gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
5768         gtk_list_store_append( model, &iter );
5769         gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
5770         gtk_list_store_append( model, &iter );
5771         gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
5772         gtk_list_store_append( model, &iter );
5773         gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
5774     }
5776     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5779 static void
5780 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
5781     using namespace Inkscape::LivePathEffect;
5783     // quit if run by the attr_changed listener
5784     if (g_object_get_data(tbl, "freeze")) {
5785         return;
5786     }
5788     // in turn, prevent listener from responding
5789     g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5791     LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
5792     SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5793     if (lpeitem) {
5794         SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5795         lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
5796         sp_lpe_item_update_patheffect(lpeitem, true, true);
5797     }
5799     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5802 static void
5803 lpetool_open_lpe_dialog (GtkToggleAction *act, gpointer data) {
5804     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5806     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5807         sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5808     }
5809     gtk_toggle_action_set_active(act, false);
5812 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5814     UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5815     tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5816     g_object_set_data(holder, "tracker", tracker);
5817     SPUnit const *unit = tracker->getActiveUnit();
5819     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5820     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5822     /** Automatically create a list of LPEs that get added to the toolbar **/
5823     {
5824         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5826         GtkTreeIter iter;
5828         // the first toggle button represents the state that no subtool is active (remove this when
5829         // this can be modeled by EgeSelectOneAction or some other action)
5830         gtk_list_store_append( model, &iter );
5831         gtk_list_store_set( model, &iter,
5832                             0, _("All inactive"),
5833                             1, _("No geometric tool is active"),
5834                             2, "draw-geometry-inactive",
5835                             -1 );
5837         Inkscape::LivePathEffect::EffectType type;
5838         for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5839             type =  lpesubtools[i].type;
5840             gtk_list_store_append( model, &iter );
5841             gtk_list_store_set( model, &iter,
5842                                 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5843                                 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5844                                 2, lpesubtools[i].icon_name,
5845                                 -1 );
5846         }
5848         EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5849         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5850         g_object_set_data( holder, "lpetool_mode_action", act );
5852         ege_select_one_action_set_appearance( act, "full" );
5853         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5854         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5855         ege_select_one_action_set_icon_column( act, 2 );
5856         ege_select_one_action_set_tooltip_column( act, 1  );
5858         gint lpeToolMode = prefs->getInt("/tools/lpetool/mode", 0);
5859         ege_select_one_action_set_active( act, lpeToolMode );
5860         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5861     }
5863     /* Show limiting bounding box */
5864     {
5865         InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5866                                                       _("Show limiting bounding box"),
5867                                                       _("Show bounding box (used to cut infinite lines)"),
5868                                                       "show-bounding-box",
5869                                                       Inkscape::ICON_SIZE_DECORATION );
5870         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5871         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5872         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_bbox", true ) );
5873     }
5875     /* Set limiting bounding box to bbox of current selection */
5876     {
5877         InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5878                                                       _("Get limiting bounding box from selection"),
5879                                                       _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5880                                                       "draw-geometry-set-bounding-box",
5881                                                       Inkscape::ICON_SIZE_DECORATION );
5882         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5883         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5884         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5885     }
5888     /* Combo box to choose line segment type */
5889     {
5890         GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5891         EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5892         ege_select_one_action_set_appearance (act, "compact");
5893         g_object_set_data (holder, "lpetool_line_segment_action", act );
5895         g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5897         sp_line_segment_build_list (holder);
5899         g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5900         gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5901         gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5902     }
5904     /* Display measuring info for selected items */
5905     {
5906         InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5907                                                       _("Display measuring info"),
5908                                                       _("Display measuring info for selected items"),
5909                                                       "draw-geometry-show-measuring-info",
5910                                                       Inkscape::ICON_SIZE_DECORATION );
5911         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5912         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
5913         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_measuring_info", true ) );
5914     }
5916     // add the units menu
5917     {
5918         GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5919         gtk_action_group_add_action( mainActions, act );
5920         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5921         g_object_set_data(holder, "lpetool_units_action", act);
5922         gtk_action_set_sensitive(act, prefs->getBool("/tools/lpetool/show_measuring_info", true));
5923     }
5925     /* Open LPE dialog (to adapt parameters numerically) */
5926     {
5927         InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
5928                                                       _("Open LPE dialog"),
5929                                                       _("Open LPE dialog (to adapt parameters numerically)"),
5930                                                       "dialog-geometry",
5931                                                       Inkscape::ICON_SIZE_DECORATION );
5932         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5933         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
5934         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5935     }
5937     //watch selection
5938     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5940     sigc::connection *c_selection_modified =
5941         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5942                               (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
5943     pool->add_connection ("selection-modified", c_selection_modified);
5945     sigc::connection *c_selection_changed =
5946         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5947                               (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5948     pool->add_connection ("selection-changed", c_selection_changed);
5951 //########################
5952 //##       Eraser       ##
5953 //########################
5955 static void sp_erc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
5957     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5958     prefs->setDouble( "/tools/eraser/width", adj->value );
5959     update_presets_list(tbl);
5962 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5964     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5965     bool eraserMode = ege_select_one_action_get_active( act ) != 0;
5966     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5967         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5968         prefs->setBool( "/tools/eraser/mode", eraserMode );
5969     }
5971     // only take action if run by the attr_changed listener
5972     if (!g_object_get_data( tbl, "freeze" )) {
5973         // in turn, prevent listener from responding
5974         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5976         if ( eraserMode != 0 ) {
5977         } else {
5978         }
5979         // TODO finish implementation
5981         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5982     }
5985 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5987     {
5988         /* Width */
5989         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5990         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5991         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5992                                                               _("Pen Width"), _("Width:"),
5993                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
5994                                                               "/tools/eraser/width", 15,
5995                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5996                                                               1, 100, 1.0, 10.0,
5997                                                               labels, values, G_N_ELEMENTS(labels),
5998                                                               sp_erc_width_value_changed, 1, 0);
5999         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6000         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6001         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
6002     }
6004     {
6005         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
6007         GtkTreeIter iter;
6008         gtk_list_store_append( model, &iter );
6009         gtk_list_store_set( model, &iter,
6010                             0, _("Delete"),
6011                             1, _("Delete objects touched by the eraser"),
6012                             2, INKSCAPE_ICON_DRAW_ERASER_DELETE_OBJECTS,
6013                             -1 );
6015         gtk_list_store_append( model, &iter );
6016         gtk_list_store_set( model, &iter,
6017                             0, _("Cut"),
6018                             1, _("Cut out from objects"),
6019                             2, INKSCAPE_ICON_PATH_DIFFERENCE,
6020                             -1 );
6022         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
6023         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
6024         g_object_set_data( holder, "eraser_mode_action", act );
6026         ege_select_one_action_set_appearance( act, "full" );
6027         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
6028         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
6029         ege_select_one_action_set_icon_column( act, 2 );
6030         ege_select_one_action_set_tooltip_column( act, 1  );
6032         /// @todo Convert to boolean?
6033         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6034         gint eraserMode = prefs->getBool("/tools/eraser/mode") ? 1 : 0;
6035         ege_select_one_action_set_active( act, eraserMode );
6036         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
6037     }
6041 //########################
6042 //##    Text Toolbox    ##
6043 //########################
6044 /*
6045 static void
6046 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
6048     //Call back for letter sizing spinbutton
6051 static void
6052 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
6054     //Call back for line height spinbutton
6057 static void
6058 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
6060     //Call back for horizontal kerning spinbutton
6063 static void
6064 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
6066     //Call back for vertical kerning spinbutton
6069 static void
6070 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
6072     //Call back for letter rotation spinbutton
6073 }*/
6075 namespace {
6077 void
6078 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
6080     // quit if run by the _changed callbacks
6081     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6082         return;
6083     }
6085     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6087     SPStyle *query =
6088         sp_style_new (SP_ACTIVE_DOCUMENT);
6090     int result_family =
6091         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6093     int result_style =
6094         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6096     int result_numbers =
6097         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6099     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6101     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6102     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
6103         // there are no texts in selection, read from prefs
6105         sp_style_read_from_prefs(query, "/tools/text");
6107         if (g_object_get_data(tbl, "text_style_from_prefs")) {
6108             // do not reset the toolbar style from prefs if we already did it last time
6109             sp_style_unref(query);
6110             g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6111             return;
6112         }
6113         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
6114     } else {
6115         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
6116     }
6118     if (query->text)
6119     {
6120         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
6121             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6122             gtk_entry_set_text (GTK_ENTRY (entry), "");
6124         } else if (query->text->font_specification.value || query->text->font_family.value) {
6126             Gtk::ComboBoxEntry *combo = (Gtk::ComboBoxEntry *) (g_object_get_data (G_OBJECT (tbl), "family-entry-combo"));
6127             GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6129             // Get the font that corresponds
6130             Glib::ustring familyName;
6132             font_instance * font = font_factory::Default()->FaceFromStyle(query);
6133             if (font) {
6134                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
6135                 font->Unref();
6136                 font = NULL;
6137             }
6139             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
6141             Gtk::TreeIter iter;
6142             try {
6143                 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
6144                 Glib::RefPtr<Gtk::TreeModel> model = combo->get_model();
6145                 iter = model->get_iter(path);
6146             } catch (...) {
6147                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
6148                 sp_style_unref(query);
6149                 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6150                 return;
6151             }
6153             combo->set_active (iter);
6154         }
6156         //Size
6157         {
6158             GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
6159             gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
6160             gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
6161             g_free(str);
6162         }
6164         //Anchor
6165         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
6166         {
6167             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
6168             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6169             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6170             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6171         }
6172         else
6173         {
6174             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
6175             {
6176                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
6177                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6178                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6179                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6180             }
6181             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
6182             {
6183                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
6184                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6185                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6186                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6187             }
6188             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
6189             {
6190                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
6191                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6192                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6193                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6194             }
6195         }
6197         //Style
6198         {
6199             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
6201             gboolean active = gtk_toggle_button_get_active (button);
6202             gboolean check  = ((query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700) && (query->font_weight.computed != SP_CSS_FONT_WEIGHT_NORMAL) && (query->font_weight.computed != SP_CSS_FONT_WEIGHT_LIGHTER));
6204             if (active != check)
6205             {
6206                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6207                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6208                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6209             }
6210         }
6212         {
6213             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
6215             gboolean active = gtk_toggle_button_get_active (button);
6216             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
6218             if (active != check)
6219             {
6220                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6221                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6222                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6223             }
6224         }
6226         //Orientation
6227         //locking both buttons, changing one affect all group (both)
6228         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
6229         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6231         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
6232         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
6234         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
6235         {
6236             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6237         }
6238         else
6239         {
6240             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
6241         }
6242         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6243         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
6244     }
6246     sp_style_unref(query);
6248     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6251 void
6252 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
6254     sp_text_toolbox_selection_changed (selection, tbl);
6257 void
6258 sp_text_toolbox_subselection_changed (gpointer /*tc*/, GObject *tbl)
6260     sp_text_toolbox_selection_changed (NULL, tbl);
6263 void
6264 sp_text_toolbox_family_changed (GtkComboBoxEntry    *,
6265                                 GObject             *tbl)
6267     // quit if run by the _changed callbacks
6268     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6269         return;
6270     }
6272     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6274     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
6275     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6276     const gchar* family = gtk_entry_get_text (GTK_ENTRY (entry));
6278     //g_print ("family changed to: %s\n", family);
6280     SPStyle *query =
6281         sp_style_new (SP_ACTIVE_DOCUMENT);
6283     int result_fontspec =
6284         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6286     SPCSSAttr *css = sp_repr_css_attr_new ();
6288     // First try to get the font spec from the stored value
6289     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
6291     if (fontSpec.empty()) {
6292         // Construct a new font specification if it does not yet exist
6293         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6294         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6295         fontFromStyle->Unref();
6296     }
6298     if (!fontSpec.empty()) {
6300         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
6302         if (!newFontSpec.empty()) {
6304             if (fontSpec != newFontSpec) {
6306                 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
6308                 if (font) {
6309                     sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6311                     // Set all the these just in case they were altered when finding the best
6312                     // match for the new family and old style...
6314                     gchar c[256];
6316                     font->Family(c, 256);
6318                     sp_repr_css_set_property (css, "font-family", c);
6320                     font->Attribute( "weight", c, 256);
6321                     sp_repr_css_set_property (css, "font-weight", c);
6323                     font->Attribute("style", c, 256);
6324                     sp_repr_css_set_property (css, "font-style", c);
6326                     font->Attribute("stretch", c, 256);
6327                     sp_repr_css_set_property (css, "font-stretch", c);
6329                     font->Attribute("variant", c, 256);
6330                     sp_repr_css_set_property (css, "font-variant", c);
6332                     font->Unref();
6333                 }
6334             }
6336         } else {
6337             // If the old font on selection (or default) was not existing on the system,
6338             // ReplaceFontSpecificationFamily does not work. In that case we fall back to blindly
6339             // setting the family reported by the family chooser.
6341             //g_print ("fallback setting family: %s\n", family);
6342             sp_repr_css_set_property (css, "-inkscape-font-specification", family);
6343             sp_repr_css_set_property (css, "font-family", family);
6344         }
6345     }
6347     // If querying returned nothing, set the default style of the tool (for new texts)
6348     if (result_fontspec == QUERY_STYLE_NOTHING)
6349     {
6350         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6351         prefs->mergeStyle("/tools/text/style", css);
6352         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
6353     }
6354     else
6355     {
6356         sp_desktop_set_style (desktop, css, true, true);
6357     }
6359     sp_style_unref(query);
6361     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6362                                    _("Text: Change font family"));
6363     sp_repr_css_attr_unref (css);
6365     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6367     // unfreeze
6368     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6370     // focus to canvas
6371     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6375 void
6376 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
6377                                    gpointer          data)
6379     if (g_object_get_data (G_OBJECT (button), "block")) return;
6380     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
6381     int prop = GPOINTER_TO_INT(data);
6383     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6385     // move the x of all texts to preserve the same bbox
6386     Inkscape::Selection *selection = sp_desktop_selection(desktop);
6387     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
6388         if (SP_IS_TEXT((SPItem *) items->data)) {
6389             SPItem *item = SP_ITEM(items->data);
6391             unsigned writing_mode = SP_OBJECT_STYLE(item)->writing_mode.value;
6392             // below, variable names suggest horizontal move, but we check the writing direction
6393             // and move in the corresponding axis
6394             int axis;
6395             if (writing_mode == SP_CSS_WRITING_MODE_LR_TB || writing_mode == SP_CSS_WRITING_MODE_RL_TB) {
6396                 axis = NR::X;
6397             } else {
6398                 axis = NR::Y;
6399             }
6401             Geom::OptRect bbox
6402                   = item->getBounds(Geom::identity(), SPItem::GEOMETRIC_BBOX);
6403             if (!bbox)
6404                 continue;
6405             double width = bbox->dimensions()[axis];
6406             // If you want to align within some frame, other than the text's own bbox, calculate
6407             // the left and right (or top and bottom for tb text) slacks of the text inside that
6408             // frame (currently unused)
6409             double left_slack = 0;
6410             double right_slack = 0;
6411             unsigned old_align = SP_OBJECT_STYLE(item)->text_align.value;
6412             double move = 0;
6413             if (old_align == SP_CSS_TEXT_ALIGN_START || old_align == SP_CSS_TEXT_ALIGN_LEFT) {
6414                 switch (prop) {
6415                     case 0:
6416                         move = -left_slack;
6417                         break;
6418                     case 1:
6419                         move = width/2 + (right_slack - left_slack)/2;
6420                         break;
6421                     case 2:
6422                         move = width + right_slack;
6423                         break;
6424                 }
6425             } else if (old_align == SP_CSS_TEXT_ALIGN_CENTER) {
6426                 switch (prop) {
6427                     case 0:
6428                         move = -width/2 - left_slack;
6429                         break;
6430                     case 1:
6431                         move = (right_slack - left_slack)/2;
6432                         break;
6433                     case 2:
6434                         move = width/2 + right_slack;
6435                         break;
6436                 }
6437             } else if (old_align == SP_CSS_TEXT_ALIGN_END || old_align == SP_CSS_TEXT_ALIGN_RIGHT) {
6438                 switch (prop) {
6439                     case 0:
6440                         move = -width - left_slack;
6441                         break;
6442                     case 1:
6443                         move = -width/2 + (right_slack - left_slack)/2;
6444                         break;
6445                     case 2:
6446                         move = right_slack;
6447                         break;
6448                 }
6449             }
6450             Geom::Point XY = SP_TEXT(item)->attributes.firstXY();
6451             if (axis == NR::X) {
6452                 XY = XY + Geom::Point (move, 0);
6453             } else {
6454                 XY = XY + Geom::Point (0, move);
6455             }
6456             SP_TEXT(item)->attributes.setFirstXY(XY);
6457             SP_OBJECT(item)->updateRepr();
6458             SP_OBJECT(item)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
6459         }
6460     }
6462     SPCSSAttr *css = sp_repr_css_attr_new ();
6463     switch (prop)
6464     {
6465         case 0:
6466         {
6467             sp_repr_css_set_property (css, "text-anchor", "start");
6468             sp_repr_css_set_property (css, "text-align", "start");
6469             break;
6470         }
6471         case 1:
6472         {
6473             sp_repr_css_set_property (css, "text-anchor", "middle");
6474             sp_repr_css_set_property (css, "text-align", "center");
6475             break;
6476         }
6478         case 2:
6479         {
6480             sp_repr_css_set_property (css, "text-anchor", "end");
6481             sp_repr_css_set_property (css, "text-align", "end");
6482             break;
6483         }
6485         case 3:
6486         {
6487             sp_repr_css_set_property (css, "text-anchor", "start");
6488             sp_repr_css_set_property (css, "text-align", "justify");
6489             break;
6490         }
6491     }
6493     SPStyle *query =
6494         sp_style_new (SP_ACTIVE_DOCUMENT);
6495     int result_numbers =
6496         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6498     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6499     if (result_numbers == QUERY_STYLE_NOTHING)
6500     {
6501         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6502         prefs->mergeStyle("/tools/text/style", css);
6503     }
6505     sp_style_unref(query);
6507     sp_desktop_set_style (desktop, css, true, true);
6508     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6509                                    _("Text: Change alignment"));
6510     sp_repr_css_attr_unref (css);
6512     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6515 void
6516 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
6517                                gpointer          data)
6519     if (g_object_get_data (G_OBJECT (button), "block")) return;
6521     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
6522     SPCSSAttr   *css        = sp_repr_css_attr_new ();
6523     int          prop       = GPOINTER_TO_INT(data);
6524     bool         active     = gtk_toggle_button_get_active (button);
6526     SPStyle *query =
6527         sp_style_new (SP_ACTIVE_DOCUMENT);
6529     int result_fontspec =
6530         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6532     //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6533     //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6534     //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6536     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
6537     Glib::ustring newFontSpec = "";
6539     if (fontSpec.empty()) {
6540         // Construct a new font specification if it does not yet exist
6541         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6542         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6543         fontFromStyle->Unref();
6544     }
6546     bool nochange = true;
6547     switch (prop)
6548     {
6549         case 0:
6550         {
6551             if (!fontSpec.empty()) {
6552                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
6553                 if (!newFontSpec.empty()) {
6554                     // Don't even set the bold if the font didn't exist on the system
6555                     sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
6556                     nochange = false;
6557                 }
6558             }
6559             // set or reset the button according
6560             if(nochange) {
6561                 gboolean check = gtk_toggle_button_get_active (button);
6563                 if (active != check)
6564                 {
6565                     g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6566                     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6567                     g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6568                 }
6569             }
6571             break;
6572         }
6574         case 1:
6575         {
6576             if (!fontSpec.empty()) {
6577                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
6578                 if (!newFontSpec.empty()) {
6579                     // Don't even set the italic if the font didn't exist on the system
6580                     sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
6581                     nochange = false;
6582                 }
6583             }
6584             if(nochange) {
6585                 gboolean check = gtk_toggle_button_get_active (button);
6587                 if (active != check)
6588                 {
6589                     g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6590                     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6591                     g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6592                 }
6593             }
6594             break;
6595         }
6596     }
6598     if (!newFontSpec.empty()) {
6599         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6600     }
6602     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6603     if (result_fontspec == QUERY_STYLE_NOTHING)
6604     {
6605         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6606         prefs->mergeStyle("/tools/text/style", css);
6607     }
6609     sp_style_unref(query);
6611     sp_desktop_set_style (desktop, css, true, true);
6612     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6613                                    _("Text: Change font style"));
6614     sp_repr_css_attr_unref (css);
6616     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6619 void
6620 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
6621                                      gpointer         data)
6623     if (g_object_get_data (G_OBJECT (button), "block")) {
6624         return;
6625     }
6627     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
6628     SPCSSAttr   *css        = sp_repr_css_attr_new ();
6629     int          prop       = GPOINTER_TO_INT(data);
6631     switch (prop)
6632     {
6633         case 0:
6634         {
6635             sp_repr_css_set_property (css, "writing-mode", "lr");
6636             break;
6637         }
6639         case 1:
6640         {
6641             sp_repr_css_set_property (css, "writing-mode", "tb");
6642             break;
6643         }
6644     }
6646     SPStyle *query =
6647         sp_style_new (SP_ACTIVE_DOCUMENT);
6648     int result_numbers =
6649         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6651     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6652     if (result_numbers == QUERY_STYLE_NOTHING)
6653     {
6654         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6655         prefs->mergeStyle("/tools/text/style", css);
6656     }
6658     sp_desktop_set_style (desktop, css, true, true);
6659     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6660                                    _("Text: Change orientation"));
6661     sp_repr_css_attr_unref (css);
6663     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6666 gboolean
6667 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6669     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6670     if (!desktop) return FALSE;
6672     switch (get_group0_keyval (event)) {
6673         case GDK_KP_Enter: // chosen
6674         case GDK_Return:
6675             // unfreeze and update, which will defocus
6676             g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6677             sp_text_toolbox_family_changed (NULL, tbl);
6678             return TRUE; // I consumed the event
6679             break;
6680         case GDK_Escape:
6681             // defocus
6682             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6683             return TRUE; // I consumed the event
6684             break;
6685     }
6686     return FALSE;
6689 gboolean
6690 sp_text_toolbox_family_list_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject */*tbl*/)
6692     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6693     if (!desktop) return FALSE;
6695     switch (get_group0_keyval (event)) {
6696         case GDK_KP_Enter:
6697         case GDK_Return:
6698         case GDK_Escape: // defocus
6699             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6700             return TRUE; // I consumed the event
6701             break;
6702         case GDK_w:
6703         case GDK_W:
6704             if (event->state & GDK_CONTROL_MASK) {
6705                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6706                 return TRUE; // I consumed the event
6707             }
6708             break;
6709     }
6710     return FALSE;
6714 void
6715 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
6716                                GObject     *tbl)
6718      // quit if run by the _changed callbacks
6719     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6720         return;
6721     }
6723     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6725    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6727     // If this is not from selecting a size in the list (in which case get_active will give the
6728     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
6729     // process this event. This fixes GTK's stupid insistence on sending an activate change every
6730     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
6731    if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed")) {
6732         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6733         return;
6734    }
6736     gdouble value = -1;
6737     {
6738         gchar *endptr;
6739         gchar *const text = gtk_combo_box_get_active_text(cbox);
6740         if (text) {
6741             value = g_strtod(text, &endptr);
6742             if (endptr == text) {  // Conversion failed, non-numeric input.
6743                 value = -1;
6744             }
6745             g_free(text);
6746         }
6747     }
6748     if (value <= 0) {
6749         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6750         return; // could not parse value
6751     }
6753     SPCSSAttr *css = sp_repr_css_attr_new ();
6754     Inkscape::CSSOStringStream osfs;
6755     osfs << value;
6756     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
6758     SPStyle *query =
6759         sp_style_new (SP_ACTIVE_DOCUMENT);
6760     int result_numbers =
6761         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6763     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6764     if (result_numbers == QUERY_STYLE_NOTHING)
6765     {
6766         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6767         prefs->mergeStyle("/tools/text/style", css);
6768     }
6770     sp_style_unref(query);
6772     sp_desktop_set_style (desktop, css, true, true);
6773     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
6774                                    _("Text: Change font size"));
6775     sp_repr_css_attr_unref (css);
6777     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6779     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6782 gboolean
6783 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
6785     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6786     if (!desktop) return FALSE;
6788     if (!g_object_get_data (tbl, "esc-pressed")) {
6789         g_object_set_data (tbl, "enter-pressed", gpointer(1));
6790         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6791         sp_text_toolbox_size_changed (cbox, tbl);
6792         g_object_set_data (tbl, "enter-pressed", gpointer(0));
6793     }
6794     return FALSE; // I consumed the event
6798 gboolean
6799 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6801     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6802     if (!desktop) return FALSE;
6804     switch (get_group0_keyval (event)) {
6805         case GDK_Escape: // defocus
6806             g_object_set_data (tbl, "esc-pressed", gpointer(1));
6807             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6808             g_object_set_data (tbl, "esc-pressed", gpointer(0));
6809             return TRUE; // I consumed the event
6810             break;
6811         case GDK_Return: // defocus
6812         case GDK_KP_Enter:
6813             g_object_set_data (tbl, "enter-pressed", gpointer(1));
6814             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6815             sp_text_toolbox_size_changed (cbox, tbl);
6816             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6817             g_object_set_data (tbl, "enter-pressed", gpointer(0));
6818             return TRUE; // I consumed the event
6819             break;
6820     }
6821     return FALSE;
6824 // While editing font name in the entry, disable family_changed by freezing, otherwise completion
6825 // does not work!
6826 gboolean
6827 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
6828                                  GdkEventFocus    */*event*/,
6829                                  GObject          *tbl)
6831     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6832     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1); // select all
6833     return FALSE;
6836 gboolean
6837 sp_text_toolbox_entry_focus_out  (GtkWidget        *entry,
6838                                  GdkEventFocus    */*event*/,
6839                                  GObject          *tbl)
6841     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6842     gtk_entry_select_region (GTK_ENTRY (entry), 0, 0); // deselect
6843     return FALSE;
6846 void
6847 cell_data_func  (GtkCellLayout */*cell_layout*/,
6848                  GtkCellRenderer   *cell,
6849                  GtkTreeModel      *tree_model,
6850                  GtkTreeIter       *iter,
6851                  gpointer           /*data*/)
6853     gchar *family;
6854     gtk_tree_model_get(tree_model, iter, 0, &family, -1);
6855     gchar *const family_escaped = g_markup_escape_text(family, -1);
6857     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6858     int show_sample = prefs->getInt("/tools/text/show_sample_in_list", 1);
6859     if (show_sample) {
6861         Glib::ustring sample = prefs->getString("/tools/text/font_sample");
6862         gchar *const sample_escaped = g_markup_escape_text(sample.data(), -1);
6864     std::stringstream markup;
6865     markup << family_escaped << "  <span foreground='darkgray' font_family='"
6866            << family_escaped << "'>" << sample_escaped << "</span>";
6867     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6869         g_free(sample_escaped);
6870     } else {
6871         g_object_set (G_OBJECT (cell), "markup", family_escaped, NULL);
6872     }
6874     g_free(family);
6875     g_free(family_escaped);
6878 gboolean text_toolbox_completion_match_selected(GtkEntryCompletion */*widget*/,
6879                                                 GtkTreeModel       *model,
6880                                                 GtkTreeIter        *iter,
6881                                                 GObject            *tbl)
6883     // We intercept this signal so as to fire family_changed at once (without it, you'd have to
6884     // press Enter again after choosing a completion)
6885     gchar *family = 0;
6886     gtk_tree_model_get(model, iter, 0, &family, -1);
6888     GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6889     gtk_entry_set_text (GTK_ENTRY (entry), family);
6891     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6892     sp_text_toolbox_family_changed (NULL, tbl);
6893     return TRUE;
6897 static void
6898 cbe_add_completion (GtkComboBoxEntry *cbe, GObject *tbl){
6899     GtkEntry *entry;
6900     GtkEntryCompletion *completion;
6901     GtkTreeModel *model;
6903     entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(cbe)));
6904     completion = gtk_entry_completion_new();
6905     model = gtk_combo_box_get_model(GTK_COMBO_BOX(cbe));
6906     gtk_entry_completion_set_model(completion, model);
6907     gtk_entry_completion_set_text_column(completion, 0);
6908     gtk_entry_completion_set_inline_completion(completion, FALSE);
6909     gtk_entry_completion_set_inline_selection(completion, FALSE);
6910     gtk_entry_completion_set_popup_completion(completion, TRUE);
6911     gtk_entry_set_completion(entry, completion);
6913     g_signal_connect (G_OBJECT (completion),  "match-selected", G_CALLBACK (text_toolbox_completion_match_selected), tbl);
6915     g_object_unref(completion);
6918 void sp_text_toolbox_family_popnotify(GtkComboBox *widget,
6919                                       void */*property*/,
6920                                       GObject *tbl)
6922   // while the drop-down is open, we disable font family changing, reenabling it only when it closes
6924   gboolean shown;
6925   g_object_get (G_OBJECT(widget), "popup-shown", &shown, NULL);
6926   if (shown) {
6927          g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6928          //g_print("POP: notify: SHOWN\n");
6929   } else {
6930          g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6932          // stupid GTK doesn't let us attach to events in the drop-down window, so we peek here to
6933          // find out if the drop down was closed by Enter and if so, manually update (only
6934          // necessary on Windows, on Linux it updates itself - what a mess, but we'll manage)
6935          GdkEvent *ev = gtk_get_current_event();
6936          if (ev) {
6937              //g_print ("ev type: %d\n", ev->type);
6938              if (ev->type == GDK_KEY_PRESS) {
6939                  switch (get_group0_keyval ((GdkEventKey *) ev)) {
6940                      case GDK_KP_Enter: // chosen
6941                      case GDK_Return:
6942                      {
6943                          // make sure the chosen one is inserted into the entry
6944                          GtkComboBox  *combo = GTK_COMBO_BOX (((Gtk::ComboBox *) (g_object_get_data (tbl, "family-entry-combo")))->gobj());
6945                          GtkTreeModel *model = gtk_combo_box_get_model(combo);
6946                          GtkTreeIter iter;
6947                          gboolean has_active = gtk_combo_box_get_active_iter (combo, &iter);
6948                          if (has_active) {
6949                              gchar *family;
6950                              gtk_tree_model_get(model, &iter, 0, &family, -1);
6951                              GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6952                              gtk_entry_set_text (GTK_ENTRY (entry), family);
6953                          }
6955                          // update
6956                          sp_text_toolbox_family_changed (NULL, tbl);
6957                          break;
6958                      }
6959                  }
6960              }
6961          }
6963          // regardless of whether we updated, defocus the widget
6964          SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6965          if (desktop)
6966              gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6967          //g_print("POP: notify: HIDDEN\n");
6968   }
6971 GtkWidget *sp_text_toolbox_new (SPDesktop *desktop)
6973     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
6974     GtkIconSize secondarySize = static_cast<GtkIconSize>(ToolboxFactory::prefToSize("/toolbox/secondary", 1));
6976     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
6977     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
6979     GtkTooltips *tt = gtk_tooltips_new();
6981     ////////////Family
6982     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
6983     Gtk::ComboBoxEntry *font_sel = Gtk::manage(new Gtk::ComboBoxEntry(store));
6985     gtk_rc_parse_string (
6986        "style \"dropdown-as-list-style\"\n"
6987        "{\n"
6988        "    GtkComboBox::appears-as-list = 1\n"
6989        "}\n"
6990        "widget \"*.toolbox-fontfamily-list\" style \"dropdown-as-list-style\"");
6991     gtk_widget_set_name(GTK_WIDGET (font_sel->gobj()), "toolbox-fontfamily-list");
6992     gtk_tooltips_set_tip (tt, GTK_WIDGET (font_sel->gobj()), _("Select font family (Alt+X to access)"), "");
6994     g_signal_connect (G_OBJECT (font_sel->gobj()), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
6996     cbe_add_completion(font_sel->gobj(), G_OBJECT(tbl));
6998     gtk_toolbar_append_widget( tbl, (GtkWidget*) font_sel->gobj(), "", "");
6999     g_object_set_data (G_OBJECT (tbl), "family-entry-combo", font_sel);
7001     // expand the field a bit so as to view more of the previews in the drop-down
7002     GtkRequisition req;
7003     gtk_widget_size_request (GTK_WIDGET (font_sel->gobj()), &req);
7004     gtk_widget_set_size_request  (GTK_WIDGET (font_sel->gobj()), MIN(req.width + 50, 500), -1);
7006     GtkWidget* entry = (GtkWidget*) font_sel->get_entry()->gobj();
7007     g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
7009     g_signal_connect (G_OBJECT (font_sel->gobj()), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
7010     g_signal_connect (G_OBJECT (font_sel->gobj()), "notify::popup-shown",
7011              G_CALLBACK (sp_text_toolbox_family_popnotify), tbl);
7012     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
7013     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
7014     g_signal_connect (G_OBJECT (entry),  "focus-out-event", G_CALLBACK (sp_text_toolbox_entry_focus_out), tbl);
7016     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
7017     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
7019     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
7020     gtk_cell_layout_clear( GTK_CELL_LAYOUT(font_sel->gobj()) );
7021     gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(font_sel->gobj()) , cell , TRUE );
7022     gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT(font_sel->gobj()), cell, GtkCellLayoutDataFunc (cell_data_func), NULL, NULL);
7024     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
7025     GtkWidget *box = gtk_event_box_new ();
7026     gtk_container_add (GTK_CONTAINER (box), image);
7027     gtk_toolbar_append_widget( tbl, box, "", "");
7028     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
7029     gtk_tooltips_set_tip (tt, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
7030     gtk_widget_hide (GTK_WIDGET (box));
7031     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
7033     ////////////Size
7034     gchar const *const sizes[] = {
7035         "4", "6", "8", "9", "10", "11", "12", "13", "14",
7036         "16", "18", "20", "22", "24", "28",
7037         "32", "36", "40", "48", "56", "64", "72", "144"
7038     };
7040     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
7041     for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
7042         gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
7043     }
7044     gtk_widget_set_size_request (cbox, 80, -1);
7045     gtk_toolbar_append_widget( tbl, cbox, "", "");
7046     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
7047     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
7048     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
7049     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
7051     ////////////Text anchor
7052     GtkWidget *group   = gtk_radio_button_new (NULL);
7053     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
7054     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
7056     // left
7057     GtkWidget *rbutton = group;
7058     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7059     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
7060     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7062     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7063     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
7064     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
7065     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
7067     // center
7068     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7069     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7070     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
7071     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7073     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7074     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
7075     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
7076     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
7078     // right
7079     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7080     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7081     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
7082     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7084     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7085     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
7086     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
7087     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
7089     // fill
7090     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7091     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7092     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
7093     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7095     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7096     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
7097     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
7098     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
7100     gtk_toolbar_append_widget( tbl, row, "", "");
7102     //spacer
7103     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
7105     ////////////Text style
7106     row = gtk_hbox_new (FALSE, 4);
7108     // bold
7109     rbutton = gtk_toggle_button_new ();
7110     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7111     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
7112     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7113     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
7115     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7116     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
7117     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
7119     // italic
7120     rbutton = gtk_toggle_button_new ();
7121     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7122     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
7123     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7124     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
7126     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7127     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
7128     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
7130     gtk_toolbar_append_widget( tbl, row, "", "");
7132     //spacer
7133     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
7135     // Text orientation
7136     group   = gtk_radio_button_new (NULL);
7137     row     = gtk_hbox_new (FALSE, 4);
7138     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
7140     // horizontal
7141     rbutton = group;
7142     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7143     gtk_container_add           (GTK_CONTAINER (rbutton),
7144                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_HORIZONTAL));
7145     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7146     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
7148     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7149     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
7150     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
7152     // vertical
7153     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7154     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7155     gtk_container_add           (GTK_CONTAINER (rbutton),
7156                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_VERTICAL));
7157     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7158     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
7160     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7161     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
7162     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
7163     gtk_toolbar_append_widget( tbl, row, "", "" );
7166     //watch selection
7167     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
7169     sigc::connection *c_selection_changed =
7170         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
7171                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
7172     pool->add_connection ("selection-changed", c_selection_changed);
7174     sigc::connection *c_selection_modified =
7175         new sigc::connection (sp_desktop_selection (desktop)->connectModified
7176                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
7177     pool->add_connection ("selection-modified", c_selection_modified);
7179     sigc::connection *c_subselection_changed =
7180         new sigc::connection (desktop->connectToolSubselectionChanged
7181                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
7182     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
7184     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
7187     gtk_widget_show_all( GTK_WIDGET(tbl) );
7189     return GTK_WIDGET(tbl);
7190 } // end of sp_text_toolbox_new()
7192 }//<unnamed> namespace
7195 //#########################
7196 //##      Connector      ##
7197 //#########################
7199 static void sp_connector_mode_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7201     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7202     prefs->setBool("/tools/connector/mode",
7203                 gtk_toggle_action_get_active( act ));
7206 static void sp_connector_path_set_avoid(void)
7208     cc_selection_set_avoid(true);
7212 static void sp_connector_path_set_ignore(void)
7214     cc_selection_set_avoid(false);
7217 static void sp_connector_orthogonal_toggled( GtkToggleAction* act, GObject *tbl )
7219     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7220     Inkscape::Selection * selection = sp_desktop_selection(desktop);
7221     SPDocument *doc = sp_desktop_document(desktop);
7223     if (!sp_document_get_undo_sensitive(doc))
7224     {
7225         return;
7226     }
7229     // quit if run by the _changed callbacks
7230     if (g_object_get_data( tbl, "freeze" )) {
7231         return;
7232     }
7234     // in turn, prevent callbacks from responding
7235     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7237     bool is_orthog = gtk_toggle_action_get_active( act );
7238     gchar orthog_str[] = "orthogonal";
7239     gchar polyline_str[] = "polyline";
7240     gchar *value = is_orthog ? orthog_str : polyline_str ;
7242     bool modmade = false;
7243     GSList *l = (GSList *) selection->itemList();
7244     while (l) {
7245         SPItem *item = (SPItem *) l->data;
7247         if (cc_item_is_connector(item)) {
7248             sp_object_setAttribute(item, "inkscape:connector-type",
7249                     value, false);
7250             item->avoidRef->handleSettingChange();
7251             modmade = true;
7252         }
7253         l = l->next;
7254     }
7256     if (!modmade)
7257     {
7258         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7259         prefs->setBool("/tools/connector/orthogonal", is_orthog);
7260     }
7262     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7263             is_orthog ? _("Set connector type: orthogonal"): _("Set connector type: polyline"));
7265     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7268 static void connector_curvature_changed(GtkAdjustment *adj, GObject* tbl)
7270     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7271     Inkscape::Selection * selection = sp_desktop_selection(desktop);
7272     SPDocument *doc = sp_desktop_document(desktop);
7274     if (!sp_document_get_undo_sensitive(doc))
7275     {
7276         return;
7277     }
7280     // quit if run by the _changed callbacks
7281     if (g_object_get_data( tbl, "freeze" )) {
7282         return;
7283     }
7285     // in turn, prevent callbacks from responding
7286     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7288     gdouble newValue = gtk_adjustment_get_value(adj);
7289     gchar value[G_ASCII_DTOSTR_BUF_SIZE];
7290     g_ascii_dtostr(value, G_ASCII_DTOSTR_BUF_SIZE, newValue);
7292     bool modmade = false;
7293     GSList *l = (GSList *) selection->itemList();
7294     while (l) {
7295         SPItem *item = (SPItem *) l->data;
7297         if (cc_item_is_connector(item)) {
7298             sp_object_setAttribute(item, "inkscape:connector-curvature",
7299                     value, false);
7300             item->avoidRef->handleSettingChange();
7301             modmade = true;
7302         }
7303         l = l->next;
7304     }
7306     if (!modmade)
7307     {
7308         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7309         prefs->setDouble(Glib::ustring("/tools/connector/curvature"), newValue);
7310     }
7312     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7313             _("Change connector curvature"));
7315     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7319 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
7321     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7322     SPDocument *doc = sp_desktop_document(desktop);
7324     if (!sp_document_get_undo_sensitive(doc))
7325     {
7326         return;
7327     }
7329     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7331     if ( !repr->attribute("inkscape:connector-spacing") &&
7332             ( adj->value == defaultConnSpacing )) {
7333         // Don't need to update the repr if the attribute doesn't
7334         // exist and it is being set to the default value -- as will
7335         // happen at startup.
7336         return;
7337     }
7339     // quit if run by the attr_changed listener
7340     if (g_object_get_data( tbl, "freeze" )) {
7341         return;
7342     }
7344     // in turn, prevent listener from responding
7345     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
7347     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
7348     SP_OBJECT(desktop->namedview)->updateRepr();
7350     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
7351     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
7352         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
7353         Geom::Matrix m = Geom::identity();
7354         avoid_item_move(&m, item);
7355     }
7357     if (items) {
7358         g_slist_free(items);
7359     }
7361     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7362             _("Change connector spacing"));
7364     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7367 static void sp_connector_graph_layout(void)
7369     if (!SP_ACTIVE_DESKTOP) return;
7370     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7372     // hack for clones, see comment in align-and-distribute.cpp
7373     int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7374     prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7376     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
7378     prefs->setInt("/options/clonecompensation/value", saved_compensation);
7380     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
7383 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7385     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7386     prefs->setBool("/tools/connector/directedlayout",
7387                 gtk_toggle_action_get_active( act ));
7390 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7392     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7393     prefs->setBool("/tools/connector/avoidoverlaplayout",
7394                 gtk_toggle_action_get_active( act ));
7398 static void connector_length_changed(GtkAdjustment *adj, GObject* /*tbl*/)
7400     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7401     prefs->setDouble("/tools/connector/length", adj->value);
7404 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
7405                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
7406                                             bool /*is_interactive*/, gpointer data)
7408     GtkWidget *tbl = GTK_WIDGET(data);
7410     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
7411         return;
7412     }
7413     if (strcmp(name, "inkscape:connector-spacing") == 0)
7414     {
7415         GtkAdjustment *adj = (GtkAdjustment*)
7416                 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
7417         gdouble spacing = defaultConnSpacing;
7418         sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
7420         gtk_adjustment_set_value(adj, spacing);
7421         gtk_adjustment_value_changed(adj);
7422     }
7424     spinbutton_defocus(GTK_OBJECT(tbl));
7427 static void sp_connector_new_connection_point(GtkWidget *, GObject *tbl)
7429     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7430     SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7432     if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE)
7433         cc_create_connection_point(cc);
7436 static void sp_connector_remove_connection_point(GtkWidget *, GObject *tbl)
7438     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7439     SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7441     if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE)
7442         cc_remove_connection_point(cc);
7445 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
7446     NULL, /* child_added */
7447     NULL, /* child_removed */
7448     connector_tb_event_attr_changed,
7449     NULL, /* content_changed */
7450     NULL  /* order_changed */
7451 };
7453 static void sp_connector_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
7455     GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "curvature" ) );
7456     GtkToggleAction *act = GTK_TOGGLE_ACTION( g_object_get_data( tbl, "orthogonal" ) );
7457     SPItem *item = selection->singleItem();
7458     if (SP_IS_PATH(item))
7459     {
7460         gdouble curvature = SP_PATH(item)->connEndPair.getCurvature();
7461         bool is_orthog = SP_PATH(item)->connEndPair.isOrthogonal();
7462         gtk_toggle_action_set_active(act, is_orthog);
7463         gtk_adjustment_set_value(adj, curvature);
7464     }
7468 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
7470     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7471     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
7473     // Editing mode toggle button
7474     {
7475         InkToggleAction* act = ink_toggle_action_new( "ConnectorEditModeAction",
7476                                                       _("EditMode"),
7477                                                       _("Switch between connection point editing and connector drawing mode"),
7478                                                       INKSCAPE_ICON_CONNECTOR_EDIT,
7479                                                       Inkscape::ICON_SIZE_DECORATION );
7480         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7482         bool tbuttonstate = prefs->getBool("/tools/connector/mode");
7483         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7484         g_object_set_data( holder, "mode", act );
7485         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_mode_toggled), holder );
7486     }
7489     {
7490         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
7491                                           _("Avoid"),
7492                                           _("Make connectors avoid selected objects"),
7493                                           INKSCAPE_ICON_CONNECTOR_AVOID,
7494                                           secondarySize );
7495         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
7496         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7497     }
7499     {
7500         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
7501                                           _("Ignore"),
7502                                           _("Make connectors ignore selected objects"),
7503                                           INKSCAPE_ICON_CONNECTOR_IGNORE,
7504                                           secondarySize );
7505         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
7506         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7507     }
7509     // Orthogonal connectors toggle button
7510     {
7511         InkToggleAction* act = ink_toggle_action_new( "ConnectorOrthogonalAction",
7512                                                       _("Orthogonal"),
7513                                                       _("Make connector orthogonal or polyline"),
7514                                                       INKSCAPE_ICON_CONNECTOR_ORTHOGONAL,
7515                                                       Inkscape::ICON_SIZE_DECORATION );
7516         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7518         bool tbuttonstate = prefs->getBool("/tools/connector/orthogonal");
7519         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7520         g_object_set_data( holder, "orthogonal", act );
7521         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_orthogonal_toggled), holder );
7522     }
7524     EgeAdjustmentAction* eact = 0;
7525     // Curvature spinbox
7526     eact = create_adjustment_action( "ConnectorCurvatureAction",
7527                                     _("Connector Curvature"), _("Curvature:"),
7528                                     _("The amount of connectors curvature"),
7529                                     "/tools/connector/curvature", defaultConnCurvature,
7530                                     GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-curvature",
7531                                     0, 100, 1.0, 10.0,
7532                                     0, 0, 0,
7533                                     connector_curvature_changed, 1, 0 );
7534     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7536     // Spacing spinbox
7537     eact = create_adjustment_action( "ConnectorSpacingAction",
7538                                     _("Connector Spacing"), _("Spacing:"),
7539                                     _("The amount of space left around objects by auto-routing connectors"),
7540                                     "/tools/connector/spacing", defaultConnSpacing,
7541                                     GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
7542                                     0, 100, 1.0, 10.0,
7543                                     0, 0, 0,
7544                                     connector_spacing_changed, 1, 0 );
7545     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7547     // Graph (connector network) layout
7548     {
7549         InkAction* inky = ink_action_new( "ConnectorGraphAction",
7550                                           _("Graph"),
7551                                           _("Nicely arrange selected connector network"),
7552                                           INKSCAPE_ICON_DISTRIBUTE_GRAPH,
7553                                           secondarySize );
7554         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
7555         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7556     }
7558     // Default connector length spinbox
7559     eact = create_adjustment_action( "ConnectorLengthAction",
7560                                      _("Connector Length"), _("Length:"),
7561                                      _("Ideal length for connectors when layout is applied"),
7562                                      "/tools/connector/length", 100,
7563                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
7564                                      10, 1000, 10.0, 100.0,
7565                                      0, 0, 0,
7566                                      connector_length_changed, 1, 0 );
7567     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7570     // Directed edges toggle button
7571     {
7572         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
7573                                                       _("Downwards"),
7574                                                       _("Make connectors with end-markers (arrows) point downwards"),
7575                                                       INKSCAPE_ICON_DISTRIBUTE_GRAPH_DIRECTED,
7576                                                       Inkscape::ICON_SIZE_DECORATION );
7577         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7579         bool tbuttonstate = prefs->getBool("/tools/connector/directedlayout");
7580         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7582         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
7583         sigc::connection *connection = new sigc::connection(sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_connector_toolbox_selection_changed), (GObject *)holder))
7584         );
7585     }
7587     // Avoid overlaps toggle button
7588     {
7589         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
7590                                                       _("Remove overlaps"),
7591                                                       _("Do not allow overlapping shapes"),
7592                                                       INKSCAPE_ICON_DISTRIBUTE_REMOVE_OVERLAPS,
7593                                                       Inkscape::ICON_SIZE_DECORATION );
7594         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7596         bool tbuttonstate = prefs->getBool("/tools/connector/avoidoverlaplayout");
7597         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), (tbuttonstate ? TRUE : FALSE ));
7599         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
7600     }
7603     // New connection point button
7604     {
7605         InkAction* inky = ink_action_new( "ConnectorNewConnPointAction",
7606                                           _("New connection point"),
7607                                           _("Add a new connection point to the currently selected item"),
7608                                           INKSCAPE_ICON_CONNECTOR_NEW_CONNPOINT,
7609                                           secondarySize );
7610         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_new_connection_point), holder );
7611         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7612     }
7614     // Remove selected connection point button
7616     {
7617         InkAction* inky = ink_action_new( "ConnectorRemoveConnPointAction",
7618                                           _("Remove connection point"),
7619                                           _("Remove the currently selected connection point"),
7620                                           INKSCAPE_ICON_CONNECTOR_REMOVE_CONNPOINT,
7621                                           secondarySize );
7622         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_remove_connection_point), holder );
7623         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7624     }
7627     // Code to watch for changes to the connector-spacing attribute in
7628     // the XML.
7629     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7630     g_assert(repr != NULL);
7632     purge_repr_listener( holder, holder );
7634     if (repr) {
7635         g_object_set_data( holder, "repr", repr );
7636         Inkscape::GC::anchor(repr);
7637         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
7638         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
7639     }
7640 } // end of sp_connector_toolbox_prep()
7643 //#########################
7644 //##     Paintbucket     ##
7645 //#########################
7647 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
7649     gint channels = ege_select_one_action_get_active( act );
7650     flood_channels_set_channels( channels );
7653 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
7655     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7656     prefs->setInt("/tools/paintbucket/threshold", (gint)adj->value);
7659 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
7661     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7662     prefs->setBool("/tools/paintbucket/autogap", ege_select_one_action_get_active( act ));
7665 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
7667     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
7668     SPUnit const *unit = tracker->getActiveUnit();
7669     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7671     prefs->setDouble("/tools/paintbucket/offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
7672     prefs->setString("/tools/paintbucket/offsetunits", sp_unit_get_abbreviation(unit));
7675 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
7677     // FIXME: make defaults settable via Inkscape Options
7678     struct KeyValue {
7679         char const *key;
7680         double value;
7681     } const key_values[] = {
7682         {"threshold", 15},
7683         {"offset", 0.0}
7684     };
7686     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
7687         KeyValue const &kv = key_values[i];
7688         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
7689         if ( adj ) {
7690             gtk_adjustment_set_value(adj, kv.value);
7691         }
7692     }
7694     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
7695     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
7696     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
7697     ege_select_one_action_set_active( autogap_action, 0 );
7700 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
7702     EgeAdjustmentAction* eact = 0;
7703     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7705     {
7706         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7708         GList* items = 0;
7709         gint count = 0;
7710         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
7711         {
7712             GtkTreeIter iter;
7713             gtk_list_store_append( model, &iter );
7714             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7715             count++;
7716         }
7717         g_list_free( items );
7718         items = 0;
7719         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
7720         g_object_set( act1, "short_label", _("Fill by:"), NULL );
7721         ege_select_one_action_set_appearance( act1, "compact" );
7722         ege_select_one_action_set_active( act1, prefs->getInt("/tools/paintbucket/channels", 0) );
7723         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
7724         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
7725         g_object_set_data( holder, "channels_action", act1 );
7726     }
7728     // Spacing spinbox
7729     {
7730         eact = create_adjustment_action(
7731             "ThresholdAction",
7732             _("Fill Threshold"), _("Threshold:"),
7733             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
7734             "/tools/paintbucket/threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
7735             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
7736             0, 0, 0,
7737             paintbucket_threshold_changed, 1, 0 );
7739         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
7740         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7741     }
7743     // Create the units menu.
7744     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
7745     Glib::ustring stored_unit = prefs->getString("/tools/paintbucket/offsetunits");
7746     if (!stored_unit.empty())
7747         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit.data()));
7748     g_object_set_data( holder, "tracker", tracker );
7749     {
7750         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
7751         gtk_action_group_add_action( mainActions, act );
7752     }
7754     // Offset spinbox
7755     {
7756         eact = create_adjustment_action(
7757             "OffsetAction",
7758             _("Grow/shrink by"), _("Grow/shrink by:"),
7759             _("The amount to grow (positive) or shrink (negative) the created fill path"),
7760             "/tools/paintbucket/offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
7761             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
7762             0, 0, 0,
7763             paintbucket_offset_changed, 1, 2);
7764         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
7766         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7767     }
7769     /* Auto Gap */
7770     {
7771         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7773         GList* items = 0;
7774         gint count = 0;
7775         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
7776         {
7777             GtkTreeIter iter;
7778             gtk_list_store_append( model, &iter );
7779             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7780             count++;
7781         }
7782         g_list_free( items );
7783         items = 0;
7784         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
7785         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
7786         ege_select_one_action_set_appearance( act2, "compact" );
7787         ege_select_one_action_set_active( act2, prefs->getBool("/tools/paintbucket/autogap") );
7788         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
7789         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
7790         g_object_set_data( holder, "autogap_action", act2 );
7791     }
7793     /* Reset */
7794     {
7795         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
7796                                           _("Defaults"),
7797                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
7798                                           GTK_STOCK_CLEAR );
7799         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
7800         gtk_action_group_add_action( mainActions, act );
7801         gtk_action_set_sensitive( act, TRUE );
7802     }
7806 /*
7807   Local Variables:
7808   mode:c++
7809   c-file-style:"stroustrup"
7810   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
7811   indent-tabs-mode:nil
7812   fill-column:99
7813   End:
7814 */
7815 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :