Code

b4dc4ee17bb789889940b52c4b44be5916170ff3
[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( 2, 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                             -1 );
850         gtk_list_store_append( model, &iter );
851         gtk_list_store_set( model, &iter,
852                             0, _("Custom"),
853                             1, _("Set the custom task"),
854                             -1 );
856         EgeSelectOneAction* act = ege_select_one_action_new( "TaskSetAction", _("Task"), (""), NULL, GTK_TREE_MODEL(model) );
857         g_object_set( act, "short_label", _("Task:"), NULL );
858         mainActions->add(Glib::wrap(GTK_ACTION(act)));
859         //g_object_set_data( holder, "mode_action", act );
861         ege_select_one_action_set_appearance( act, "minimal" );
862         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
863         //ege_select_one_action_set_icon_size( act, secondarySize );
864         ege_select_one_action_set_tooltip_column( act, 1  );
866         //ege_select_one_action_set_active( act, mode );
867         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(fireTaskChange), desktop );
868     }
870     return mainActions;
874 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
876     gtk_widget_set_size_request( widget,
877                                  widget->allocation.width,
878                                  widget->allocation.height );
881 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
883     gtk_widget_set_size_request( widget, -1, -1 );
886 static GtkWidget* toolboxNewCommon( GtkWidget* tb, BarId id, GtkPositionType handlePos )
888     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
890     gtk_widget_set_sensitive(tb, FALSE);
892     GtkWidget *hb = gtk_handle_box_new();
893     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), handlePos);
894     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
895     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
897     gtk_container_add(GTK_CONTAINER(hb), tb);
898     gtk_widget_show(GTK_WIDGET(tb));
900     sigc::connection* conn = new sigc::connection;
901     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
903     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
904     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
906     gpointer val = GINT_TO_POINTER(id);
907     g_object_set_data(G_OBJECT(hb), BAR_ID_KEY, val);
909     return hb;
912 GtkWidget *ToolboxFactory::createToolToolbox()
914     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
916     return toolboxNewCommon( tb, BAR_TOOL, GTK_POS_TOP );
919 GtkWidget *ToolboxFactory::createAuxToolbox()
921     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
923     return toolboxNewCommon( tb, BAR_AUX, GTK_POS_LEFT );
926 //####################################
927 //# Commands Bar
928 //####################################
930 GtkWidget *ToolboxFactory::createCommandsToolbox()
932     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
934     return toolboxNewCommon( tb, BAR_COMMANDS, GTK_POS_LEFT );
937 GtkWidget *ToolboxFactory::createSnapToolbox()
939     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
941     return toolboxNewCommon( tb, BAR_SNAP, GTK_POS_LEFT );
944 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
945                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
946                                                        Glib::ustring const &path, gdouble def,
947                                                        GtkWidget *focusTarget,
948                                                        GtkWidget *us,
949                                                        GObject *dataKludge,
950                                                        gboolean altx, gchar const *altx_mark,
951                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
952                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
953                                                        void (*callback)(GtkAdjustment *, GObject *),
954                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
956     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
957     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs->getDouble(path, def) * factor,
958                                                              lower, upper, step, page, 0 ) );
959     if (us) {
960         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
961     }
963     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
965     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
966     if ( shortLabel ) {
967         g_object_set( act, "short_label", shortLabel, NULL );
968     }
970     if ( (descrCount > 0) && descrLabels && descrValues ) {
971         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
972     }
974     if ( focusTarget ) {
975         ege_adjustment_action_set_focuswidget( act, focusTarget );
976     }
978     if ( altx && altx_mark ) {
979         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
980     }
982     if ( dataKludge ) {
983         // Rather lame, but it's the only place where we need to get the entry name
984         // but we don't have an Entry
985         g_object_set_data( dataKludge, prefs->getEntry(path).getEntryName().data(), adj );
986     }
988     // Using a cast just to make sure we pass in the right kind of function pointer
989     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
991     return act;
995 //####################################
996 //# node editing callbacks
997 //####################################
999 /**
1000  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
1001  */
1002 static ShapeEditor *get_current_shape_editor()
1004     if (!SP_ACTIVE_DESKTOP) {
1005         return NULL;
1006     }
1008     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
1010     if (!SP_IS_NODE_CONTEXT(event_context)) {
1011         return NULL;
1012     }
1014     return event_context->shape_editor;
1018 void
1019 sp_node_path_edit_add(void)
1021     ShapeEditor *shape_editor = get_current_shape_editor();
1022     if (shape_editor) shape_editor->add_node();
1025 void
1026 sp_node_path_edit_delete(void)
1028     ShapeEditor *shape_editor = get_current_shape_editor();
1029     if (shape_editor) shape_editor->delete_nodes_preserving_shape();
1032 void
1033 sp_node_path_edit_delete_segment(void)
1035     ShapeEditor *shape_editor = get_current_shape_editor();
1036     if (shape_editor) shape_editor->delete_segment();
1039 void
1040 sp_node_path_edit_break(void)
1042     ShapeEditor *shape_editor = get_current_shape_editor();
1043     if (shape_editor) shape_editor->break_at_nodes();
1046 void
1047 sp_node_path_edit_join(void)
1049     ShapeEditor *shape_editor = get_current_shape_editor();
1050     if (shape_editor) shape_editor->join_nodes();
1053 void
1054 sp_node_path_edit_join_segment(void)
1056     ShapeEditor *shape_editor = get_current_shape_editor();
1057     if (shape_editor) shape_editor->join_segments();
1060 void
1061 sp_node_path_edit_toline(void)
1063     ShapeEditor *shape_editor = get_current_shape_editor();
1064     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1067 void
1068 sp_node_path_edit_tocurve(void)
1070     ShapeEditor *shape_editor = get_current_shape_editor();
1071     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1074 void
1075 sp_node_path_edit_cusp(void)
1077     ShapeEditor *shape_editor = get_current_shape_editor();
1078     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1081 void
1082 sp_node_path_edit_smooth(void)
1084     ShapeEditor *shape_editor = get_current_shape_editor();
1085     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1088 void
1089 sp_node_path_edit_symmetrical(void)
1091     ShapeEditor *shape_editor = get_current_shape_editor();
1092     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1095 void
1096 sp_node_path_edit_auto(void)
1098     ShapeEditor *shape_editor = get_current_shape_editor();
1099     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_AUTO);
1102 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1103     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1104     bool show = gtk_toggle_action_get_active( act );
1105     prefs->setBool("/tools/nodes/show_handles",  show);
1106     ShapeEditor *shape_editor = get_current_shape_editor();
1107     if (shape_editor) shape_editor->show_handles(show);
1110 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1111     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1112     bool show = gtk_toggle_action_get_active( act );
1113     prefs->setBool("/tools/nodes/show_helperpath",  show);
1114     ShapeEditor *shape_editor = get_current_shape_editor();
1115     if (shape_editor) shape_editor->show_helperpath(show);
1118 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1119     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1122 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1123     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1126 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1127     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1130 /* is called when the node selection is modified */
1131 static void
1132 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1134     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1135     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1136     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1137     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1139     // quit if run by the attr_changed listener
1140     if (g_object_get_data( tbl, "freeze" )) {
1141         return;
1142     }
1144     // in turn, prevent listener from responding
1145     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1147     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1148     SPUnit const *unit = tracker->getActiveUnit();
1150     ShapeEditor *shape_editor = get_current_shape_editor();
1151     if (shape_editor && shape_editor->has_nodepath()) {
1152         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1153         int n_selected = 0;
1154         if (nodepath) {
1155             n_selected = nodepath->numSelected();
1156         }
1158         if (n_selected == 0) {
1159             gtk_action_set_sensitive(xact, FALSE);
1160             gtk_action_set_sensitive(yact, FALSE);
1161         } else {
1162             gtk_action_set_sensitive(xact, TRUE);
1163             gtk_action_set_sensitive(yact, TRUE);
1164             Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1165             Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1167             if (n_selected == 1) {
1168                 Geom::Point sel_node = nodepath->singleSelectedCoords();
1169                 if (oldx != sel_node[Geom::X] || oldy != sel_node[Geom::Y]) {
1170                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[Geom::X], *unit));
1171                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[Geom::Y], *unit));
1172                 }
1173             } else {
1174                 boost::optional<Geom::Coord> x = sp_node_selected_common_coord(nodepath, Geom::X);
1175                 boost::optional<Geom::Coord> y = sp_node_selected_common_coord(nodepath, Geom::Y);
1176                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1177                     /* Note: Currently x and y will always have a value, even if the coordinates of the
1178                        selected nodes don't coincide (in this case we use the coordinates of the center
1179                        of the bounding box). So the entries are never set to zero. */
1180                     // FIXME: Maybe we should clear the entry if several nodes are selected
1181                     //        instead of providing a kind of average value
1182                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1183                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1184                 }
1185             }
1186         }
1187     } else {
1188         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1189         gtk_action_set_sensitive(xact, FALSE);
1190         gtk_action_set_sensitive(yact, FALSE);
1191     }
1193     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1196 static void
1197 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1199     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1200     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1202     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1203     SPUnit const *unit = tracker->getActiveUnit();
1205     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1206         prefs->setDouble(Glib::ustring("/tools/nodes/") + value_name, sp_units_get_pixels(adj->value, *unit));
1207     }
1209     // quit if run by the attr_changed listener
1210     if (g_object_get_data( tbl, "freeze" )) {
1211         return;
1212     }
1214     // in turn, prevent listener from responding
1215     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1217     ShapeEditor *shape_editor = get_current_shape_editor();
1218     if (shape_editor && shape_editor->has_nodepath()) {
1219         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1220         if (!strcmp(value_name, "x")) {
1221             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::X);
1222         }
1223         if (!strcmp(value_name, "y")) {
1224             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::Y);
1225         }
1226     }
1228     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1231 static void
1232 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1234     sp_node_path_value_changed(adj, tbl, "x");
1237 static void
1238 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1240     sp_node_path_value_changed(adj, tbl, "y");
1243 void
1244 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1246     {
1247     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1248     SPItem *item = selection->singleItem();
1249     if (item && SP_IS_LPE_ITEM(item)) {
1250        if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1251            gtk_action_set_sensitive(w, TRUE);
1252        } else {
1253            gtk_action_set_sensitive(w, FALSE);
1254        }
1255     } else {
1256        gtk_action_set_sensitive(w, FALSE);
1257     }
1258     }
1260     {
1261     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1262     SPItem *item = selection->singleItem();
1263     if (item && item->clip_ref && item->clip_ref->getObject()) {
1264        gtk_action_set_sensitive(w, TRUE);
1265     } else {
1266        gtk_action_set_sensitive(w, FALSE);
1267     }
1268     }
1270     {
1271     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1272     SPItem *item = selection->singleItem();
1273     if (item && item->mask_ref && item->mask_ref->getObject()) {
1274        gtk_action_set_sensitive(w, TRUE);
1275     } else {
1276        gtk_action_set_sensitive(w, FALSE);
1277     }
1278     }
1281 void
1282 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1284     sp_node_toolbox_sel_changed (selection, tbl);
1289 //################################
1290 //##    Node Editing Toolbox    ##
1291 //################################
1293 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1295     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1296     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1297     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1298     g_object_set_data( holder, "tracker", tracker );
1300     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
1302     {
1303         InkAction* inky = ink_action_new( "NodeInsertAction",
1304                                           _("Insert node"),
1305                                           _("Insert new nodes into selected segments"),
1306                                           INKSCAPE_ICON_NODE_ADD,
1307                                           secondarySize );
1308         g_object_set( inky, "short_label", _("Insert"), NULL );
1309         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1310         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1311     }
1313     {
1314         InkAction* inky = ink_action_new( "NodeDeleteAction",
1315                                           _("Delete node"),
1316                                           _("Delete selected nodes"),
1317                                           INKSCAPE_ICON_NODE_DELETE,
1318                                           secondarySize );
1319         g_object_set( inky, "short_label", _("Delete"), NULL );
1320         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1321         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1322     }
1324     {
1325         InkAction* inky = ink_action_new( "NodeJoinAction",
1326                                           _("Join endnodes"),
1327                                           _("Join selected endnodes"),
1328                                           INKSCAPE_ICON_NODE_JOIN,
1329                                           secondarySize );
1330         g_object_set( inky, "short_label", _("Join"), NULL );
1331         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1332         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1333     }
1335     {
1336         InkAction* inky = ink_action_new( "NodeBreakAction",
1337                                           _("Break nodes"),
1338                                           _("Break path at selected nodes"),
1339                                           INKSCAPE_ICON_NODE_BREAK,
1340                                           secondarySize );
1341         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1342         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1343     }
1346     {
1347         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1348                                           _("Join with segment"),
1349                                           _("Join selected endnodes with a new segment"),
1350                                           INKSCAPE_ICON_NODE_JOIN_SEGMENT,
1351                                           secondarySize );
1352         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1353         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1354     }
1356     {
1357         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1358                                           _("Delete segment"),
1359                                           _("Delete segment between two non-endpoint nodes"),
1360                                           INKSCAPE_ICON_NODE_DELETE_SEGMENT,
1361                                           secondarySize );
1362         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1363         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1364     }
1366     {
1367         InkAction* inky = ink_action_new( "NodeCuspAction",
1368                                           _("Node Cusp"),
1369                                           _("Make selected nodes corner"),
1370                                           INKSCAPE_ICON_NODE_TYPE_CUSP,
1371                                           secondarySize );
1372         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1373         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1374     }
1376     {
1377         InkAction* inky = ink_action_new( "NodeSmoothAction",
1378                                           _("Node Smooth"),
1379                                           _("Make selected nodes smooth"),
1380                                           INKSCAPE_ICON_NODE_TYPE_SMOOTH,
1381                                           secondarySize );
1382         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1383         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1384     }
1386     {
1387         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1388                                           _("Node Symmetric"),
1389                                           _("Make selected nodes symmetric"),
1390                                           INKSCAPE_ICON_NODE_TYPE_SYMMETRIC,
1391                                           secondarySize );
1392         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1393         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1394     }
1396     {
1397         InkAction* inky = ink_action_new( "NodeAutoAction",
1398                                           _("Node Auto"),
1399                                           _("Make selected nodes auto-smooth"),
1400                                           INKSCAPE_ICON_NODE_TYPE_AUTO_SMOOTH,
1401                                           secondarySize );
1402         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_auto), 0 );
1403         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1404     }
1406     {
1407         InkAction* inky = ink_action_new( "NodeLineAction",
1408                                           _("Node Line"),
1409                                           _("Make selected segments lines"),
1410                                           INKSCAPE_ICON_NODE_SEGMENT_LINE,
1411                                           secondarySize );
1412         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1413         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1414     }
1416     {
1417         InkAction* inky = ink_action_new( "NodeCurveAction",
1418                                           _("Node Curve"),
1419                                           _("Make selected segments curves"),
1420                                           INKSCAPE_ICON_NODE_SEGMENT_CURVE,
1421                                           secondarySize );
1422         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1423         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1424     }
1426     {
1427         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1428                                                       _("Show Handles"),
1429                                                       _("Show the Bezier handles of selected nodes"),
1430                                                       INKSCAPE_ICON_SHOW_NODE_HANDLES,
1431                                                       Inkscape::ICON_SIZE_DECORATION );
1432         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1433         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1434         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_handles", true) );
1435     }
1437     {
1438         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1439                                                       _("Show Outline"),
1440                                                       _("Show the outline of the path"),
1441                                                       INKSCAPE_ICON_SHOW_PATH_OUTLINE,
1442                                                       Inkscape::ICON_SIZE_DECORATION );
1443         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1444         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1445         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_helperpath", false) );
1446     }
1448     {
1449         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1450                                           _("Next path effect parameter"),
1451                                           _("Show next path effect parameter for editing"),
1452                                           INKSCAPE_ICON_PATH_EFFECT_PARAMETER_NEXT,
1453                                           Inkscape::ICON_SIZE_DECORATION );
1454         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1455         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1456         g_object_set_data( holder, "nodes_lpeedit", inky);
1457     }
1459     {
1460         InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1461                                           _("Edit clipping path"),
1462                                           _("Edit the clipping path of the object"),
1463                                           INKSCAPE_ICON_PATH_CLIP_EDIT,
1464                                           Inkscape::ICON_SIZE_DECORATION );
1465         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1466         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1467         g_object_set_data( holder, "nodes_clippathedit", inky);
1468     }
1470     {
1471         InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1472                                           _("Edit mask path"),
1473                                           _("Edit the mask of the object"),
1474                                           INKSCAPE_ICON_PATH_MASK_EDIT,
1475                                           Inkscape::ICON_SIZE_DECORATION );
1476         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1477         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1478         g_object_set_data( holder, "nodes_maskedit", inky);
1479     }
1481     /* X coord of selected node(s) */
1482     {
1483         EgeAdjustmentAction* eact = 0;
1484         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1485         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1486         eact = create_adjustment_action( "NodeXAction",
1487                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1488                                          "/tools/nodes/Xcoord", 0,
1489                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1490                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1491                                          labels, values, G_N_ELEMENTS(labels),
1492                                          sp_node_path_x_value_changed );
1493         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1494         g_object_set_data( holder, "nodes_x_action", eact );
1495         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1496         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1497     }
1499     /* Y coord of selected node(s) */
1500     {
1501         EgeAdjustmentAction* eact = 0;
1502         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1503         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1504         eact = create_adjustment_action( "NodeYAction",
1505                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1506                                          "/tools/nodes/Ycoord", 0,
1507                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1508                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1509                                          labels, values, G_N_ELEMENTS(labels),
1510                                          sp_node_path_y_value_changed );
1511         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1512         g_object_set_data( holder, "nodes_y_action", eact );
1513         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1514         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1515     }
1517     // add the units menu
1518     {
1519         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1520         gtk_action_group_add_action( mainActions, act );
1521     }
1524     sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1526     //watch selection
1527     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1529     sigc::connection *c_selection_changed =
1530         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1531                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1532     pool->add_connection ("selection-changed", c_selection_changed);
1534     sigc::connection *c_selection_modified =
1535         new sigc::connection (sp_desktop_selection (desktop)->connectModified
1536                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1537     pool->add_connection ("selection-modified", c_selection_modified);
1539     sigc::connection *c_subselection_changed =
1540         new sigc::connection (desktop->connectToolSubselectionChanged
1541                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1542     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1544     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1546     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1547 } // end of sp_node_toolbox_prep()
1550 //########################
1551 //##    Zoom Toolbox    ##
1552 //########################
1554 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1556     // no custom GtkAction setup needed
1557 } // end of sp_zoom_toolbox_prep()
1559 void ToolboxFactory::setToolboxDesktop(GtkWidget *toolbox, SPDesktop *desktop)
1561     sigc::connection *conn = static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1562                                                                               "event_context_connection"));
1564     BarId id = static_cast<BarId>( GPOINTER_TO_INT(g_object_get_data(G_OBJECT(toolbox), BAR_ID_KEY)) );
1566     SetupFunction setup_func = 0;
1567     UpdateFunction update_func = 0;
1569     switch (id) {
1570         case BAR_TOOL:
1571             setup_func = setup_tool_toolbox;
1572             update_func = update_tool_toolbox;
1573             break;
1575         case BAR_AUX:
1576             toolbox = gtk_bin_get_child(GTK_BIN(toolbox));
1577             setup_func = setup_aux_toolbox;
1578             update_func = update_aux_toolbox;
1579             break;
1581         case BAR_COMMANDS:
1582             setup_func = setup_commands_toolbox;
1583             update_func = update_commands_toolbox;
1584             break;
1586         case BAR_SNAP:
1587             setup_func = setup_snap_toolbox;
1588             update_func = updateSnapToolbox;
1589             break;
1590         default:
1591             g_warning("Unexpected toolbox id encountered.");
1592     }
1594     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1595     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1597     if (old_desktop) {
1598         GList *children, *iter;
1600         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1601         for ( iter = children ; iter ; iter = iter->next ) {
1602             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1603         }
1604         g_list_free(children);
1605     }
1607     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1609     if (desktop && setup_func && update_func) {
1610         gtk_widget_set_sensitive(toolbox, TRUE);
1611         setup_func(toolbox, desktop);
1612         update_func(desktop, desktop->event_context, toolbox);
1613         *conn = desktop->connectEventContextChanged(sigc::bind (sigc::ptr_fun(update_func), toolbox));
1614     } else {
1615         gtk_widget_set_sensitive(toolbox, FALSE);
1616     }
1618 } // end of sp_toolbox_set_desktop()
1621 static void setupToolboxCommon( GtkWidget *toolbox,
1622                                 SPDesktop *desktop,
1623                                 gchar const *descr,
1624                                 gchar const* toolbarName,
1625                                 gchar const* sizePref )
1627     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1628     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1630     GtkUIManager* mgr = gtk_ui_manager_new();
1631     GError* errVal = 0;
1633     GtkOrientation orientation = GTK_ORIENTATION_HORIZONTAL;
1635     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1636     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1638     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, toolbarName );
1639     if ( prefs->getBool("/toolbox/icononly", true) ) {
1640         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1641     }
1643     Inkscape::IconSize toolboxSize = ToolboxFactory::prefToSize(sizePref);
1644     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1646     if (GTK_IS_HANDLE_BOX(toolbox)) {
1647         // g_message("GRABBING ORIENTATION   [%s]", toolbarName);
1648         GtkPositionType pos = gtk_handle_box_get_handle_position(GTK_HANDLE_BOX(toolbox));
1649         orientation = ((pos == GTK_POS_LEFT) || (pos == GTK_POS_RIGHT)) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
1650     }
1651     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), orientation);
1652     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1654     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1656     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1657     if ( child ) {
1658         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1659     }
1661     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1664 void ToolboxFactory::setOrientation(GtkWidget* toolbox, GtkOrientation orientation)
1666     //g_message("Set orientation for %p to be %d", toolbox, orientation);
1667     //GType type = GTK_WIDGET_TYPE(toolbox);
1668     //g_message("        [%s]", g_type_name(type));
1669     //g_message("             %p", g_object_get_data(G_OBJECT(toolbox), BAR_ID_KEY));
1671     GtkPositionType pos = (orientation == GTK_ORIENTATION_HORIZONTAL) ? GTK_POS_LEFT : GTK_POS_TOP;
1672     GtkHandleBox* handleBox = 0;
1674     if (GTK_IS_BIN(toolbox)) {
1675         //g_message("            is a BIN");
1676         GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1677         if (child) {
1678             //GType type2 = GTK_WIDGET_TYPE(child);
1679             //g_message("            child    [%s]", g_type_name(type2));
1681             if (GTK_IS_BOX(child)) {
1682                 //g_message("                is a BOX");
1684                 GList* children = gtk_container_get_children(GTK_CONTAINER(child));
1685                 if (children && children->data) {
1686                     //GtkWidget* child2 = GTK_WIDGET(children->data);
1687                     //GType type3 = GTK_WIDGET_TYPE(child2);
1688                     //g_message("                child    [%s]", g_type_name(type3));
1689                     g_message("need to add dynamic switch");
1690                     for (GList* curr = children; curr; curr = g_list_next(curr)) {
1691                         GtkWidget* child2 = GTK_WIDGET(curr->data);
1693                         if (GTK_IS_TOOLBAR(child2)) {
1694                             GtkToolbar* childBar = GTK_TOOLBAR(child2);
1695                             gtk_toolbar_set_orientation(childBar, orientation);
1696                             if (GTK_IS_HANDLE_BOX(toolbox)) {
1697                                 handleBox = GTK_HANDLE_BOX(toolbox);
1698                             }
1699                         }
1700                     }
1701                     g_list_free(children);
1702                 } else {
1703                     if (GTK_IS_HANDLE_BOX(toolbox)) {
1704                         handleBox = GTK_HANDLE_BOX(toolbox);
1705                     }
1706                     // The call is being made before the toolbox proper has been setup.
1707                 }
1708             } else if (GTK_IS_TOOLBAR(child)) {
1709                 GtkToolbar* toolbar = GTK_TOOLBAR(child);
1710                 gtk_toolbar_set_orientation( toolbar, orientation );
1711                 if (GTK_IS_HANDLE_BOX(toolbox)) {
1712                     handleBox = GTK_HANDLE_BOX(toolbox);
1713                 }
1714             }
1715         }
1716     }
1718     if (handleBox) {
1719         gtk_handle_box_set_handle_position(handleBox, pos);
1720     }
1723 static void
1724 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1726     gchar const * descr =
1727         "<ui>"
1728         "  <toolbar name='ToolToolbar'>"
1729         "    <toolitem action='ToolSelector' />"
1730         "    <toolitem action='ToolNode' />"
1731         "    <toolitem action='ToolTweak' />"
1732         "    <toolitem action='ToolSpray' />"
1733         "    <toolitem action='ToolZoom' />"
1734         "    <toolitem action='ToolRect' />"
1735         "    <toolitem action='Tool3DBox' />"
1736         "    <toolitem action='ToolArc' />"
1737         "    <toolitem action='ToolStar' />"
1738         "    <toolitem action='ToolSpiral' />"
1739         "    <toolitem action='ToolPencil' />"
1740         "    <toolitem action='ToolPen' />"
1741         "    <toolitem action='ToolCalligraphic' />"
1742         "    <toolitem action='ToolEraser' />"
1743 //        "    <toolitem action='ToolLPETool' />"
1744         "    <toolitem action='ToolPaintBucket' />"
1745         "    <toolitem action='ToolText' />"
1746         "    <toolitem action='ToolConnector' />"
1747         "    <toolitem action='ToolGradient' />"
1748         "    <toolitem action='ToolDropper' />"
1749         "  </toolbar>"
1750         "</ui>";
1752     setupToolboxCommon( toolbox, desktop, descr,
1753                         "/ui/ToolToolbar",
1754                         "/toolbox/tools/small");
1757 static void
1758 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1760     gchar const *const tname = ( eventcontext
1761                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1762                                  : NULL );
1763     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1765     for (int i = 0 ; tools[i].type_name ; i++ ) {
1766         Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1767         if ( act ) {
1768             bool setActive = tname && !strcmp(tname, tools[i].type_name);
1769             Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1770             if ( verbAct ) {
1771                 verbAct->set_active(setActive);
1772             }
1773         }
1774     }
1777 static void
1778 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1780     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1781     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1782     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1783     GtkUIManager* mgr = gtk_ui_manager_new();
1784     GError* errVal = 0;
1785     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1786     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1788     std::map<std::string, GtkWidget*> dataHolders;
1790     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1791         if ( aux_toolboxes[i].prep_func ) {
1792             // converted to GtkActions and UIManager
1794             GtkWidget* kludge = gtk_toolbar_new();
1795             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1796             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1797             dataHolders[aux_toolboxes[i].type_name] = kludge;
1798             aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1799         } else {
1801             GtkWidget *sub_toolbox = 0;
1802             if (aux_toolboxes[i].create_func == NULL) {
1803                 sub_toolbox = sp_empty_toolbox_new(desktop);
1804             } else {
1805                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1806             }
1808             gtk_size_group_add_widget( grouper, sub_toolbox );
1810             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1811             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1813         }
1814     }
1816     // Second pass to create toolbars *after* all GtkActions are created
1817     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1818         if ( aux_toolboxes[i].prep_func ) {
1819             // converted to GtkActions and UIManager
1821             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1823             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1824             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1826             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1827             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1828             g_free( tmp );
1829             tmp = 0;
1831             if ( prefs->getBool( "/toolbox/icononly", true) ) {
1832                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1833             }
1835             Inkscape::IconSize toolboxSize = ToolboxFactory::prefToSize("/toolbox/small");
1836             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1838             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1840             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1841                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1842                 swatch->setDesktop( desktop );
1843                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1844                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1845                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1846                 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 );
1847             }
1849             gtk_widget_show_all( holder );
1850             sp_set_font_size_smaller( holder );
1852             gtk_size_group_add_widget( grouper, holder );
1854             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1855             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1856         }
1857     }
1859     g_object_unref( G_OBJECT(grouper) );
1862 static void
1863 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1865     gchar const *tname = ( eventcontext
1866                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1867                            : NULL );
1868     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1869         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1870         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1871             gtk_widget_show_all(sub_toolbox);
1872             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1873         } else {
1874             gtk_widget_hide(sub_toolbox);
1875         }
1876     }
1879 static void
1880 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1882     gchar const * descr =
1883         "<ui>"
1884         "  <toolbar name='CommandsToolbar'>"
1885         "    <toolitem action='FileNew' />"
1886         "    <toolitem action='FileOpen' />"
1887         "    <toolitem action='FileSave' />"
1888         "    <toolitem action='FilePrint' />"
1889         "    <separator />"
1890         "    <toolitem action='FileImport' />"
1891         "    <toolitem action='FileExport' />"
1892         "    <separator />"
1893         "    <toolitem action='EditUndo' />"
1894         "    <toolitem action='EditRedo' />"
1895         "    <separator />"
1896         "    <toolitem action='EditCopy' />"
1897         "    <toolitem action='EditCut' />"
1898         "    <toolitem action='EditPaste' />"
1899         "    <separator />"
1900         "    <toolitem action='ZoomSelection' />"
1901         "    <toolitem action='ZoomDrawing' />"
1902         "    <toolitem action='ZoomPage' />"
1903         "    <separator />"
1904         "    <toolitem action='EditDuplicate' />"
1905         "    <toolitem action='EditClone' />"
1906         "    <toolitem action='EditUnlinkClone' />"
1907         "    <separator />"
1908         "    <toolitem action='SelectionGroup' />"
1909         "    <toolitem action='SelectionUnGroup' />"
1910         "    <separator />"
1911         "    <toolitem action='DialogFillStroke' />"
1912         "    <toolitem action='DialogText' />"
1913         "    <toolitem action='DialogLayers' />"
1914         "    <toolitem action='DialogXMLEditor' />"
1915         "    <toolitem action='DialogAlignDistribute' />"
1916         "    <separator />"
1917         "    <toolitem action='DialogPreferences' />"
1918         "    <toolitem action='DialogDocumentProperties' />"
1919         "    <separator />"
1920         "    <toolitem action='TaskSetAction' />"
1921         "  </toolbar>"
1922         "</ui>";
1924     setupToolboxCommon( toolbox, desktop, descr,
1925                         "/ui/CommandsToolbar",
1926                         "/toolbox/small" );
1929 static void
1930 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1934 void toggle_snap_callback (GtkToggleAction *act, gpointer data) { //data points to the toolbox
1936     if (g_object_get_data(G_OBJECT(data), "freeze" )) {
1937         return;
1938     }
1940     gpointer ptr = g_object_get_data(G_OBJECT(data), "desktop");
1941     g_assert(ptr != NULL);
1943     SPDesktop *dt = reinterpret_cast<SPDesktop*>(ptr);
1944     SPNamedView *nv = sp_desktop_namedview(dt);
1945     SPDocument *doc = SP_OBJECT_DOCUMENT(nv);
1947     if (dt == NULL || nv == NULL) {
1948         g_warning("No desktop or namedview specified (in toggle_snap_callback)!");
1949         return;
1950     }
1952     Inkscape::XML::Node *repr = SP_OBJECT_REPR(nv);
1954     if (repr == NULL) {
1955         g_warning("This namedview doesn't have a xml representation attached!");
1956         return;
1957     }
1959     bool saved = sp_document_get_undo_sensitive(doc);
1960     sp_document_set_undo_sensitive(doc, false);
1962     bool v = false;
1963     SPAttributeEnum attr = (SPAttributeEnum) GPOINTER_TO_INT(g_object_get_data(G_OBJECT(act), "SP_ATTR_INKSCAPE"));
1965     switch (attr) {
1966         case SP_ATTR_INKSCAPE_SNAP_GLOBAL:
1967             dt->toggleSnapGlobal();
1968             break;
1969         case SP_ATTR_INKSCAPE_SNAP_BBOX:
1970             v = nv->snap_manager.snapprefs.getSnapModeBBox();
1971             sp_repr_set_boolean(repr, "inkscape:snap-bbox", !v);
1972             break;
1973         case SP_ATTR_INKSCAPE_BBOX_PATHS:
1974             v = nv->snap_manager.snapprefs.getSnapToBBoxPath();
1975             sp_repr_set_boolean(repr, "inkscape:bbox-paths", !v);
1976             break;
1977         case SP_ATTR_INKSCAPE_BBOX_NODES:
1978             v = nv->snap_manager.snapprefs.getSnapToBBoxNode();
1979             sp_repr_set_boolean(repr, "inkscape:bbox-nodes", !v);
1980             break;
1981         case SP_ATTR_INKSCAPE_SNAP_NODES:
1982             v = nv->snap_manager.snapprefs.getSnapModeNode();
1983             sp_repr_set_boolean(repr, "inkscape:snap-nodes", !v);
1984             break;
1985         case SP_ATTR_INKSCAPE_OBJECT_PATHS:
1986             v = nv->snap_manager.snapprefs.getSnapToItemPath();
1987             sp_repr_set_boolean(repr, "inkscape:object-paths", !v);
1988             break;
1989         case SP_ATTR_INKSCAPE_OBJECT_NODES:
1990             v = nv->snap_manager.snapprefs.getSnapToItemNode();
1991             sp_repr_set_boolean(repr, "inkscape:object-nodes", !v);
1992             break;
1993         case SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES:
1994             v = nv->snap_manager.snapprefs.getSnapSmoothNodes();
1995             sp_repr_set_boolean(repr, "inkscape:snap-smooth-nodes", !v);
1996             break;
1997         case SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS:
1998             v = nv->snap_manager.snapprefs.getSnapIntersectionCS();
1999             sp_repr_set_boolean(repr, "inkscape:snap-intersection-paths", !v);
2000             break;
2001         case SP_ATTR_INKSCAPE_SNAP_CENTER:
2002             v = nv->snap_manager.snapprefs.getIncludeItemCenter();
2003             sp_repr_set_boolean(repr, "inkscape:snap-center", !v);
2004             break;
2005         case SP_ATTR_INKSCAPE_SNAP_GRIDS:
2006             v = nv->snap_manager.snapprefs.getSnapToGrids();
2007             sp_repr_set_boolean(repr, "inkscape:snap-grids", !v);
2008             break;
2009         case SP_ATTR_INKSCAPE_SNAP_TO_GUIDES:
2010             v = nv->snap_manager.snapprefs.getSnapToGuides();
2011             sp_repr_set_boolean(repr, "inkscape:snap-to-guides", !v);
2012             break;
2013         case SP_ATTR_INKSCAPE_SNAP_PAGE:
2014             v = nv->snap_manager.snapprefs.getSnapToPageBorder();
2015             sp_repr_set_boolean(repr, "inkscape:snap-page", !v);
2016             break;
2017             /*case SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE:
2018               v = nv->snap_manager.snapprefs.getSnapIntersectionGG();
2019               sp_repr_set_boolean(repr, "inkscape:snap-intersection-grid-guide", !v);
2020               break;*/
2021         case SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS:
2022             v = nv->snap_manager.snapprefs.getSnapLineMidpoints();
2023             sp_repr_set_boolean(repr, "inkscape:snap-midpoints", !v);
2024             break;
2025         case SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS:
2026             v = nv->snap_manager.snapprefs.getSnapObjectMidpoints();
2027             sp_repr_set_boolean(repr, "inkscape:snap-object-midpoints", !v);
2028             break;
2029         case SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS:
2030             v = nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints();
2031             sp_repr_set_boolean(repr, "inkscape:snap-bbox-edge-midpoints", !v);
2032             break;
2033         case SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS:
2034             v = nv->snap_manager.snapprefs.getSnapBBoxMidpoints();
2035             sp_repr_set_boolean(repr, "inkscape:snap-bbox-midpoints", !v);
2036             break;
2037         default:
2038             g_warning("toggle_snap_callback has been called with an ID for which no action has been defined");
2039             break;
2040     }
2042     // The snapping preferences are stored in the document, and therefore toggling makes the document dirty
2043     doc->setModifiedSinceSave();
2045     sp_document_set_undo_sensitive(doc, saved);
2048 void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
2050     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2052     gchar const * descr =
2053         "<ui>"
2054         "  <toolbar name='SnapToolbar'>"
2055         "    <toolitem action='ToggleSnapGlobal' />"
2056         "    <separator />"
2057         "    <toolitem action='ToggleSnapFromBBoxCorner' />"
2058         "    <toolitem action='ToggleSnapToBBoxPath' />"
2059         "    <toolitem action='ToggleSnapToBBoxNode' />"
2060         "    <toolitem action='ToggleSnapToFromBBoxEdgeMidpoints' />"
2061         "    <toolitem action='ToggleSnapToFromBBoxCenters' />"
2062         "    <separator />"
2063         "    <toolitem action='ToggleSnapFromNode' />"
2064         "    <toolitem action='ToggleSnapToItemPath' />"
2065         "    <toolitem action='ToggleSnapToPathIntersections' />"
2066         "    <toolitem action='ToggleSnapToItemNode' />"
2067         "    <toolitem action='ToggleSnapToSmoothNodes' />"
2068         "    <toolitem action='ToggleSnapToFromLineMidpoints' />"
2069         "    <toolitem action='ToggleSnapToFromObjectCenters' />"
2070         "    <toolitem action='ToggleSnapToFromRotationCenter' />"
2071         "    <separator />"
2072         "    <toolitem action='ToggleSnapToPageBorder' />"
2073         "    <toolitem action='ToggleSnapToGrids' />"
2074         "    <toolitem action='ToggleSnapToGuides' />"
2075         //"    <toolitem action='ToggleSnapToGridGuideIntersections' />"
2076         "  </toolbar>"
2077         "</ui>";
2079     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
2081     {
2082         InkToggleAction* act = ink_toggle_action_new("ToggleSnapGlobal",
2083                                                      _("Snap"), _("Enable snapping"), INKSCAPE_ICON_SNAP, secondarySize,
2084                                                      SP_ATTR_INKSCAPE_SNAP_GLOBAL);
2086         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2087         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2088     }
2090     {
2091         InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromBBoxCorner",
2092                                                      _("Bounding box"), _("Snap bounding box corners"), INKSCAPE_ICON_SNAP_BOUNDING_BOX,
2093                                                      secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX);
2095         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2096         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2097     }
2099     {
2100         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxPath",
2101                                                      _("Bounding box edges"), _("Snap to edges of a bounding box"),
2102                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_EDGES, secondarySize, SP_ATTR_INKSCAPE_BBOX_PATHS);
2104         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2105         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2106     }
2108     {
2109         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxNode",
2110                                                      _("Bounding box corners"), _("Snap to bounding box corners"),
2111                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_CORNERS, secondarySize, SP_ATTR_INKSCAPE_BBOX_NODES);
2113         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2114         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2115     }
2117     {
2118         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxEdgeMidpoints",
2119                                                      _("BBox Edge Midpoints"), _("Snap from and to midpoints of bounding box edges"),
2120                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_MIDPOINTS, secondarySize,
2121                                                      SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS);
2123         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2124         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2125     }
2127     {
2128         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxCenters",
2129                                                      _("BBox Centers"), _("Snapping from and to centers of bounding boxes"),
2130                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS);
2132         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2133         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2134     }
2136     {
2137         InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromNode",
2138                                                      _("Nodes"), _("Snap nodes or handles"), INKSCAPE_ICON_SNAP_NODES, secondarySize, SP_ATTR_INKSCAPE_SNAP_NODES);
2140         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2141         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2142     }
2144     {
2145         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemPath",
2146                                                      _("Paths"), _("Snap to paths"), INKSCAPE_ICON_SNAP_NODES_PATH, secondarySize,
2147                                                      SP_ATTR_INKSCAPE_OBJECT_PATHS);
2149         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2150         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2151     }
2153     {
2154         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPathIntersections",
2155                                                      _("Path intersections"), _("Snap to path intersections"),
2156                                                      INKSCAPE_ICON_SNAP_NODES_INTERSECTION, secondarySize, SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS);
2158         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2159         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2160     }
2162     {
2163         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemNode",
2164                                                      _("To nodes"), _("Snap to cusp nodes"), INKSCAPE_ICON_SNAP_NODES_CUSP, secondarySize,
2165                                                      SP_ATTR_INKSCAPE_OBJECT_NODES);
2167         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2168         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2169     }
2171     {
2172         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToSmoothNodes",
2173                                                      _("Smooth nodes"), _("Snap to smooth nodes"), INKSCAPE_ICON_SNAP_NODES_SMOOTH,
2174                                                      secondarySize, SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES);
2176         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2177         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2178     }
2180     {
2181         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromLineMidpoints",
2182                                                      _("Line Midpoints"), _("Snap from and to midpoints of line segments"),
2183                                                      INKSCAPE_ICON_SNAP_NODES_MIDPOINT, secondarySize, SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS);
2185         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2186         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2187     }
2189     {
2190         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromObjectCenters",
2191                                                      _("Object Centers"), _("Snap from and to centers of objects"),
2192                                                      INKSCAPE_ICON_SNAP_NODES_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS);
2194         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2195         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2196     }
2198     {
2199         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromRotationCenter",
2200                                                      _("Rotation Centers"), _("Snap from and to an item's rotation center"),
2201                                                      INKSCAPE_ICON_SNAP_NODES_ROTATION_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_CENTER);
2203         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2204         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2205     }
2207     {
2208         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPageBorder",
2209                                                      _("Page border"), _("Snap to the page border"), INKSCAPE_ICON_SNAP_PAGE,
2210                                                      secondarySize, SP_ATTR_INKSCAPE_SNAP_PAGE);
2212         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2213         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2214     }
2216     {
2217         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGrids",
2218                                                      _("Grids"), _("Snap to grids"), INKSCAPE_ICON_GRID_RECTANGULAR, secondarySize,
2219                                                      SP_ATTR_INKSCAPE_SNAP_GRIDS);
2221         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2222         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2223     }
2225     {
2226         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGuides",
2227                                                      _("Guides"), _("Snap to guides"), INKSCAPE_ICON_GUIDES, secondarySize,
2228                                                      SP_ATTR_INKSCAPE_SNAP_TO_GUIDES);
2230         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2231         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2232     }
2234     /*{
2235       InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGridGuideIntersections",
2236       _("Grid/guide intersections"), _("Snap to intersections of a grid with a guide"),
2237       INKSCAPE_ICON_SNAP_GRID_GUIDE_INTERSECTIONS, secondarySize,
2238       SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE);
2240       gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2241       g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2242       }*/
2244     setupToolboxCommon( toolbox, desktop, descr,
2245                         "/ui/SnapToolbar",
2246                         "/toolbox/secondary" );
2249 void ToolboxFactory::updateSnapToolbox(SPDesktop *desktop, SPEventContext */*eventcontext*/, GtkWidget *toolbox)
2251     g_assert(desktop != NULL);
2252     g_assert(toolbox != NULL);
2254     SPNamedView *nv = sp_desktop_namedview(desktop);
2255     if (nv == NULL) {
2256         g_warning("Namedview cannot be retrieved (in updateSnapToolbox)!");
2257         return;
2258     }
2260     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2262     Glib::RefPtr<Gtk::Action> act1 = mainActions->get_action("ToggleSnapGlobal");
2263     Glib::RefPtr<Gtk::Action> act2 = mainActions->get_action("ToggleSnapFromBBoxCorner");
2264     Glib::RefPtr<Gtk::Action> act3 = mainActions->get_action("ToggleSnapToBBoxPath");
2265     Glib::RefPtr<Gtk::Action> act4 = mainActions->get_action("ToggleSnapToBBoxNode");
2266     Glib::RefPtr<Gtk::Action> act4b = mainActions->get_action("ToggleSnapToFromBBoxEdgeMidpoints");
2267     Glib::RefPtr<Gtk::Action> act4c = mainActions->get_action("ToggleSnapToFromBBoxCenters");
2268     Glib::RefPtr<Gtk::Action> act5 = mainActions->get_action("ToggleSnapFromNode");
2269     Glib::RefPtr<Gtk::Action> act6 = mainActions->get_action("ToggleSnapToItemPath");
2270     Glib::RefPtr<Gtk::Action> act6b = mainActions->get_action("ToggleSnapToPathIntersections");
2271     Glib::RefPtr<Gtk::Action> act7 = mainActions->get_action("ToggleSnapToItemNode");
2272     Glib::RefPtr<Gtk::Action> act8 = mainActions->get_action("ToggleSnapToSmoothNodes");
2273     Glib::RefPtr<Gtk::Action> act9 = mainActions->get_action("ToggleSnapToFromLineMidpoints");
2274     Glib::RefPtr<Gtk::Action> act10 = mainActions->get_action("ToggleSnapToFromObjectCenters");
2275     Glib::RefPtr<Gtk::Action> act11 = mainActions->get_action("ToggleSnapToFromRotationCenter");
2276     Glib::RefPtr<Gtk::Action> act12 = mainActions->get_action("ToggleSnapToPageBorder");
2277     //Glib::RefPtr<Gtk::Action> act13 = mainActions->get_action("ToggleSnapToGridGuideIntersections");
2278     Glib::RefPtr<Gtk::Action> act14 = mainActions->get_action("ToggleSnapToGrids");
2279     Glib::RefPtr<Gtk::Action> act15 = mainActions->get_action("ToggleSnapToGuides");
2282     if (!act1) {
2283         return; // The snap actions haven't been defined yet (might be the case during startup)
2284     }
2286     // The ..._set_active calls below will toggle the buttons, but this shouldn't lead to
2287     // changes in our document because we're only updating the UI;
2288     // Setting the "freeze" parameter to true will block the code in toggle_snap_callback()
2289     g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(TRUE));
2291     bool const c1 = nv->snap_manager.snapprefs.getSnapEnabledGlobally();
2292     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act1->gobj()), c1);
2294     bool const c2 = nv->snap_manager.snapprefs.getSnapModeBBox();
2295     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act2->gobj()), c2);
2296     gtk_action_set_sensitive(GTK_ACTION(act2->gobj()), c1);
2298     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act3->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxPath());
2299     gtk_action_set_sensitive(GTK_ACTION(act3->gobj()), c1 && c2);
2300     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxNode());
2301     gtk_action_set_sensitive(GTK_ACTION(act4->gobj()), c1 && c2);
2302     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4b->gobj()), nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints());
2303     gtk_action_set_sensitive(GTK_ACTION(act4b->gobj()), c1 && c2);
2304     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4c->gobj()), nv->snap_manager.snapprefs.getSnapBBoxMidpoints());
2305     gtk_action_set_sensitive(GTK_ACTION(act4c->gobj()), c1 && c2);
2307     bool const c3 = nv->snap_manager.snapprefs.getSnapModeNode();
2308     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act5->gobj()), c3);
2309     gtk_action_set_sensitive(GTK_ACTION(act5->gobj()), c1);
2311     bool const c4 = nv->snap_manager.snapprefs.getSnapToItemPath();
2312     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6->gobj()), c4);
2313     gtk_action_set_sensitive(GTK_ACTION(act6->gobj()), c1 && c3);
2314     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6b->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionCS());
2315     gtk_action_set_sensitive(GTK_ACTION(act6b->gobj()), c1 && c3 && c4);
2316     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act7->gobj()), nv->snap_manager.snapprefs.getSnapToItemNode());
2317     gtk_action_set_sensitive(GTK_ACTION(act7->gobj()), c1 && c3);
2318     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act8->gobj()), nv->snap_manager.snapprefs.getSnapSmoothNodes());
2319     gtk_action_set_sensitive(GTK_ACTION(act8->gobj()), c1 && c3);
2320     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act9->gobj()), nv->snap_manager.snapprefs.getSnapLineMidpoints());
2321     gtk_action_set_sensitive(GTK_ACTION(act9->gobj()), c1 && c3);
2322     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act10->gobj()), nv->snap_manager.snapprefs.getSnapObjectMidpoints());
2323     gtk_action_set_sensitive(GTK_ACTION(act10->gobj()), c1 && c3);
2324     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act11->gobj()), nv->snap_manager.snapprefs.getIncludeItemCenter());
2325     gtk_action_set_sensitive(GTK_ACTION(act11->gobj()), c1 && c3);
2327     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act12->gobj()), nv->snap_manager.snapprefs.getSnapToPageBorder());
2328     gtk_action_set_sensitive(GTK_ACTION(act12->gobj()), c1);
2329     //gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act13->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionGG());
2330     //gtk_action_set_sensitive(GTK_ACTION(act13->gobj()), c1);
2332     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act14->gobj()), nv->snap_manager.snapprefs.getSnapToGrids());
2333     gtk_action_set_sensitive(GTK_ACTION(act14->gobj()), c1);
2334     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act15->gobj()), nv->snap_manager.snapprefs.getSnapToGuides());
2335     gtk_action_set_sensitive(GTK_ACTION(act15->gobj()), c1);
2338     g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(FALSE)); // unfreeze (see above)
2341 void ToolboxFactory::showAuxToolbox(GtkWidget *toolbox_toplevel)
2343     gtk_widget_show(toolbox_toplevel);
2344     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
2346     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
2347     if (!shown_toolbox) {
2348         return;
2349     }
2350     gtk_widget_show(toolbox);
2352     gtk_widget_show_all(shown_toolbox);
2355 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop)
2357     GtkWidget *tbl = gtk_toolbar_new();
2358     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
2359     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
2361     gtk_widget_show_all(tbl);
2362     sp_set_font_size_smaller (tbl);
2364     return tbl;
2367 #define MODE_LABEL_WIDTH 70
2369 //########################
2370 //##       Star         ##
2371 //########################
2373 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2375     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2377     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2378         // do not remember prefs if this call is initiated by an undo change, because undoing object
2379         // creation sets bogus values to its attributes before it is deleted
2380         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2381         prefs->setInt("/tools/shapes/star/magnitude", (gint)adj->value);
2382     }
2384     // quit if run by the attr_changed listener
2385     if (g_object_get_data( dataKludge, "freeze" )) {
2386         return;
2387     }
2389     // in turn, prevent listener from responding
2390     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2392     bool modmade = false;
2394     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2395     GSList const *items = selection->itemList();
2396     for (; items != NULL; items = items->next) {
2397         if (SP_IS_STAR((SPItem *) items->data)) {
2398             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2399             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
2400             sp_repr_set_svg_double(repr, "sodipodi:arg2",
2401                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
2402                                     + M_PI / (gint)adj->value));
2403             SP_OBJECT((SPItem *) items->data)->updateRepr();
2404             modmade = true;
2405         }
2406     }
2407     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2408                                    _("Star: Change number of corners"));
2410     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2413 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2415     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2417     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2418         if (!IS_NAN(adj->value)) {
2419             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2420             prefs->setDouble("/tools/shapes/star/proportion", adj->value);
2421         }
2422     }
2424     // quit if run by the attr_changed listener
2425     if (g_object_get_data( dataKludge, "freeze" )) {
2426         return;
2427     }
2429     // in turn, prevent listener from responding
2430     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2432     bool modmade = false;
2433     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2434     GSList const *items = selection->itemList();
2435     for (; items != NULL; items = items->next) {
2436         if (SP_IS_STAR((SPItem *) items->data)) {
2437             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2439             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2440             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2441             if (r2 < r1) {
2442                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
2443             } else {
2444                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
2445             }
2447             SP_OBJECT((SPItem *) items->data)->updateRepr();
2448             modmade = true;
2449         }
2450     }
2452     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2453                                    _("Star: Change spoke ratio"));
2455     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2458 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
2460     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2461     bool flat = ege_select_one_action_get_active( act ) == 0;
2463     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2464         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2465         prefs->setBool( "/tools/shapes/star/isflatsided", flat);
2466     }
2468     // quit if run by the attr_changed listener
2469     if (g_object_get_data( dataKludge, "freeze" )) {
2470         return;
2471     }
2473     // in turn, prevent listener from responding
2474     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2476     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2477     GSList const *items = selection->itemList();
2478     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2479     bool modmade = false;
2481     if ( prop_action ) {
2482         gtk_action_set_sensitive( prop_action, !flat );
2483     }
2485     for (; items != NULL; items = items->next) {
2486         if (SP_IS_STAR((SPItem *) items->data)) {
2487             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2488             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
2489             SP_OBJECT((SPItem *) items->data)->updateRepr();
2490             modmade = true;
2491         }
2492     }
2494     if (modmade) {
2495         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2496                          flat ? _("Make polygon") : _("Make star"));
2497     }
2499     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2502 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2504     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2506     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2507         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2508         prefs->setDouble("/tools/shapes/star/rounded", (gdouble) adj->value);
2509     }
2511     // quit if run by the attr_changed listener
2512     if (g_object_get_data( dataKludge, "freeze" )) {
2513         return;
2514     }
2516     // in turn, prevent listener from responding
2517     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2519     bool modmade = false;
2521     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2522     GSList const *items = selection->itemList();
2523     for (; items != NULL; items = items->next) {
2524         if (SP_IS_STAR((SPItem *) items->data)) {
2525             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2526             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2527             SP_OBJECT(items->data)->updateRepr();
2528             modmade = true;
2529         }
2530     }
2531     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2532                                    _("Star: Change rounding"));
2534     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2537 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2539     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2541     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2542         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2543         prefs->setDouble("/tools/shapes/star/randomized", (gdouble) adj->value);
2544     }
2546     // quit if run by the attr_changed listener
2547     if (g_object_get_data( dataKludge, "freeze" )) {
2548         return;
2549     }
2551     // in turn, prevent listener from responding
2552     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2554     bool modmade = false;
2556     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2557     GSList const *items = selection->itemList();
2558     for (; items != NULL; items = items->next) {
2559         if (SP_IS_STAR((SPItem *) items->data)) {
2560             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2561             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2562             SP_OBJECT(items->data)->updateRepr();
2563             modmade = true;
2564         }
2565     }
2566     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2567                                    _("Star: Change randomization"));
2569     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2573 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2574                                        gchar const */*old_value*/, gchar const */*new_value*/,
2575                                        bool /*is_interactive*/, gpointer data)
2577     GtkWidget *tbl = GTK_WIDGET(data);
2579     // quit if run by the _changed callbacks
2580     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2581         return;
2582     }
2584     // in turn, prevent callbacks from responding
2585     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2587     GtkAdjustment *adj = 0;
2589     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2590     bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2592     if (!strcmp(name, "inkscape:randomized")) {
2593         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2594         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2595     } else if (!strcmp(name, "inkscape:rounded")) {
2596         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2597         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2598     } else if (!strcmp(name, "inkscape:flatsided")) {
2599         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2600         char const *flatsides = repr->attribute("inkscape:flatsided");
2601         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2602         if ( flatsides && !strcmp(flatsides,"false") ) {
2603             ege_select_one_action_set_active( flat_action, 1 );
2604             gtk_action_set_sensitive( prop_action, TRUE );
2605         } else {
2606             ege_select_one_action_set_active( flat_action, 0 );
2607             gtk_action_set_sensitive( prop_action, FALSE );
2608         }
2609     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2610         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2611         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2612         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2613         if (r2 < r1) {
2614             gtk_adjustment_set_value(adj, r2/r1);
2615         } else {
2616             gtk_adjustment_set_value(adj, r1/r2);
2617         }
2618     } else if (!strcmp(name, "sodipodi:sides")) {
2619         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2620         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2621     }
2623     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2627 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2629     NULL, /* child_added */
2630     NULL, /* child_removed */
2631     star_tb_event_attr_changed,
2632     NULL, /* content_changed */
2633     NULL  /* order_changed */
2634 };
2637 /**
2638  *  \param selection Should not be NULL.
2639  */
2640 static void
2641 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2643     int n_selected = 0;
2644     Inkscape::XML::Node *repr = NULL;
2646     purge_repr_listener( tbl, tbl );
2648     for (GSList const *items = selection->itemList();
2649          items != NULL;
2650          items = items->next)
2651     {
2652         if (SP_IS_STAR((SPItem *) items->data)) {
2653             n_selected++;
2654             repr = SP_OBJECT_REPR((SPItem *) items->data);
2655         }
2656     }
2658     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2660     if (n_selected == 0) {
2661         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2662     } else if (n_selected == 1) {
2663         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2665         if (repr) {
2666             g_object_set_data( tbl, "repr", repr );
2667             Inkscape::GC::anchor(repr);
2668             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2669             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2670         }
2671     } else {
2672         // FIXME: implement averaging of all parameters for multiple selected stars
2673         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2674         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2675     }
2679 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2681     // FIXME: in this and all other _default functions, set some flag telling the value_changed
2682     // callbacks to lump all the changes for all selected objects in one undo step
2684     GtkAdjustment *adj = 0;
2686     // fixme: make settable in prefs!
2687     gint mag = 5;
2688     gdouble prop = 0.5;
2689     gboolean flat = FALSE;
2690     gdouble randomized = 0;
2691     gdouble rounded = 0;
2693     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2694     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2696     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2697     gtk_action_set_sensitive( sb2, !flat );
2699     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2700     gtk_adjustment_set_value(adj, mag);
2701     gtk_adjustment_value_changed(adj);
2703     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2704     gtk_adjustment_set_value(adj, prop);
2705     gtk_adjustment_value_changed(adj);
2707     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2708     gtk_adjustment_set_value(adj, rounded);
2709     gtk_adjustment_value_changed(adj);
2711     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2712     gtk_adjustment_set_value(adj, randomized);
2713     gtk_adjustment_value_changed(adj);
2717 void
2718 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2720     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2721     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2722     GtkWidget *l = gtk_label_new(NULL);
2723     gtk_label_set_markup(GTK_LABEL(l), title);
2724     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2725     if ( GTK_IS_TOOLBAR(tbl) ) {
2726         gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2727     } else {
2728         gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2729     }
2730     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2734 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2736     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
2738     {
2739         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2740         ege_output_action_set_use_markup( act, TRUE );
2741         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2742         g_object_set_data( holder, "mode_action", act );
2743     }
2745     {
2746         EgeAdjustmentAction* eact = 0;
2747         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2748         bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2750         /* Flatsided checkbox */
2751         {
2752             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2754             GtkTreeIter iter;
2755             gtk_list_store_append( model, &iter );
2756             gtk_list_store_set( model, &iter,
2757                                 0, _("Polygon"),
2758                                 1, _("Regular polygon (with one handle) instead of a star"),
2759                                 2, INKSCAPE_ICON_DRAW_POLYGON,
2760                                 -1 );
2762             gtk_list_store_append( model, &iter );
2763             gtk_list_store_set( model, &iter,
2764                                 0, _("Star"),
2765                                 1, _("Star instead of a regular polygon (with one handle)"),
2766                                 2, INKSCAPE_ICON_DRAW_STAR,
2767                                 -1 );
2769             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2770             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2771             g_object_set_data( holder, "flat_action", act );
2773             ege_select_one_action_set_appearance( act, "full" );
2774             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2775             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2776             ege_select_one_action_set_icon_column( act, 2 );
2777             ege_select_one_action_set_icon_size( act, secondarySize );
2778             ege_select_one_action_set_tooltip_column( act, 1  );
2780             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2781             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2782         }
2784         /* Magnitude */
2785         {
2786         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2787         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2788         eact = create_adjustment_action( "MagnitudeAction",
2789                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2790                                          "/tools/shapes/star/magnitude", 3,
2791                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2792                                          3, 1024, 1, 5,
2793                                          labels, values, G_N_ELEMENTS(labels),
2794                                          sp_stb_magnitude_value_changed,
2795                                          1.0, 0 );
2796         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2797         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2798         }
2800         /* Spoke ratio */
2801         {
2802         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2803         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2804         eact = create_adjustment_action( "SpokeAction",
2805                                          _("Spoke ratio"), _("Spoke ratio:"),
2806                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2807                                          // Base radius is the same for the closest handle.
2808                                          _("Base radius to tip radius ratio"),
2809                                          "/tools/shapes/star/proportion", 0.5,
2810                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2811                                          0.01, 1.0, 0.01, 0.1,
2812                                          labels, values, G_N_ELEMENTS(labels),
2813                                          sp_stb_proportion_value_changed );
2814         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2815         g_object_set_data( holder, "prop_action", eact );
2816         }
2818         if ( !isFlatSided ) {
2819             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2820         } else {
2821             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2822         }
2824         /* Roundedness */
2825         {
2826         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2827         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2828         eact = create_adjustment_action( "RoundednessAction",
2829                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2830                                          "/tools/shapes/star/rounded", 0.0,
2831                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2832                                          -10.0, 10.0, 0.01, 0.1,
2833                                          labels, values, G_N_ELEMENTS(labels),
2834                                          sp_stb_rounded_value_changed );
2835         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2836         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2837         }
2839         /* Randomization */
2840         {
2841         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2842         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2843         eact = create_adjustment_action( "RandomizationAction",
2844                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2845                                          "/tools/shapes/star/randomized", 0.0,
2846                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2847                                          -10.0, 10.0, 0.001, 0.01,
2848                                          labels, values, G_N_ELEMENTS(labels),
2849                                          sp_stb_randomized_value_changed, 0.1, 3 );
2850         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2851         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2852         }
2853     }
2855     {
2856         /* Reset */
2857         {
2858             GtkAction* act = gtk_action_new( "StarResetAction",
2859                                              _("Defaults"),
2860                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2861                                              GTK_STOCK_CLEAR );
2862             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2863             gtk_action_group_add_action( mainActions, act );
2864             gtk_action_set_sensitive( act, TRUE );
2865         }
2866     }
2868     sigc::connection *connection = new sigc::connection(
2869         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2870         );
2871     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2872     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2876 //########################
2877 //##       Rect         ##
2878 //########################
2880 static void sp_rtb_sensitivize( GObject *tbl )
2882     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2883     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2884     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2886     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2887         gtk_action_set_sensitive( not_rounded, FALSE );
2888     } else {
2889         gtk_action_set_sensitive( not_rounded, TRUE );
2890     }
2894 static void
2895 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2896                           void (*setter)(SPRect *, gdouble))
2898     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2900     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2901     SPUnit const *unit = tracker->getActiveUnit();
2903     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2904         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2905         prefs->setDouble(Glib::ustring("/tools/shapes/rect/") + value_name, sp_units_get_pixels(adj->value, *unit));
2906     }
2908     // quit if run by the attr_changed listener
2909     if (g_object_get_data( tbl, "freeze" )) {
2910         return;
2911     }
2913     // in turn, prevent listener from responding
2914     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2916     bool modmade = false;
2917     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2918     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2919         if (SP_IS_RECT(items->data)) {
2920             if (adj->value != 0) {
2921                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2922             } else {
2923                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2924             }
2925             modmade = true;
2926         }
2927     }
2929     sp_rtb_sensitivize( tbl );
2931     if (modmade) {
2932         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2933                                    _("Change rectangle"));
2934     }
2936     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2939 static void
2940 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2942     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2945 static void
2946 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2948     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2951 static void
2952 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2954     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2957 static void
2958 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2960     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2965 static void
2966 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2968     GtkAdjustment *adj = 0;
2970     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2971     gtk_adjustment_set_value(adj, 0.0);
2972     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2973     gtk_adjustment_value_changed(adj);
2975     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2976     gtk_adjustment_set_value(adj, 0.0);
2977     gtk_adjustment_value_changed(adj);
2979     sp_rtb_sensitivize( obj );
2982 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2983                                        gchar const */*old_value*/, gchar const */*new_value*/,
2984                                        bool /*is_interactive*/, gpointer data)
2986     GObject *tbl = G_OBJECT(data);
2988     // quit if run by the _changed callbacks
2989     if (g_object_get_data( tbl, "freeze" )) {
2990         return;
2991     }
2993     // in turn, prevent callbacks from responding
2994     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2996     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2997     SPUnit const *unit = tracker->getActiveUnit();
2999     gpointer item = g_object_get_data( tbl, "item" );
3000     if (item && SP_IS_RECT(item)) {
3001         {
3002             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
3003             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
3004             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
3005         }
3007         {
3008             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
3009             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
3010             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
3011         }
3013         {
3014             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
3015             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
3016             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
3017         }
3019         {
3020             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
3021             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
3022             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
3023         }
3024     }
3026     sp_rtb_sensitivize( tbl );
3028     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3032 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
3033     NULL, /* child_added */
3034     NULL, /* child_removed */
3035     rect_tb_event_attr_changed,
3036     NULL, /* content_changed */
3037     NULL  /* order_changed */
3038 };
3040 /**
3041  *  \param selection should not be NULL.
3042  */
3043 static void
3044 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3046     int n_selected = 0;
3047     Inkscape::XML::Node *repr = NULL;
3048     SPItem *item = NULL;
3050     if ( g_object_get_data( tbl, "repr" ) ) {
3051         g_object_set_data( tbl, "item", NULL );
3052     }
3053     purge_repr_listener( tbl, tbl );
3055     for (GSList const *items = selection->itemList();
3056          items != NULL;
3057          items = items->next) {
3058         if (SP_IS_RECT((SPItem *) items->data)) {
3059             n_selected++;
3060             item = (SPItem *) items->data;
3061             repr = SP_OBJECT_REPR(item);
3062         }
3063     }
3065     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3067     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3069     if (n_selected == 0) {
3070         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3072         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3073         gtk_action_set_sensitive(w, FALSE);
3074         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3075         gtk_action_set_sensitive(h, FALSE);
3077     } else if (n_selected == 1) {
3078         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3079         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3081         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3082         gtk_action_set_sensitive(w, TRUE);
3083         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3084         gtk_action_set_sensitive(h, TRUE);
3086         if (repr) {
3087             g_object_set_data( tbl, "repr", repr );
3088             g_object_set_data( tbl, "item", item );
3089             Inkscape::GC::anchor(repr);
3090             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
3091             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
3092         }
3093     } else {
3094         // FIXME: implement averaging of all parameters for multiple selected
3095         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3096         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3097         sp_rtb_sensitivize( tbl );
3098     }
3102 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3104     EgeAdjustmentAction* eact = 0;
3105     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
3107     {
3108         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
3109         ege_output_action_set_use_markup( act, TRUE );
3110         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3111         g_object_set_data( holder, "mode_action", act );
3112     }
3114     // rx/ry units menu: create
3115     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
3116     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
3117     // fixme: add % meaning per cent of the width/height
3118     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
3119     g_object_set_data( holder, "tracker", tracker );
3121     /* W */
3122     {
3123         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3124         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3125         eact = create_adjustment_action( "RectWidthAction",
3126                                          _("Width"), _("W:"), _("Width of rectangle"),
3127                                          "/tools/shapes/rect/width", 0,
3128                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
3129                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3130                                          labels, values, G_N_ELEMENTS(labels),
3131                                          sp_rtb_width_value_changed );
3132         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3133         g_object_set_data( holder, "width_action", eact );
3134         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3135         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3136     }
3138     /* H */
3139     {
3140         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3141         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3142         eact = create_adjustment_action( "RectHeightAction",
3143                                          _("Height"), _("H:"), _("Height of rectangle"),
3144                                          "/tools/shapes/rect/height", 0,
3145                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3146                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3147                                          labels, values, G_N_ELEMENTS(labels),
3148                                          sp_rtb_height_value_changed );
3149         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3150         g_object_set_data( holder, "height_action", eact );
3151         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3152         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3153     }
3155     /* rx */
3156     {
3157         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3158         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3159         eact = create_adjustment_action( "RadiusXAction",
3160                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
3161                                          "/tools/shapes/rect/rx", 0,
3162                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3163                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3164                                          labels, values, G_N_ELEMENTS(labels),
3165                                          sp_rtb_rx_value_changed);
3166         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3167         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3168     }
3170     /* ry */
3171     {
3172         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3173         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3174         eact = create_adjustment_action( "RadiusYAction",
3175                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
3176                                          "/tools/shapes/rect/ry", 0,
3177                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3178                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3179                                          labels, values, G_N_ELEMENTS(labels),
3180                                          sp_rtb_ry_value_changed);
3181         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3182         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3183     }
3185     // add the units menu
3186     {
3187         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
3188         gtk_action_group_add_action( mainActions, act );
3189     }
3191     /* Reset */
3192     {
3193         InkAction* inky = ink_action_new( "RectResetAction",
3194                                           _("Not rounded"),
3195                                           _("Make corners sharp"),
3196                                           INKSCAPE_ICON_RECTANGLE_MAKE_CORNERS_SHARP,
3197                                           secondarySize );
3198         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
3199         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3200         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3201         g_object_set_data( holder, "not_rounded", inky );
3202     }
3204     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
3205     sp_rtb_sensitivize( holder );
3207     sigc::connection *connection = new sigc::connection(
3208         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
3209         );
3210     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3211     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3214 //########################
3215 //##       3D Box       ##
3216 //########################
3218 // normalize angle so that it lies in the interval [0,360]
3219 static double box3d_normalize_angle (double a) {
3220     double angle = a + ((int) (a/360.0))*360;
3221     if (angle < 0) {
3222         angle += 360.0;
3223     }
3224     return angle;
3227 static void
3228 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
3229                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
3230     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
3231     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
3232     //       are reset).
3233     bool is_infinite = !persp3d_VP_is_finite(persp->perspective_impl, axis);
3235     if (is_infinite) {
3236         gtk_toggle_action_set_active(tact, TRUE);
3237         gtk_action_set_sensitive(act, TRUE);
3239         double angle = persp3d_get_infinite_angle(persp, axis);
3240         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
3241             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
3242         }
3243     } else {
3244         gtk_toggle_action_set_active(tact, FALSE);
3245         gtk_action_set_sensitive(act, FALSE);
3246     }
3249 static void
3250 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
3251     if (!persp_repr) {
3252         g_print ("No perspective given to box3d_resync_toolbar().\n");
3253         return;
3254     }
3256     GtkWidget *tbl = GTK_WIDGET(data);
3257     GtkAdjustment *adj = 0;
3258     GtkAction *act = 0;
3259     GtkToggleAction *tact = 0;
3260     Persp3D *persp = persp3d_get_from_repr(persp_repr);
3261     if (!persp) {
3262         // Hmm, is it an error if this happens?
3263         return;
3264     }
3265     {
3266         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
3267         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
3268         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
3270         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
3271     }
3272     {
3273         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
3274         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
3275         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
3277         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
3278     }
3279     {
3280         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
3281         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
3282         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
3284         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
3285     }
3288 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3289                                                   gchar const */*old_value*/, gchar const */*new_value*/,
3290                                                   bool /*is_interactive*/, gpointer data)
3292     GtkWidget *tbl = GTK_WIDGET(data);
3294     // quit if run by the attr_changed or selection changed listener
3295     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3296         return;
3297     }
3299     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
3300     // sp_document_maybe_done() when the document is undo insensitive)
3301     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3303     // TODO: Only update the appropriate part of the toolbar
3304 //    if (!strcmp(name, "inkscape:vp_z")) {
3305         box3d_resync_toolbar(repr, G_OBJECT(tbl));
3306 //    }
3308     Persp3D *persp = persp3d_get_from_repr(repr);
3309     persp3d_update_box_reprs(persp);
3311     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3314 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
3316     NULL, /* child_added */
3317     NULL, /* child_removed */
3318     box3d_persp_tb_event_attr_changed,
3319     NULL, /* content_changed */
3320     NULL  /* order_changed */
3321 };
3323 /**
3324  *  \param selection Should not be NULL.
3325  */
3326 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
3327 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
3328 static void
3329 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3331     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
3332     // disable the angle entry fields for this direction (otherwise entering a value in them should only
3333     // update the perspectives with infinite VPs and leave the other ones untouched).
3335     Inkscape::XML::Node *persp_repr = NULL;
3336     purge_repr_listener(tbl, tbl);
3338     SPItem *item = selection->singleItem();
3339     if (item && SP_IS_BOX3D(item)) {
3340         // FIXME: Also deal with multiple selected boxes
3341         SPBox3D *box = SP_BOX3D(item);
3342         Persp3D *persp = box3d_get_perspective(box);
3343         persp_repr = SP_OBJECT_REPR(persp);
3344         if (persp_repr) {
3345             g_object_set_data(tbl, "repr", persp_repr);
3346             Inkscape::GC::anchor(persp_repr);
3347             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
3348             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
3349         }
3351         inkscape_active_document()->setCurrentPersp3D(persp3d_get_from_repr(persp_repr));
3352         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3353         prefs->setString("/tools/shapes/3dbox/persp", persp_repr->attribute("id"));
3355         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
3356         box3d_resync_toolbar(persp_repr, tbl);
3357         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
3358     }
3361 static void
3362 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
3364     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3365     SPDocument *document = sp_desktop_document(desktop);
3367     // quit if run by the attr_changed or selection changed listener
3368     if (g_object_get_data( dataKludge, "freeze" )) {
3369         return;
3370     }
3372     // in turn, prevent listener from responding
3373     g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
3375     std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
3376     if (sel_persps.empty()) {
3377         // this can happen when the document is created; we silently ignore it
3378         return;
3379     }
3380     Persp3D *persp = sel_persps.front();
3382     persp->perspective_impl->tmat.set_infinite_direction (axis, adj->value);
3383     SP_OBJECT(persp)->updateRepr();
3385     // TODO: use the correct axis here, too
3386     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
3388     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
3392 static void
3393 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3395     box3d_angle_value_changed(adj, dataKludge, Proj::X);
3398 static void
3399 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3401     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
3404 static void
3405 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3407     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
3411 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
3413     // TODO: Take all selected perspectives into account
3414     std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
3415     if (sel_persps.empty()) {
3416         // this can happen when the document is created; we silently ignore it
3417         return;
3418     }
3419     Persp3D *persp = sel_persps.front();
3421     bool set_infinite = gtk_toggle_action_get_active(act);
3422     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
3425 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3427     box3d_vp_state_changed(act, box3d_angle, Proj::X);
3430 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3432     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
3435 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3437     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
3440 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3442     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3443     EgeAdjustmentAction* eact = 0;
3444     SPDocument *document = sp_desktop_document (desktop);
3445     Persp3DImpl *persp_impl = document->getCurrentPersp3DImpl();
3447     EgeAdjustmentAction* box3d_angle_x = 0;
3448     EgeAdjustmentAction* box3d_angle_y = 0;
3449     EgeAdjustmentAction* box3d_angle_z = 0;
3451     /* Angle X */
3452     {
3453         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3454         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3455         eact = create_adjustment_action( "3DBoxAngleXAction",
3456                                          _("Angle in X direction"), _("Angle X:"),
3457                                          // Translators: PL is short for 'perspective line'
3458                                          _("Angle of PLs in X direction"),
3459                                          "/tools/shapes/3dbox/box3d_angle_x", 30,
3460                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
3461                                          -360.0, 360.0, 1.0, 10.0,
3462                                          labels, values, G_N_ELEMENTS(labels),
3463                                          box3d_angle_x_value_changed );
3464         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3465         g_object_set_data( holder, "box3d_angle_x_action", eact );
3466         box3d_angle_x = eact;
3467     }
3469     if (!persp_impl || !persp3d_VP_is_finite(persp_impl, Proj::X)) {
3470         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3471     } else {
3472         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3473     }
3476     /* VP X state */
3477     {
3478         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
3479                                                       // Translators: VP is short for 'vanishing point'
3480                                                       _("State of VP in X direction"),
3481                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
3482                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3483                                                       Inkscape::ICON_SIZE_DECORATION );
3484         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3485         g_object_set_data( holder, "box3d_vp_x_state_action", act );
3486         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
3487         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3488         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3489     }
3491     /* Angle Y */
3492     {
3493         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3494         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3495         eact = create_adjustment_action( "3DBoxAngleYAction",
3496                                          _("Angle in Y direction"), _("Angle Y:"),
3497                                          // Translators: PL is short for 'perspective line'
3498                                          _("Angle of PLs in Y direction"),
3499                                          "/tools/shapes/3dbox/box3d_angle_y", 30,
3500                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3501                                          -360.0, 360.0, 1.0, 10.0,
3502                                          labels, values, G_N_ELEMENTS(labels),
3503                                          box3d_angle_y_value_changed );
3504         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3505         g_object_set_data( holder, "box3d_angle_y_action", eact );
3506         box3d_angle_y = eact;
3507     }
3509     if (!persp_impl || !persp3d_VP_is_finite(persp_impl, Proj::Y)) {
3510         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3511     } else {
3512         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3513     }
3515     /* VP Y state */
3516     {
3517         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
3518                                                       // Translators: VP is short for 'vanishing point'
3519                                                       _("State of VP in Y direction"),
3520                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
3521                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3522                                                       Inkscape::ICON_SIZE_DECORATION );
3523         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3524         g_object_set_data( holder, "box3d_vp_y_state_action", act );
3525         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
3526         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3527         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3528     }
3530     /* Angle Z */
3531     {
3532         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3533         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3534         eact = create_adjustment_action( "3DBoxAngleZAction",
3535                                          _("Angle in Z direction"), _("Angle Z:"),
3536                                          // Translators: PL is short for 'perspective line'
3537                                          _("Angle of PLs in Z direction"),
3538                                          "/tools/shapes/3dbox/box3d_angle_z", 30,
3539                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3540                                          -360.0, 360.0, 1.0, 10.0,
3541                                          labels, values, G_N_ELEMENTS(labels),
3542                                          box3d_angle_z_value_changed );
3543         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3544         g_object_set_data( holder, "box3d_angle_z_action", eact );
3545         box3d_angle_z = eact;
3546     }
3548     if (!persp_impl || !persp3d_VP_is_finite(persp_impl, Proj::Z)) {
3549         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3550     } else {
3551         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3552     }
3554     /* VP Z state */
3555     {
3556         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3557                                                       // Translators: VP is short for 'vanishing point'
3558                                                       _("State of VP in Z direction"),
3559                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3560                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3561                                                       Inkscape::ICON_SIZE_DECORATION );
3562         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3563         g_object_set_data( holder, "box3d_vp_z_state_action", act );
3564         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3565         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3566         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3567     }
3569     sigc::connection *connection = new sigc::connection(
3570         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3571        );
3572     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3573     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3576 //########################
3577 //##       Spiral       ##
3578 //########################
3580 static void
3581 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, Glib::ustring const &value_name)
3583     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3585     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3586         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3587         prefs->setDouble("/tools/shapes/spiral/" + value_name, adj->value);
3588     }
3590     // quit if run by the attr_changed listener
3591     if (g_object_get_data( tbl, "freeze" )) {
3592         return;
3593     }
3595     // in turn, prevent listener from responding
3596     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3598     gchar* namespaced_name = g_strconcat("sodipodi:", value_name.data(), NULL);
3600     bool modmade = false;
3601     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3602          items != NULL;
3603          items = items->next)
3604     {
3605         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3606             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3607             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3608             SP_OBJECT((SPItem *) items->data)->updateRepr();
3609             modmade = true;
3610         }
3611     }
3613     g_free(namespaced_name);
3615     if (modmade) {
3616         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3617                                    _("Change spiral"));
3618     }
3620     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3623 static void
3624 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3626     sp_spl_tb_value_changed(adj, tbl, "revolution");
3629 static void
3630 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3632     sp_spl_tb_value_changed(adj, tbl, "expansion");
3635 static void
3636 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3638     sp_spl_tb_value_changed(adj, tbl, "t0");
3641 static void
3642 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3644     GtkWidget *tbl = GTK_WIDGET(obj);
3646     GtkAdjustment *adj;
3648     // fixme: make settable
3649     gdouble rev = 5;
3650     gdouble exp = 1.0;
3651     gdouble t0 = 0.0;
3653     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3654     gtk_adjustment_set_value(adj, rev);
3655     gtk_adjustment_value_changed(adj);
3657     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3658     gtk_adjustment_set_value(adj, exp);
3659     gtk_adjustment_value_changed(adj);
3661     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3662     gtk_adjustment_set_value(adj, t0);
3663     gtk_adjustment_value_changed(adj);
3665     spinbutton_defocus(GTK_OBJECT(tbl));
3669 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3670                                          gchar const */*old_value*/, gchar const */*new_value*/,
3671                                          bool /*is_interactive*/, gpointer data)
3673     GtkWidget *tbl = GTK_WIDGET(data);
3675     // quit if run by the _changed callbacks
3676     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3677         return;
3678     }
3680     // in turn, prevent callbacks from responding
3681     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3683     GtkAdjustment *adj;
3684     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3685     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3687     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3688     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3690     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3691     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3693     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3697 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3698     NULL, /* child_added */
3699     NULL, /* child_removed */
3700     spiral_tb_event_attr_changed,
3701     NULL, /* content_changed */
3702     NULL  /* order_changed */
3703 };
3705 static void
3706 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3708     int n_selected = 0;
3709     Inkscape::XML::Node *repr = NULL;
3711     purge_repr_listener( tbl, tbl );
3713     for (GSList const *items = selection->itemList();
3714          items != NULL;
3715          items = items->next)
3716     {
3717         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3718             n_selected++;
3719             repr = SP_OBJECT_REPR((SPItem *) items->data);
3720         }
3721     }
3723     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3725     if (n_selected == 0) {
3726         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3727     } else if (n_selected == 1) {
3728         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3730         if (repr) {
3731             g_object_set_data( tbl, "repr", repr );
3732             Inkscape::GC::anchor(repr);
3733             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3734             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3735         }
3736     } else {
3737         // FIXME: implement averaging of all parameters for multiple selected
3738         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3739         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3740     }
3744 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3746     EgeAdjustmentAction* eact = 0;
3747     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
3749     {
3750         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3751         ege_output_action_set_use_markup( act, TRUE );
3752         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3753         g_object_set_data( holder, "mode_action", act );
3754     }
3756     /* Revolution */
3757     {
3758         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3759         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3760         eact = create_adjustment_action( "SpiralRevolutionAction",
3761                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3762                                          "/tools/shapes/spiral/revolution", 3.0,
3763                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3764                                          0.01, 1024.0, 0.1, 1.0,
3765                                          labels, values, G_N_ELEMENTS(labels),
3766                                          sp_spl_tb_revolution_value_changed, 1, 2);
3767         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3768     }
3770     /* Expansion */
3771     {
3772         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3773         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3774         eact = create_adjustment_action( "SpiralExpansionAction",
3775                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3776                                          "/tools/shapes/spiral/expansion", 1.0,
3777                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3778                                          0.0, 1000.0, 0.01, 1.0,
3779                                          labels, values, G_N_ELEMENTS(labels),
3780                                          sp_spl_tb_expansion_value_changed);
3781         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3782     }
3784     /* T0 */
3785     {
3786         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3787         gdouble values[] = {0, 0.5, 0.9};
3788         eact = create_adjustment_action( "SpiralT0Action",
3789                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3790                                          "/tools/shapes/spiral/t0", 0.0,
3791                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3792                                          0.0, 0.999, 0.01, 1.0,
3793                                          labels, values, G_N_ELEMENTS(labels),
3794                                          sp_spl_tb_t0_value_changed);
3795         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3796     }
3798     /* Reset */
3799     {
3800         InkAction* inky = ink_action_new( "SpiralResetAction",
3801                                           _("Defaults"),
3802                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3803                                           GTK_STOCK_CLEAR,
3804                                           secondarySize );
3805         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3806         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3807     }
3810     sigc::connection *connection = new sigc::connection(
3811         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3812         );
3813     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3814     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3817 //########################
3818 //##     Pen/Pencil     ##
3819 //########################
3821 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3822 static Glib::ustring const
3823 freehand_tool_name(GObject *dataKludge)
3825     SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3826     return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3827              ? "/tools/freehand/pen"
3828              : "/tools/freehand/pencil" );
3831 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3833     gint mode = ege_select_one_action_get_active(act);
3835     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3836     prefs->setInt(freehand_tool_name(tbl) + "/freehand-mode", mode);
3838     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3840     // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3841     // preparatory work here
3842     if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3843         SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3844         sp_pen_context_set_polyline_mode(pc);
3845     }
3848 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3850     /* Freehand mode toggle buttons */
3851     {
3852         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3853         guint freehandMode = prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/freehand-mode" : "/tools/freehand/pen/freehand-mode" ), 0);
3854         Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
3856         {
3857             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3859             GtkTreeIter iter;
3860             gtk_list_store_append( model, &iter );
3861             gtk_list_store_set( model, &iter,
3862                                 0, _("Bezier"),
3863                                 1, _("Create regular Bezier path"),
3864                                 2, INKSCAPE_ICON_PATH_MODE_BEZIER,
3865                                 -1 );
3867             gtk_list_store_append( model, &iter );
3868             gtk_list_store_set( model, &iter,
3869                                 0, _("Spiro"),
3870                                 1, _("Create Spiro path"),
3871                                 2, INKSCAPE_ICON_PATH_MODE_SPIRO,
3872                                 -1 );
3874             if (!tool_is_pencil) {
3875                 gtk_list_store_append( model, &iter );
3876                 gtk_list_store_set( model, &iter,
3877                                     0, _("Zigzag"),
3878                                     1, _("Create a sequence of straight line segments"),
3879                                     2, INKSCAPE_ICON_PATH_MODE_POLYLINE,
3880                                     -1 );
3882                 gtk_list_store_append( model, &iter );
3883                 gtk_list_store_set( model, &iter,
3884                                     0, _("Paraxial"),
3885                                     1, _("Create a sequence of paraxial line segments"),
3886                                     2, INKSCAPE_ICON_PATH_MODE_POLYLINE_PARAXIAL,
3887                                     -1 );
3888             }
3890             EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3891                                                                 "FreehandModeActionPencil" :
3892                                                                 "FreehandModeActionPen",
3893                                                                 (_("Mode:")), (_("Mode of new lines drawn by this tool")), NULL, GTK_TREE_MODEL(model) );
3894             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3896             ege_select_one_action_set_appearance( act, "full" );
3897             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3898             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3899             ege_select_one_action_set_icon_column( act, 2 );
3900             ege_select_one_action_set_icon_size( act, secondarySize );
3901             ege_select_one_action_set_tooltip_column( act, 1  );
3903             ege_select_one_action_set_active( act, freehandMode);
3904             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3905         }
3906     }
3909 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3910     gint shape = ege_select_one_action_get_active( act );
3911     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3912     prefs->setInt(freehand_tool_name(dataKludge) + "/shape", shape);
3915 /**
3916  * \brief Generate the list of freehand advanced shape option entries.
3917  */
3918 GList * freehand_shape_dropdown_items_list() {
3919     GList *glist = NULL;
3921     glist = g_list_append (glist, _("None"));
3922     glist = g_list_append (glist, _("Triangle in"));
3923     glist = g_list_append (glist, _("Triangle out"));
3924     glist = g_list_append (glist, _("Ellipse"));
3925     glist = g_list_append (glist, _("From clipboard"));
3927     return glist;
3930 static void
3931 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3932     /*advanced shape options */
3933     {
3934         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3935         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3937         GList* items = 0;
3938         gint count = 0;
3939         for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3940         {
3941             GtkTreeIter iter;
3942             gtk_list_store_append( model, &iter );
3943             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3944             count++;
3945         }
3946         g_list_free( items );
3947         items = 0;
3948         EgeSelectOneAction* act1 = ege_select_one_action_new(
3949             tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3950             _("Shape:"), (_("Shape of new paths drawn by this tool")), NULL, GTK_TREE_MODEL(model));
3951         g_object_set( act1, "short_label", _("Shape:"), NULL );
3952         ege_select_one_action_set_appearance( act1, "compact" );
3953         ege_select_one_action_set_active( act1, prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/shape" : "/tools/freehand/pen/shape" ), 0) );
3954         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3955         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3956         g_object_set_data( holder, "shape_action", act1 );
3957     }
3960 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3962     sp_add_freehand_mode_toggle(mainActions, holder, false);
3963     freehand_add_advanced_shape_options(mainActions, holder, false);
3967 static void
3968 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3970     GtkWidget *tbl = GTK_WIDGET(obj);
3972     GtkAdjustment *adj;
3974     // fixme: make settable
3975     gdouble tolerance = 4;
3977     adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3978     gtk_adjustment_set_value(adj, tolerance);
3979     gtk_adjustment_value_changed(adj);
3981     spinbutton_defocus(GTK_OBJECT(tbl));
3984 static void
3985 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3987     // quit if run by the attr_changed listener
3988     if (g_object_get_data( tbl, "freeze" )) {
3989         return;
3990     }
3991     // in turn, prevent listener from responding
3992     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3993     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3994     prefs->setDouble("/tools/freehand/pencil/tolerance", adj->value);
3995     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3998 /*
3999 class PencilToleranceObserver : public Inkscape::Preferences::Observer {
4000 public:
4001     PencilToleranceObserver(Glib::ustring const &path, GObject *x) : Observer(path), _obj(x)
4002     {
4003         g_object_set_data(_obj, "prefobserver", this);
4004     }
4005     virtual ~PencilToleranceObserver() {
4006         if (g_object_get_data(_obj, "prefobserver") == this) {
4007             g_object_set_data(_obj, "prefobserver", NULL);
4008         }
4009     }
4010     virtual void notify(Inkscape::Preferences::Entry const &val) {
4011         GObject* tbl = _obj;
4012         if (g_object_get_data( tbl, "freeze" )) {
4013             return;
4014         }
4015         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4017         GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl, "tolerance");
4019         double v = val.getDouble(adj->value);
4020         gtk_adjustment_set_value(adj, v);
4021         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4022     }
4023 private:
4024     GObject *_obj;
4025 };
4026 */
4028 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4030     sp_add_freehand_mode_toggle(mainActions, holder, true);
4032     EgeAdjustmentAction* eact = 0;
4034     /* Tolerance */
4035     {
4036         gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
4037         gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
4038         eact = create_adjustment_action( "PencilToleranceAction",
4039                                          _("Smoothing:"), _("Smoothing: "),
4040                  _("How much smoothing (simplifying) is applied to the line"),
4041                                          "/tools/freehand/pencil/tolerance",
4042                                          3.0,
4043                                          GTK_WIDGET(desktop->canvas), NULL,
4044                                          holder, TRUE, "altx-pencil",
4045                                          1, 100.0, 0.5, 1.0,
4046                                          labels, values, G_N_ELEMENTS(labels),
4047                                          sp_pencil_tb_tolerance_value_changed,
4048                                          1, 2);
4049         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4050         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4051     }
4053     /* advanced shape options */
4054     freehand_add_advanced_shape_options(mainActions, holder, true);
4056     /* Reset */
4057     {
4058         InkAction* inky = ink_action_new( "PencilResetAction",
4059                                           _("Defaults"),
4060                                           _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
4061                                           GTK_STOCK_CLEAR,
4062                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4063         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
4064         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4065     }
4067     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4072 //########################
4073 //##       Tweak        ##
4074 //########################
4076 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4078     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4079     prefs->setDouble( "/tools/tweak/width", adj->value * 0.01 );
4082 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4084     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4085     prefs->setDouble( "/tools/tweak/force", adj->value * 0.01 );
4088 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4090     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4091     prefs->setBool("/tools/tweak/usepressure", gtk_toggle_action_get_active(act));
4094 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4096     int mode = ege_select_one_action_get_active( act );
4097     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4098     prefs->setInt("/tools/tweak/mode", mode);
4100     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
4101     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
4102     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
4103     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
4104     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
4105     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
4106     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
4107         if (doh) gtk_action_set_sensitive (doh, TRUE);
4108         if (dos) gtk_action_set_sensitive (dos, TRUE);
4109         if (dol) gtk_action_set_sensitive (dol, TRUE);
4110         if (doo) gtk_action_set_sensitive (doo, TRUE);
4111         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
4112         if (fid) gtk_action_set_sensitive (fid, FALSE);
4113     } else {
4114         if (doh) gtk_action_set_sensitive (doh, FALSE);
4115         if (dos) gtk_action_set_sensitive (dos, FALSE);
4116         if (dol) gtk_action_set_sensitive (dol, FALSE);
4117         if (doo) gtk_action_set_sensitive (doo, FALSE);
4118         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
4119         if (fid) gtk_action_set_sensitive (fid, TRUE);
4120     }
4123 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4125     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4126     prefs->setDouble( "/tools/tweak/fidelity", adj->value * 0.01 );
4129 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
4130     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4131     prefs->setBool("/tools/tweak/doh", gtk_toggle_action_get_active(act));
4133 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
4134     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4135     prefs->setBool("/tools/tweak/dos", gtk_toggle_action_get_active(act));
4137 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
4138     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4139     prefs->setBool("/tools/tweak/dol", gtk_toggle_action_get_active(act));
4141 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
4142     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4143     prefs->setBool("/tools/tweak/doo", gtk_toggle_action_get_active(act));
4146 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4148     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
4149     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4151     {
4152         /* Width */
4153         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
4154         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4155         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
4156                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
4157                                                               "/tools/tweak/width", 15,
4158                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
4159                                                               1, 100, 1.0, 10.0,
4160                                                               labels, values, G_N_ELEMENTS(labels),
4161                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
4162         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4163         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4164         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4165     }
4168     {
4169         /* Force */
4170         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
4171         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4172         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
4173                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
4174                                                               "/tools/tweak/force", 20,
4175                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
4176                                                               1, 100, 1.0, 10.0,
4177                                                               labels, values, G_N_ELEMENTS(labels),
4178                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
4179         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4180         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4181         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4182     }
4184     /* Mode */
4185     {
4186         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4188         GtkTreeIter iter;
4189         gtk_list_store_append( model, &iter );
4190         gtk_list_store_set( model, &iter,
4191                             0, _("Move mode"),
4192                             1, _("Move objects in any direction"),
4193                             2, INKSCAPE_ICON_OBJECT_TWEAK_PUSH,
4194                             -1 );
4196         gtk_list_store_append( model, &iter );
4197         gtk_list_store_set( model, &iter,
4198                             0, _("Move in/out mode"),
4199                             1, _("Move objects towards cursor; with Shift from cursor"),
4200                             2, INKSCAPE_ICON_OBJECT_TWEAK_ATTRACT,
4201                             -1 );
4203         gtk_list_store_append( model, &iter );
4204         gtk_list_store_set( model, &iter,
4205                             0, _("Move jitter mode"),
4206                             1, _("Move objects in random directions"),
4207                             2, INKSCAPE_ICON_OBJECT_TWEAK_RANDOMIZE,
4208                             -1 );
4210         gtk_list_store_append( model, &iter );
4211         gtk_list_store_set( model, &iter,
4212                             0, _("Scale mode"),
4213                             1, _("Shrink objects, with Shift enlarge"),
4214                             2, INKSCAPE_ICON_OBJECT_TWEAK_SHRINK,
4215                             -1 );
4217         gtk_list_store_append( model, &iter );
4218         gtk_list_store_set( model, &iter,
4219                             0, _("Rotate mode"),
4220                             1, _("Rotate objects, with Shift counterclockwise"),
4221                             2, INKSCAPE_ICON_OBJECT_TWEAK_ROTATE,
4222                             -1 );
4224         gtk_list_store_append( model, &iter );
4225         gtk_list_store_set( model, &iter,
4226                             0, _("Duplicate/delete mode"),
4227                             1, _("Duplicate objects, with Shift delete"),
4228                             2, INKSCAPE_ICON_OBJECT_TWEAK_DUPLICATE,
4229                             -1 );
4231         gtk_list_store_append( model, &iter );
4232         gtk_list_store_set( model, &iter,
4233                             0, _("Push mode"),
4234                             1, _("Push parts of paths in any direction"),
4235                             2, INKSCAPE_ICON_PATH_TWEAK_PUSH,
4236                             -1 );
4238         gtk_list_store_append( model, &iter );
4239         gtk_list_store_set( model, &iter,
4240                             0, _("Shrink/grow mode"),
4241                             1, _("Shrink (inset) parts of paths; with Shift grow (outset)"),
4242                             2, INKSCAPE_ICON_PATH_TWEAK_SHRINK,
4243                             -1 );
4245         gtk_list_store_append( model, &iter );
4246         gtk_list_store_set( model, &iter,
4247                             0, _("Attract/repel mode"),
4248                             1, _("Attract parts of paths towards cursor; with Shift from cursor"),
4249                             2, INKSCAPE_ICON_PATH_TWEAK_ATTRACT,
4250                             -1 );
4252         gtk_list_store_append( model, &iter );
4253         gtk_list_store_set( model, &iter,
4254                             0, _("Roughen mode"),
4255                             1, _("Roughen parts of paths"),
4256                             2, INKSCAPE_ICON_PATH_TWEAK_ROUGHEN,
4257                             -1 );
4259         gtk_list_store_append( model, &iter );
4260         gtk_list_store_set( model, &iter,
4261                             0, _("Color paint mode"),
4262                             1, _("Paint the tool's color upon selected objects"),
4263                             2, INKSCAPE_ICON_OBJECT_TWEAK_PAINT,
4264                             -1 );
4266         gtk_list_store_append( model, &iter );
4267         gtk_list_store_set( model, &iter,
4268                             0, _("Color jitter mode"),
4269                             1, _("Jitter the colors of selected objects"),
4270                             2, INKSCAPE_ICON_OBJECT_TWEAK_JITTER_COLOR,
4271                             -1 );
4273         gtk_list_store_append( model, &iter );
4274         gtk_list_store_set( model, &iter,
4275                             0, _("Blur mode"),
4276                             1, _("Blur selected objects more; with Shift, blur less"),
4277                             2, INKSCAPE_ICON_OBJECT_TWEAK_BLUR,
4278                             -1 );
4281         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4282         g_object_set( act, "short_label", _("Mode:"), NULL );
4283         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4284         g_object_set_data( holder, "mode_action", act );
4286         ege_select_one_action_set_appearance( act, "full" );
4287         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4288         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4289         ege_select_one_action_set_icon_column( act, 2 );
4290         ege_select_one_action_set_icon_size( act, secondarySize );
4291         ege_select_one_action_set_tooltip_column( act, 1  );
4293         gint mode = prefs->getInt("/tools/tweak/mode", 0);
4294         ege_select_one_action_set_active( act, mode );
4295         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
4297         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
4298     }
4300     guint mode = prefs->getInt("/tools/tweak/mode", 0);
4302     {
4303         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
4304         ege_output_action_set_use_markup( act, TRUE );
4305         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4306         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4307             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4308         g_object_set_data( holder, "tweak_channels_label", act);
4309     }
4311     {
4312         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
4313                                                       _("Hue"),
4314                                                       _("In color mode, act on objects' hue"),
4315                                                       NULL,
4316                                                       Inkscape::ICON_SIZE_DECORATION );
4317         //TRANSLATORS:  "H" here stands for hue
4318         g_object_set( act, "short_label", _("H"), NULL );
4319         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4320         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
4321         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doh", true) );
4322         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4323             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4324         g_object_set_data( holder, "tweak_doh", act);
4325     }
4326     {
4327         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
4328                                                       _("Saturation"),
4329                                                       _("In color mode, act on objects' saturation"),
4330                                                       NULL,
4331                                                       Inkscape::ICON_SIZE_DECORATION );
4332         //TRANSLATORS: "S" here stands for Saturation
4333         g_object_set( act, "short_label", _("S"), NULL );
4334         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4335         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
4336         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dos", true) );
4337         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4338             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4339         g_object_set_data( holder, "tweak_dos", act );
4340     }
4341     {
4342         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
4343                                                       _("Lightness"),
4344                                                       _("In color mode, act on objects' lightness"),
4345                                                       NULL,
4346                                                       Inkscape::ICON_SIZE_DECORATION );
4347         //TRANSLATORS: "L" here stands for Lightness
4348         g_object_set( act, "short_label", _("L"), NULL );
4349         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4350         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
4351         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dol", true) );
4352         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4353             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4354         g_object_set_data( holder, "tweak_dol", act );
4355     }
4356     {
4357         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
4358                                                       _("Opacity"),
4359                                                       _("In color mode, act on objects' opacity"),
4360                                                       NULL,
4361                                                       Inkscape::ICON_SIZE_DECORATION );
4362         //TRANSLATORS: "O" here stands for Opacity
4363         g_object_set( act, "short_label", _("O"), NULL );
4364         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4365         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
4366         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doo", true) );
4367         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4368             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4369         g_object_set_data( holder, "tweak_doo", act );
4370     }
4372     {   /* Fidelity */
4373         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
4374         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4375         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
4376                                                               _("Fidelity"), _("Fidelity:"),
4377                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
4378                                                               "/tools/tweak/fidelity", 50,
4379                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
4380                                                               1, 100, 1.0, 10.0,
4381                                                               labels, values, G_N_ELEMENTS(labels),
4382                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
4383         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4384         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4385         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
4386             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
4387         g_object_set_data( holder, "tweak_fidelity", eact );
4388     }
4391     /* Use Pressure button */
4392     {
4393         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
4394                                                       _("Pressure"),
4395                                                       _("Use the pressure of the input device to alter the force of tweak action"),
4396                                                       INKSCAPE_ICON_DRAW_USE_PRESSURE,
4397                                                       Inkscape::ICON_SIZE_DECORATION );
4398         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4399         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
4400         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/usepressure", true) );
4401     }
4406 //########################
4407 //##       Spray        ##
4408 //########################
4410 static void sp_spray_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4412     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4413     prefs->setDouble( "/tools/spray/width", adj->value );
4416 static void sp_spray_mean_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4418     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4419     prefs->setDouble( "/tools/spray/mean", adj->value );
4422 static void sp_spray_standard_deviation_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4424     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4425     prefs->setDouble( "/tools/spray/standard_deviation", adj->value );
4428 static void sp_spray_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4430     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4431     prefs->setBool("/tools/spray/usepressure", gtk_toggle_action_get_active(act));
4434 static void sp_spray_mode_changed( EgeSelectOneAction *act, GObject */*tbl*/ )
4436     int mode = ege_select_one_action_get_active( act );
4437     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4438     prefs->setInt("/tools/spray/mode", mode);
4441 static void sp_spray_population_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4443     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4444     prefs->setDouble( "/tools/spray/population", adj->value );
4447 static void sp_spray_rotation_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4449     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4450     prefs->setDouble( "/tools/spray/rotation_variation", adj->value );
4453 static void sp_spray_scale_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4455     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4456     prefs->setDouble( "/tools/spray/scale_variation", adj->value );
4460 static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4462     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
4463     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4465     {
4466         /* Width */
4467         gchar const* labels[] = {_("(narrow spray)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad spray)")};
4468         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4469         EgeAdjustmentAction *eact = create_adjustment_action( "SprayWidthAction",
4470                                                               _("Width"), _("Width:"), _("The width of the spray area (relative to the visible canvas area)"),
4471                                                               "/tools/spray/width", 15,
4472                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spray",
4473                                                               1, 100, 1.0, 10.0,
4474                                                               labels, values, G_N_ELEMENTS(labels),
4475                                                               sp_spray_width_value_changed,  1, 0 );
4476         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4477         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4478         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4479     }
4481     {
4482         /* Mean */
4483         gchar const* labels[] = {_("(minimum mean)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum mean)")};
4484         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4485         EgeAdjustmentAction *eact = create_adjustment_action( "SprayMeanAction",
4486                                                               _("Focus"), _("Focus:"), _("0 to spray a spot. Increase to enlarge the ring radius."),
4487                                                               "/tools/spray/mean", 0,
4488                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-mean",
4489                                                               0, 100, 1.0, 10.0,
4490                                                               labels, values, G_N_ELEMENTS(labels),
4491                                                               sp_spray_mean_value_changed,  1, 0 );
4492         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4493         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4494         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4495     }
4497     {
4498         /* Standard_deviation */
4499         gchar const* labels[] = {_("(minimum scatter)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum scatter)")};
4500         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4501         EgeAdjustmentAction *eact = create_adjustment_action( "SprayStandard_deviationAction",
4502                                                               _("Scatter"), _("Scatter:"), _("Increase to scatter sprayed objects."),
4503                                                               "/tools/spray/standard_deviation", 70,
4504                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-standard_deviation",
4505                                                               1, 100, 1.0, 10.0,
4506                                                               labels, values, G_N_ELEMENTS(labels),
4507                                                               sp_spray_standard_deviation_value_changed,  1, 0 );
4508         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4509         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4510         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4511     }
4513     /* Mode */
4514     {
4515         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4517         GtkTreeIter iter;
4518         gtk_list_store_append( model, &iter );
4519         gtk_list_store_set( model, &iter,
4520                             0, _("Spray with copies"),
4521                             1, _("Spray copies of the initial selection"),
4522                             2, INKSCAPE_ICON_SPRAY_COPY_MODE,
4523                             -1 );
4525         gtk_list_store_append( model, &iter );
4526         gtk_list_store_set( model, &iter,
4527                             0, _("Spray with clones"),
4528                             1, _("Spray clones of the initial selection"),
4529                             2, INKSCAPE_ICON_SPRAY_CLONE_MODE,
4530                             -1 );
4532         gtk_list_store_append( model, &iter );
4533         gtk_list_store_set( model, &iter,
4534                             0, _("Spray single path"),
4535                             1, _("Spray objects in a single path"),
4536                             2, INKSCAPE_ICON_SPRAY_UNION_MODE,
4537                             -1 );
4539         EgeSelectOneAction* act = ege_select_one_action_new( "SprayModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4540         g_object_set( act, "short_label", _("Mode:"), NULL );
4541         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4542         g_object_set_data( holder, "mode_action", act );
4544         ege_select_one_action_set_appearance( act, "full" );
4545         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4546         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4547         ege_select_one_action_set_icon_column( act, 2 );
4548         ege_select_one_action_set_icon_size( act, secondarySize );
4549         ege_select_one_action_set_tooltip_column( act, 1  );
4551         gint mode = prefs->getInt("/tools/spray/mode", 1);
4552         ege_select_one_action_set_active( act, mode );
4553         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_spray_mode_changed), holder );
4555         g_object_set_data( G_OBJECT(holder), "spray_tool_mode", act);
4556     }
4558     {   /* Population */
4559         gchar const* labels[] = {_("(low population)"), 0, 0, _("(default)"), 0, 0, _("(high population)")};
4560         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4561         EgeAdjustmentAction *eact = create_adjustment_action( "SprayPopulationAction",
4562                                                               _("Amount"), _("Amount:"),
4563                                                               _("Adjusts the number of items sprayed per clic."),
4564                                                               "/tools/spray/population", 70,
4565                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-population",
4566                                                               1, 100, 1.0, 10.0,
4567                                                               labels, values, G_N_ELEMENTS(labels),
4568                                                               sp_spray_population_value_changed,  1, 0 );
4569         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4570         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4571         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4572         g_object_set_data( holder, "spray_population", eact );
4573     }
4575     /* Use Pressure button */
4576     {
4577         InkToggleAction* act = ink_toggle_action_new( "SprayPressureAction",
4578                                                       _("Pressure"),
4579                                                       _("Use the pressure of the input device to alter the amount of sprayed objects."),
4580                                                       "use_pressure",
4581                                                       Inkscape::ICON_SIZE_DECORATION );
4582         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4583         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_spray_pressure_state_changed), NULL);
4584         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/spray/usepressure", true) );
4585     }
4587     {   /* Rotation */
4588         gchar const* labels[] = {_("(low rotation variation)"), 0, 0, _("(default)"), 0, 0, _("(high rotation variation)")};
4589         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4590         EgeAdjustmentAction *eact = create_adjustment_action( "SprayRotationAction",
4591                                                               _("Rotation"), _("Rotation:"),
4592                                                               // xgettext:no-c-format
4593                                                               _("Variation of the rotation of the sprayed objects. 0% for the same rotation than the original object."),
4594                                                               "/tools/spray/rotation_variation", 0,
4595                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-rotation",
4596                                                               0, 100, 1.0, 10.0,
4597                                                               labels, values, G_N_ELEMENTS(labels),
4598                                                               sp_spray_rotation_value_changed,  1, 0 );
4599         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4600         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4601         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4602         g_object_set_data( holder, "spray_rotation", eact );
4603     }
4605     {   /* Scale */
4606         gchar const* labels[] = {_("(low scale variation)"), 0, 0, _("(default)"), 0, 0, _("(high scale variation)")};
4607         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4608         EgeAdjustmentAction *eact = create_adjustment_action( "SprayScaleAction",
4609                                                               _("Scale"), _("Scale:"),
4610                                                               // xgettext:no-c-format
4611                                                               _("Variation in the scale of the sprayed objects. 0% for the same scale than the original object."),
4612                                                               "/tools/spray/scale_variation", 0,
4613                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-scale",
4614                                                               0, 100, 1.0, 10.0,
4615                                                               labels, values, G_N_ELEMENTS(labels),
4616                                                               sp_spray_scale_value_changed,  1, 0 );
4617         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4618         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4619         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4620         g_object_set_data( holder, "spray_scale", eact );
4621     }
4628 //########################
4629 //##     Calligraphy    ##
4630 //########################
4631 static void update_presets_list (GObject *tbl)
4633     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4634     if (g_object_get_data(tbl, "presets_blocked"))
4635         return;
4637     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4638     if (!sel) {
4639         // WTF!? This will cause a segfault if ever reached
4640         //ege_select_one_action_set_active(sel, 0);
4641         return;
4642     }
4644     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4646     int ege_index = 1;
4647     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++ege_index) {
4648         bool match = true;
4650         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(*i);
4651         for (std::vector<Inkscape::Preferences::Entry>::iterator j = preset.begin(); j != preset.end(); ++j) {
4652             Glib::ustring entry_name = j->getEntryName();
4653             if (entry_name == "id" || entry_name == "name") continue;
4655             void *widget = g_object_get_data(tbl, entry_name.data());
4656             if (widget) {
4657                 if (GTK_IS_ADJUSTMENT(widget)) {
4658                     double v = j->getDouble();
4659                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4660                     //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
4661                     if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
4662                         match = false;
4663                         break;
4664                     }
4665                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4666                     bool v = j->getBool();
4667                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4668                     //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
4669                     if ( static_cast<bool>(gtk_toggle_action_get_active(toggle)) != v ) {
4670                         match = false;
4671                         break;
4672                     }
4673                 }
4674             }
4675         }
4677         if (match) {
4678             // newly added item is at the same index as the
4679             // save command, so we need to change twice for it to take effect
4680             ege_select_one_action_set_active(sel, 0);
4681             ege_select_one_action_set_active(sel, ege_index); // one-based index
4682             return;
4683         }
4684     }
4686     // no match found
4687     ege_select_one_action_set_active(sel, 0);
4690 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
4692     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4693     prefs->setDouble( "/tools/calligraphic/mass", adj->value );
4694     update_presets_list(tbl);
4697 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
4699     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4700     prefs->setDouble( "/tools/calligraphic/wiggle", adj->value );
4701     update_presets_list(tbl);
4704 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
4706     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4707     prefs->setDouble( "/tools/calligraphic/angle", adj->value );
4708     update_presets_list(tbl);
4711 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
4713     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4714     prefs->setDouble( "/tools/calligraphic/width", adj->value );
4715     update_presets_list(tbl);
4718 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
4720     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4721     prefs->setDouble("/tools/calligraphic/thinning", adj->value );
4722     update_presets_list(tbl);
4725 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
4727     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4728     prefs->setDouble( "/tools/calligraphic/flatness", adj->value );
4729     update_presets_list(tbl);
4732 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
4734     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4735     prefs->setDouble( "/tools/calligraphic/tremor", adj->value );
4736     update_presets_list(tbl);
4739 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
4741     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4742     prefs->setDouble( "/tools/calligraphic/cap_rounding", adj->value );
4743     update_presets_list(tbl);
4746 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
4748     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4749     prefs->setBool("/tools/calligraphic/usepressure", gtk_toggle_action_get_active( act ));
4750     update_presets_list(tbl);
4753 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
4755     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4756     prefs->setBool("/tools/calligraphic/tracebackground", gtk_toggle_action_get_active( act ));
4757     update_presets_list(tbl);
4760 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
4762     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4763     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
4764     prefs->setBool("/tools/calligraphic/usetilt", gtk_toggle_action_get_active( act ));
4765     update_presets_list(tbl);
4766     if (calligraphy_angle )
4767         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
4771 static gchar const *const widget_names[] = {
4772     "width",
4773     "mass",
4774     "wiggle",
4775     "angle",
4776     "thinning",
4777     "tremor",
4778     "flatness",
4779     "cap_rounding",
4780     "usepressure",
4781     "tracebackground",
4782     "usetilt"
4783 };
4786 static void sp_dcc_build_presets_list(GObject *tbl)
4788     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4790     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4791     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4792     gtk_list_store_clear (model);
4794     {
4795         GtkTreeIter iter;
4796         gtk_list_store_append( model, &iter );
4797         gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4798     }
4800     // iterate over all presets to populate the list
4801     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4802     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4803     int ii=1;
4805     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i) {
4806         GtkTreeIter iter;
4807         Glib::ustring preset_name = prefs->getString(*i + "/name");
4808         gtk_list_store_append( model, &iter );
4809         gtk_list_store_set( model, &iter, 0, _(preset_name.data()), 1, ii++, -1 );
4810     }
4812     {
4813         GtkTreeIter iter;
4814         gtk_list_store_append( model, &iter );
4815         gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4816         g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4817     }
4819     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4821     update_presets_list (tbl);
4824 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4826     using Inkscape::UI::Dialog::CalligraphicProfileRename;
4827     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4828     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4829     if (! desktop) return;
4831     if (g_object_get_data(tbl, "presets_blocked"))
4832         return;
4834     CalligraphicProfileRename::show(desktop);
4835     if ( !CalligraphicProfileRename::applied()) {
4836         // dialog cancelled
4837         update_presets_list (tbl);
4838         return;
4839     }
4840     Glib::ustring profile_name = CalligraphicProfileRename::getProfileName();
4842     if (profile_name.empty()) {
4843         // empty name entered
4844         update_presets_list (tbl);
4845         return;
4846     }
4848     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4850     // If there's a preset with the given name, find it and set save_path appropriately
4851     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4852     int total_presets = presets.size();
4853     int new_index = -1;
4854     Glib::ustring save_path; // profile pref path without a trailing slash
4856     int temp_index = 0;
4857     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++temp_index) {
4858         Glib::ustring name = prefs->getString(*i + "/name");
4859         if (!name.empty() && profile_name == name) {
4860             new_index = temp_index;
4861             save_path = *i;
4862             break;
4863         }
4864     }
4866     if (new_index == -1) {
4867         // no preset with this name, create
4868         new_index = total_presets + 1;
4869         gchar *profile_id = g_strdup_printf("/dcc%d", new_index);
4870         save_path = Glib::ustring("/tools/calligraphic/preset") + profile_id;
4871         g_free(profile_id);
4872     }
4874     for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4875         gchar const *const widget_name = widget_names[i];
4876         void *widget = g_object_get_data(tbl, widget_name);
4877         if (widget) {
4878             if (GTK_IS_ADJUSTMENT(widget)) {
4879                 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4880                 prefs->setDouble(save_path + "/" + widget_name, gtk_adjustment_get_value(adj));
4881                 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4882             } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4883                 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4884                 prefs->setBool(save_path + "/" + widget_name, gtk_toggle_action_get_active(toggle));
4885                 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4886             } else {
4887                 g_warning("Unknown widget type for preset: %s\n", widget_name);
4888             }
4889         } else {
4890             g_warning("Bad key when writing preset: %s\n", widget_name);
4891         }
4892     }
4893     prefs->setString(save_path + "/name", profile_name);
4895     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4896     sp_dcc_build_presets_list (tbl);
4900 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4902     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4904     gint preset_index = ege_select_one_action_get_active( act );
4905     // This is necessary because EgeSelectOneAction spams us with GObject "changed" signal calls
4906     // even when the preset is not changed. It would be good to replace it with something more
4907     // modern. Index 0 means "No preset", so we don't do anything.
4908     if (preset_index == 0) return;
4910     gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4912     if (preset_index == save_presets_index) {
4913         // this is the Save command
4914         sp_dcc_save_profile(NULL, tbl);
4915         return;
4916     }
4918     if (g_object_get_data(tbl, "presets_blocked"))
4919         return;
4921     // preset_index is one-based so we subtract 1
4922     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4923     Glib::ustring preset_path = presets.at(preset_index - 1);
4925     if (!preset_path.empty()) {
4926         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
4928         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(preset_path);
4930         // Shouldn't this be std::map?
4931         for (std::vector<Inkscape::Preferences::Entry>::iterator i = preset.begin(); i != preset.end(); ++i) {
4932             Glib::ustring entry_name = i->getEntryName();
4933             if (entry_name == "id" || entry_name == "name") continue;
4934             void *widget = g_object_get_data(tbl, entry_name.data());
4935             if (widget) {
4936                 if (GTK_IS_ADJUSTMENT(widget)) {
4937                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4938                     gtk_adjustment_set_value(adj, i->getDouble());
4939                     //std::cout << "set adj " << attr_name << " to " << v << "\n";
4940                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4941                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4942                     gtk_toggle_action_set_active(toggle, i->getBool());
4943                     //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4944                 } else {
4945                     g_warning("Unknown widget type for preset: %s\n", entry_name.data());
4946                 }
4947             } else {
4948                 g_warning("Bad key found in a preset record: %s\n", entry_name.data());
4949             }
4950         }
4951         g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4952     }
4956 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4958     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4959     {
4960         g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4962         EgeAdjustmentAction* calligraphy_angle = 0;
4964         {
4965         /* Width */
4966         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4967         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4968         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4969                                                               _("Pen Width"), _("Width:"),
4970                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
4971                                                               "/tools/calligraphic/width", 15,
4972                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4973                                                               1, 100, 1.0, 10.0,
4974                                                               labels, values, G_N_ELEMENTS(labels),
4975                                                               sp_ddc_width_value_changed,  1, 0 );
4976         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4977         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4978         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4979         }
4981         {
4982         /* Thinning */
4983             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4984             gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4985         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4986                                                               _("Stroke Thinning"), _("Thinning:"),
4987                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4988                                                               "/tools/calligraphic/thinning", 10,
4989                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4990                                                               -100, 100, 1, 10.0,
4991                                                               labels, values, G_N_ELEMENTS(labels),
4992                                                               sp_ddc_velthin_value_changed, 1, 0);
4993         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4994         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4995         }
4997         {
4998         /* Angle */
4999         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
5000         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
5001         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
5002                                                               _("Pen Angle"), _("Angle:"),
5003                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
5004                                                               "/tools/calligraphic/angle", 30,
5005                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
5006                                                               -90.0, 90.0, 1.0, 10.0,
5007                                                               labels, values, G_N_ELEMENTS(labels),
5008                                                               sp_ddc_angle_value_changed, 1, 0 );
5009         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5010         g_object_set_data( holder, "angle_action", eact );
5011         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5012         calligraphy_angle = eact;
5013         }
5015         {
5016         /* Fixation */
5017             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
5018         gdouble values[] = {0, 20, 40, 60, 90, 100};
5019         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
5020                                                               _("Fixation"), _("Fixation:"),
5021                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"),
5022                                                               "/tools/calligraphic/flatness", 90,
5023                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5024                                                               0.0, 100, 1.0, 10.0,
5025                                                               labels, values, G_N_ELEMENTS(labels),
5026                                                               sp_ddc_flatness_value_changed, 1, 0);
5027         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5028         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5029         }
5031         {
5032         /* Cap Rounding */
5033             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
5034         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
5035         // TRANSLATORS: "cap" means "end" (both start and finish) here
5036         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
5037                                                               _("Cap rounding"), _("Caps:"),
5038                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
5039                                                               "/tools/calligraphic/cap_rounding", 0.0,
5040                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5041                                                               0.0, 5.0, 0.01, 0.1,
5042                                                               labels, values, G_N_ELEMENTS(labels),
5043                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
5044         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5045         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5046         }
5048         {
5049         /* Tremor */
5050             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
5051         gdouble values[] = {0, 10, 20, 40, 60, 100};
5052         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
5053                                                               _("Stroke Tremor"), _("Tremor:"),
5054                                                               _("Increase to make strokes rugged and trembling"),
5055                                                               "/tools/calligraphic/tremor", 0.0,
5056                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5057                                                               0.0, 100, 1, 10.0,
5058                                                               labels, values, G_N_ELEMENTS(labels),
5059                                                               sp_ddc_tremor_value_changed, 1, 0);
5061         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5062         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5063         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5064         }
5066         {
5067         /* Wiggle */
5068         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
5069         gdouble values[] = {0, 20, 40, 60, 100};
5070         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
5071                                                               _("Pen Wiggle"), _("Wiggle:"),
5072                                                               _("Increase to make the pen waver and wiggle"),
5073                                                               "/tools/calligraphic/wiggle", 0.0,
5074                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5075                                                               0.0, 100, 1, 10.0,
5076                                                               labels, values, G_N_ELEMENTS(labels),
5077                                                               sp_ddc_wiggle_value_changed, 1, 0);
5078         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5079         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5080         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5081         }
5083         {
5084         /* Mass */
5085             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
5086         gdouble values[] = {0.0, 2, 10, 20, 50, 100};
5087         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
5088                                                               _("Pen Mass"), _("Mass:"),
5089                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
5090                                                               "/tools/calligraphic/mass", 2.0,
5091                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5092                                                               0.0, 100, 1, 10.0,
5093                                                               labels, values, G_N_ELEMENTS(labels),
5094                                                               sp_ddc_mass_value_changed, 1, 0);
5095         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5096         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5097         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5098         }
5101         /* Trace Background button */
5102         {
5103             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
5104                                                           _("Trace Background"),
5105                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
5106                                                           INKSCAPE_ICON_DRAW_TRACE_BACKGROUND,
5107                                                           Inkscape::ICON_SIZE_DECORATION );
5108             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5109             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
5110             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/tracebackground", false) );
5111             g_object_set_data( holder, "tracebackground", act );
5112         }
5114         /* Use Pressure button */
5115         {
5116             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
5117                                                           _("Pressure"),
5118                                                           _("Use the pressure of the input device to alter the width of the pen"),
5119                                                           INKSCAPE_ICON_DRAW_USE_PRESSURE,
5120                                                           Inkscape::ICON_SIZE_DECORATION );
5121             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5122             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
5123             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usepressure", true) );
5124             g_object_set_data( holder, "usepressure", act );
5125         }
5127         /* Use Tilt button */
5128         {
5129             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
5130                                                           _("Tilt"),
5131                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
5132                                                           INKSCAPE_ICON_DRAW_USE_TILT,
5133                                                           Inkscape::ICON_SIZE_DECORATION );
5134             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5135             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
5136             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs->getBool("/tools/calligraphic/usetilt", true) );
5137             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usetilt", true) );
5138             g_object_set_data( holder, "usetilt", act );
5139         }
5141         /*calligraphic profile */
5142         {
5143             GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5144             EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
5145             ege_select_one_action_set_appearance (act1, "compact");
5146             g_object_set_data (holder, "profile_selector", act1 );
5148             g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
5150             sp_dcc_build_presets_list (holder);
5152             g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
5153             gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
5154         }
5155     }
5159 //########################
5160 //##    Circle / Arc    ##
5161 //########################
5163 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
5165     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
5166     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
5168     if (v1 == 0 && v2 == 0) {
5169         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
5170             gtk_action_set_sensitive( ocb, FALSE );
5171             gtk_action_set_sensitive( make_whole, FALSE );
5172         }
5173     } else {
5174         gtk_action_set_sensitive( ocb, TRUE );
5175         gtk_action_set_sensitive( make_whole, TRUE );
5176     }
5179 static void
5180 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
5182     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5184     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5185         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5186         prefs->setDouble(Glib::ustring("/tools/shapes/arc/") + value_name, adj->value);
5187     }
5189     // quit if run by the attr_changed listener
5190     if (g_object_get_data( tbl, "freeze" )) {
5191         return;
5192     }
5194     // in turn, prevent listener from responding
5195     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5197     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
5199     bool modmade = false;
5200     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5201          items != NULL;
5202          items = items->next)
5203     {
5204         SPItem *item = SP_ITEM(items->data);
5206         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
5208             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
5209             SPArc *arc = SP_ARC(item);
5211             if (!strcmp(value_name, "start"))
5212                 ge->start = (adj->value * M_PI)/ 180;
5213             else
5214                 ge->end = (adj->value * M_PI)/ 180;
5216             sp_genericellipse_normalize(ge);
5217             ((SPObject *)arc)->updateRepr();
5218             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
5220             modmade = true;
5221         }
5222     }
5224     g_free(namespaced_name);
5226     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
5228     sp_arctb_sensitivize( tbl, adj->value, other->value );
5230     if (modmade) {
5231         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
5232                                    _("Arc: Change start/end"));
5233     }
5235     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5239 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
5241     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
5244 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
5246     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
5250 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
5252     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5253     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5254         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5255         prefs->setBool("/tools/shapes/arc/open", ege_select_one_action_get_active(act) != 0);
5256     }
5258     // quit if run by the attr_changed listener
5259     if (g_object_get_data( tbl, "freeze" )) {
5260         return;
5261     }
5263     // in turn, prevent listener from responding
5264     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5266     bool modmade = false;
5268     if ( ege_select_one_action_get_active(act) != 0 ) {
5269         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5270              items != NULL;
5271              items = items->next)
5272         {
5273             if (SP_IS_ARC((SPItem *) items->data)) {
5274                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5275                 repr->setAttribute("sodipodi:open", "true");
5276                 SP_OBJECT((SPItem *) items->data)->updateRepr();
5277                 modmade = true;
5278             }
5279         }
5280     } else {
5281         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5282              items != NULL;
5283              items = items->next)
5284         {
5285             if (SP_IS_ARC((SPItem *) items->data))    {
5286                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5287                 repr->setAttribute("sodipodi:open", NULL);
5288                 SP_OBJECT((SPItem *) items->data)->updateRepr();
5289                 modmade = true;
5290             }
5291         }
5292     }
5294     if (modmade) {
5295         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
5296                                    _("Arc: Change open/closed"));
5297     }
5299     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5302 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
5304     GtkAdjustment *adj;
5305     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
5306     gtk_adjustment_set_value(adj, 0.0);
5307     gtk_adjustment_value_changed(adj);
5309     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
5310     gtk_adjustment_set_value(adj, 0.0);
5311     gtk_adjustment_value_changed(adj);
5313     spinbutton_defocus( GTK_OBJECT(obj) );
5316 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
5317                                       gchar const */*old_value*/, gchar const */*new_value*/,
5318                                       bool /*is_interactive*/, gpointer data)
5320     GObject *tbl = G_OBJECT(data);
5322     // quit if run by the _changed callbacks
5323     if (g_object_get_data( tbl, "freeze" )) {
5324         return;
5325     }
5327     // in turn, prevent callbacks from responding
5328     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5330     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
5331     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
5333     GtkAdjustment *adj1,*adj2;
5334     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
5335     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
5336     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
5337     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
5339     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
5341     char const *openstr = NULL;
5342     openstr = repr->attribute("sodipodi:open");
5343     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
5345     if (openstr) {
5346         ege_select_one_action_set_active( ocb, 1 );
5347     } else {
5348         ege_select_one_action_set_active( ocb, 0 );
5349     }
5351     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5354 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
5355     NULL, /* child_added */
5356     NULL, /* child_removed */
5357     arc_tb_event_attr_changed,
5358     NULL, /* content_changed */
5359     NULL  /* order_changed */
5360 };
5363 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
5365     int n_selected = 0;
5366     Inkscape::XML::Node *repr = NULL;
5368     purge_repr_listener( tbl, tbl );
5370     for (GSList const *items = selection->itemList();
5371          items != NULL;
5372          items = items->next)
5373     {
5374         if (SP_IS_ARC((SPItem *) items->data)) {
5375             n_selected++;
5376             repr = SP_OBJECT_REPR((SPItem *) items->data);
5377         }
5378     }
5380     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
5382     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
5383     if (n_selected == 0) {
5384         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
5385     } else if (n_selected == 1) {
5386         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
5387         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5389         if (repr) {
5390             g_object_set_data( tbl, "repr", repr );
5391             Inkscape::GC::anchor(repr);
5392             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
5393             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
5394         }
5395     } else {
5396         // FIXME: implement averaging of all parameters for multiple selected
5397         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
5398         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5399         sp_arctb_sensitivize( tbl, 1, 0 );
5400     }
5404 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5406     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5408     EgeAdjustmentAction* eact = 0;
5409     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
5412     {
5413         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
5414         ege_output_action_set_use_markup( act, TRUE );
5415         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5416         g_object_set_data( holder, "mode_action", act );
5417     }
5419     /* Start */
5420     {
5421         eact = create_adjustment_action( "ArcStartAction",
5422                                          _("Start"), _("Start:"),
5423                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
5424                                          "/tools/shapes/arc/start", 0.0,
5425                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
5426                                          -360.0, 360.0, 1.0, 10.0,
5427                                          0, 0, 0,
5428                                          sp_arctb_start_value_changed);
5429         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5430     }
5432     /* End */
5433     {
5434         eact = create_adjustment_action( "ArcEndAction",
5435                                          _("End"), _("End:"),
5436                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
5437                                          "/tools/shapes/arc/end", 0.0,
5438                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
5439                                          -360.0, 360.0, 1.0, 10.0,
5440                                          0, 0, 0,
5441                                          sp_arctb_end_value_changed);
5442         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5443     }
5445     /* Segments / Pie checkbox */
5446     {
5447         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5449         GtkTreeIter iter;
5450         gtk_list_store_append( model, &iter );
5451         gtk_list_store_set( model, &iter,
5452                             0, _("Closed arc"),
5453                             1, _("Switch to segment (closed shape with two radii)"),
5454                             2, INKSCAPE_ICON_DRAW_ELLIPSE_SEGMENT,
5455                             -1 );
5457         gtk_list_store_append( model, &iter );
5458         gtk_list_store_set( model, &iter,
5459                             0, _("Open Arc"),
5460                             1, _("Switch to arc (unclosed shape)"),
5461                             2, INKSCAPE_ICON_DRAW_ELLIPSE_ARC,
5462                             -1 );
5464         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5465         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5466         g_object_set_data( holder, "open_action", act );
5468         ege_select_one_action_set_appearance( act, "full" );
5469         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5470         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5471         ege_select_one_action_set_icon_column( act, 2 );
5472         ege_select_one_action_set_icon_size( act, secondarySize );
5473         ege_select_one_action_set_tooltip_column( act, 1  );
5475         bool isClosed = !prefs->getBool("/tools/shapes/arc/open", false);
5476         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
5477         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
5478     }
5480     /* Make Whole */
5481     {
5482         InkAction* inky = ink_action_new( "ArcResetAction",
5483                                           _("Make whole"),
5484                                           _("Make the shape a whole ellipse, not arc or segment"),
5485                                           INKSCAPE_ICON_DRAW_ELLIPSE_WHOLE,
5486                                           secondarySize );
5487         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
5488         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5489         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
5490         g_object_set_data( holder, "make_whole", inky );
5491     }
5493     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
5494     // sensitivize make whole and open checkbox
5495     {
5496         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
5497         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
5498         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
5499     }
5502     sigc::connection *connection = new sigc::connection(
5503         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
5504         );
5505     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
5506     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
5512 // toggle button callbacks and updaters
5514 //########################
5515 //##      Dropper       ##
5516 //########################
5518 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
5519     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5520     prefs->setInt( "/tools/dropper/pick", gtk_toggle_action_get_active( act ) );
5521     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
5522     if ( set_action ) {
5523         if ( gtk_toggle_action_get_active( act ) ) {
5524             gtk_action_set_sensitive( set_action, TRUE );
5525         } else {
5526             gtk_action_set_sensitive( set_action, FALSE );
5527         }
5528     }
5530     spinbutton_defocus(GTK_OBJECT(tbl));
5533 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
5534     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5535     prefs->setBool( "/tools/dropper/setalpha", gtk_toggle_action_get_active( act ) );
5536     spinbutton_defocus(GTK_OBJECT(tbl));
5540 /**
5541  * Dropper auxiliary toolbar construction and setup.
5542  *
5543  * TODO: Would like to add swatch of current color.
5544  * TODO: Add queue of last 5 or so colors selected with new swatches so that
5545  *       can drag and drop places. Will provide a nice mixing palette.
5546  */
5547 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
5549     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5550     gint pickAlpha = prefs->getInt( "/tools/dropper/pick", 1 );
5552     {
5553         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
5554         ege_output_action_set_use_markup( act, TRUE );
5555         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5556     }
5558     {
5559         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
5560                                                       _("Pick opacity"),
5561                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
5562                                                       NULL,
5563                                                       Inkscape::ICON_SIZE_DECORATION );
5564         g_object_set( act, "short_label", _("Pick"), NULL );
5565         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5566         g_object_set_data( holder, "pick_action", act );
5567         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
5568         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
5569     }
5571     {
5572         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
5573                                                       _("Assign opacity"),
5574                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
5575                                                       NULL,
5576                                                       Inkscape::ICON_SIZE_DECORATION );
5577         g_object_set( act, "short_label", _("Assign"), NULL );
5578         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5579         g_object_set_data( holder, "set_action", act );
5580         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/dropper/setalpha", true) );
5581         // make sure it's disabled if we're not picking alpha
5582         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
5583         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
5584     }
5588 //########################
5589 //##      LPETool       ##
5590 //########################
5592 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
5594 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
5595 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
5597     using namespace Inkscape::LivePathEffect;
5599     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
5600     SPEventContext *ec = desktop->event_context;
5601     if (!SP_IS_LPETOOL_CONTEXT(ec)) {
5602         return;
5603     }
5605     // only take action if run by the attr_changed listener
5606     if (!g_object_get_data(tbl, "freeze")) {
5607         // in turn, prevent listener from responding
5608         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5610         gint mode = ege_select_one_action_get_active(act);
5611         EffectType type = lpesubtools[mode].type;
5613         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5614         bool success = lpetool_try_construction(lc, type);
5615         if (success) {
5616             // since the construction was already performed, we set the state back to inactive
5617             ege_select_one_action_set_active(act, 0);
5618             mode = 0;
5619         } else {
5620             // switch to the chosen subtool
5621             SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
5622         }
5624         if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5625             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5626             prefs->setInt( "/tools/lpetool/mode", mode );
5627         }
5629         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
5630     }
5633 void sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject */*tbl*/)
5635     SPEventContext *ec = selection->desktop()->event_context;
5636     if (!SP_IS_LPETOOL_CONTEXT(ec))
5637         return;
5639     lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
5642 void
5643 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
5645     using namespace Inkscape::LivePathEffect;
5646     SPEventContext *ec = selection->desktop()->event_context;
5647     if (!SP_IS_LPETOOL_CONTEXT(ec))
5648         return;
5649     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
5651     lpetool_delete_measuring_items(lc);
5652     lpetool_create_measuring_items(lc, selection);
5654     // activate line segment combo box if a single item with LPELineSegment is selected
5655     GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
5656     SPItem *item = selection->singleItem();
5657     if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
5658         SPLPEItem *lpeitem = SP_LPE_ITEM(item);
5659         Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
5660         if (lpe && lpe->effectType() == LINE_SEGMENT) {
5661             LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
5662             g_object_set_data(tbl, "currentlpe", lpe);
5663             g_object_set_data(tbl, "currentlpeitem", lpeitem);
5664             gtk_action_set_sensitive(w, TRUE);
5665             ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
5666         } else {
5667             g_object_set_data(tbl, "currentlpe", NULL);
5668             g_object_set_data(tbl, "currentlpeitem", NULL);
5669             gtk_action_set_sensitive(w, FALSE);
5670         }
5671     } else {
5672         g_object_set_data(tbl, "currentlpe", NULL);
5673         g_object_set_data(tbl, "currentlpeitem", NULL);
5674         gtk_action_set_sensitive(w, FALSE);
5675     }
5678 static void
5679 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
5680     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5681     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5683     bool show = gtk_toggle_action_get_active( act );
5684     prefs->setBool("/tools/lpetool/show_bbox",  show);
5686     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5687         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5688         lpetool_context_reset_limiting_bbox(lc);
5689     }
5692 static void
5693 lpetool_toggle_show_measuring_info (GtkToggleAction *act, GObject *tbl) {
5694     SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
5695     if (!tools_isactive(desktop, TOOLS_LPETOOL))
5696         return;
5698     GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
5699     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5700     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5701         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5702         bool show = gtk_toggle_action_get_active( act );
5703         prefs->setBool("/tools/lpetool/show_measuring_info",  show);
5704         lpetool_show_measuring_info(lc, show);
5705         gtk_action_set_sensitive(GTK_ACTION(unitact), show);
5706     }
5709 static void lpetool_unit_changed(GtkAction* /*act*/, GObject* tbl) {
5710     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
5711     SPUnit const *unit = tracker->getActiveUnit();
5712     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5713     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5715     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5716     if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
5717         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5718         lpetool_delete_measuring_items(lc);
5719         lpetool_create_measuring_items(lc);
5720     }
5723 static void
5724 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
5725     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5726     Inkscape::Selection *selection = desktop->selection;
5728     Geom::OptRect bbox = selection->bounds();
5730     if (bbox) {
5731         Geom::Point A(bbox->min());
5732         Geom::Point B(bbox->max());
5734         A *= desktop->doc2dt();
5735         B *= desktop->doc2dt();
5737         // TODO: should we provide a way to store points in prefs?
5738         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5739         prefs->setDouble("/tools/lpetool/bbox_upperleftx", A[Geom::X]);
5740         prefs->setDouble("/tools/lpetool/bbox_upperlefty", A[Geom::Y]);
5741         prefs->setDouble("/tools/lpetool/bbox_lowerrightx", B[Geom::X]);
5742         prefs->setDouble("/tools/lpetool/bbox_lowerrighty", B[Geom::Y]);
5744         lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
5745     }
5747     gtk_toggle_action_set_active(act, false);
5750 static void
5751 sp_line_segment_build_list(GObject *tbl)
5753     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
5755     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
5756     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
5757     gtk_list_store_clear (model);
5759     // TODO: we add the entries of rht combo box manually; later this should be done automatically
5760     {
5761         GtkTreeIter iter;
5762         gtk_list_store_append( model, &iter );
5763         gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
5764         gtk_list_store_append( model, &iter );
5765         gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
5766         gtk_list_store_append( model, &iter );
5767         gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
5768         gtk_list_store_append( model, &iter );
5769         gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
5770     }
5772     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5775 static void
5776 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
5777     using namespace Inkscape::LivePathEffect;
5779     // quit if run by the attr_changed listener
5780     if (g_object_get_data(tbl, "freeze")) {
5781         return;
5782     }
5784     // in turn, prevent listener from responding
5785     g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5787     LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
5788     SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5789     if (lpeitem) {
5790         SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5791         lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
5792         sp_lpe_item_update_patheffect(lpeitem, true, true);
5793     }
5795     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5798 static void
5799 lpetool_open_lpe_dialog (GtkToggleAction *act, gpointer data) {
5800     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5802     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5803         sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5804     }
5805     gtk_toggle_action_set_active(act, false);
5808 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5810     UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5811     tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5812     g_object_set_data(holder, "tracker", tracker);
5813     SPUnit const *unit = tracker->getActiveUnit();
5815     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5816     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5818     /** Automatically create a list of LPEs that get added to the toolbar **/
5819     {
5820         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5822         GtkTreeIter iter;
5824         // the first toggle button represents the state that no subtool is active (remove this when
5825         // this can be modeled by EgeSelectOneAction or some other action)
5826         gtk_list_store_append( model, &iter );
5827         gtk_list_store_set( model, &iter,
5828                             0, _("All inactive"),
5829                             1, _("No geometric tool is active"),
5830                             2, "draw-geometry-inactive",
5831                             -1 );
5833         Inkscape::LivePathEffect::EffectType type;
5834         for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5835             type =  lpesubtools[i].type;
5836             gtk_list_store_append( model, &iter );
5837             gtk_list_store_set( model, &iter,
5838                                 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5839                                 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5840                                 2, lpesubtools[i].icon_name,
5841                                 -1 );
5842         }
5844         EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5845         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5846         g_object_set_data( holder, "lpetool_mode_action", act );
5848         ege_select_one_action_set_appearance( act, "full" );
5849         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5850         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5851         ege_select_one_action_set_icon_column( act, 2 );
5852         ege_select_one_action_set_tooltip_column( act, 1  );
5854         gint lpeToolMode = prefs->getInt("/tools/lpetool/mode", 0);
5855         ege_select_one_action_set_active( act, lpeToolMode );
5856         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5857     }
5859     /* Show limiting bounding box */
5860     {
5861         InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5862                                                       _("Show limiting bounding box"),
5863                                                       _("Show bounding box (used to cut infinite lines)"),
5864                                                       "show-bounding-box",
5865                                                       Inkscape::ICON_SIZE_DECORATION );
5866         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5867         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5868         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_bbox", true ) );
5869     }
5871     /* Set limiting bounding box to bbox of current selection */
5872     {
5873         InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5874                                                       _("Get limiting bounding box from selection"),
5875                                                       _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5876                                                       "draw-geometry-set-bounding-box",
5877                                                       Inkscape::ICON_SIZE_DECORATION );
5878         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5879         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5880         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5881     }
5884     /* Combo box to choose line segment type */
5885     {
5886         GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5887         EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5888         ege_select_one_action_set_appearance (act, "compact");
5889         g_object_set_data (holder, "lpetool_line_segment_action", act );
5891         g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5893         sp_line_segment_build_list (holder);
5895         g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5896         gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5897         gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5898     }
5900     /* Display measuring info for selected items */
5901     {
5902         InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5903                                                       _("Display measuring info"),
5904                                                       _("Display measuring info for selected items"),
5905                                                       "draw-geometry-show-measuring-info",
5906                                                       Inkscape::ICON_SIZE_DECORATION );
5907         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5908         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
5909         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_measuring_info", true ) );
5910     }
5912     // add the units menu
5913     {
5914         GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5915         gtk_action_group_add_action( mainActions, act );
5916         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5917         g_object_set_data(holder, "lpetool_units_action", act);
5918         gtk_action_set_sensitive(act, prefs->getBool("/tools/lpetool/show_measuring_info", true));
5919     }
5921     /* Open LPE dialog (to adapt parameters numerically) */
5922     {
5923         InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
5924                                                       _("Open LPE dialog"),
5925                                                       _("Open LPE dialog (to adapt parameters numerically)"),
5926                                                       "dialog-geometry",
5927                                                       Inkscape::ICON_SIZE_DECORATION );
5928         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5929         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
5930         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5931     }
5933     //watch selection
5934     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5936     sigc::connection *c_selection_modified =
5937         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5938                               (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
5939     pool->add_connection ("selection-modified", c_selection_modified);
5941     sigc::connection *c_selection_changed =
5942         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5943                               (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5944     pool->add_connection ("selection-changed", c_selection_changed);
5947 //########################
5948 //##       Eraser       ##
5949 //########################
5951 static void sp_erc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
5953     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5954     prefs->setDouble( "/tools/eraser/width", adj->value );
5955     update_presets_list(tbl);
5958 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5960     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5961     bool eraserMode = ege_select_one_action_get_active( act ) != 0;
5962     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5963         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5964         prefs->setBool( "/tools/eraser/mode", eraserMode );
5965     }
5967     // only take action if run by the attr_changed listener
5968     if (!g_object_get_data( tbl, "freeze" )) {
5969         // in turn, prevent listener from responding
5970         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5972         if ( eraserMode != 0 ) {
5973         } else {
5974         }
5975         // TODO finish implementation
5977         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5978     }
5981 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5983     {
5984         /* Width */
5985         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5986         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5987         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5988                                                               _("Pen Width"), _("Width:"),
5989                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
5990                                                               "/tools/eraser/width", 15,
5991                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5992                                                               1, 100, 1.0, 10.0,
5993                                                               labels, values, G_N_ELEMENTS(labels),
5994                                                               sp_erc_width_value_changed, 1, 0);
5995         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5996         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5997         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5998     }
6000     {
6001         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
6003         GtkTreeIter iter;
6004         gtk_list_store_append( model, &iter );
6005         gtk_list_store_set( model, &iter,
6006                             0, _("Delete"),
6007                             1, _("Delete objects touched by the eraser"),
6008                             2, INKSCAPE_ICON_DRAW_ERASER_DELETE_OBJECTS,
6009                             -1 );
6011         gtk_list_store_append( model, &iter );
6012         gtk_list_store_set( model, &iter,
6013                             0, _("Cut"),
6014                             1, _("Cut out from objects"),
6015                             2, INKSCAPE_ICON_PATH_DIFFERENCE,
6016                             -1 );
6018         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
6019         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
6020         g_object_set_data( holder, "eraser_mode_action", act );
6022         ege_select_one_action_set_appearance( act, "full" );
6023         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
6024         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
6025         ege_select_one_action_set_icon_column( act, 2 );
6026         ege_select_one_action_set_tooltip_column( act, 1  );
6028         /// @todo Convert to boolean?
6029         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6030         gint eraserMode = prefs->getBool("/tools/eraser/mode") ? 1 : 0;
6031         ege_select_one_action_set_active( act, eraserMode );
6032         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
6033     }
6037 //########################
6038 //##    Text Toolbox    ##
6039 //########################
6040 /*
6041 static void
6042 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
6044     //Call back for letter sizing spinbutton
6047 static void
6048 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
6050     //Call back for line height spinbutton
6053 static void
6054 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
6056     //Call back for horizontal kerning spinbutton
6059 static void
6060 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
6062     //Call back for vertical kerning spinbutton
6065 static void
6066 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
6068     //Call back for letter rotation spinbutton
6069 }*/
6071 namespace {
6073 void
6074 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
6076     // quit if run by the _changed callbacks
6077     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6078         return;
6079     }
6081     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6083     SPStyle *query =
6084         sp_style_new (SP_ACTIVE_DOCUMENT);
6086     int result_family =
6087         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6089     int result_style =
6090         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6092     int result_numbers =
6093         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6095     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6097     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6098     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
6099         // there are no texts in selection, read from prefs
6101         sp_style_read_from_prefs(query, "/tools/text");
6103         if (g_object_get_data(tbl, "text_style_from_prefs")) {
6104             // do not reset the toolbar style from prefs if we already did it last time
6105             sp_style_unref(query);
6106             g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6107             return;
6108         }
6109         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
6110     } else {
6111         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
6112     }
6114     if (query->text)
6115     {
6116         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
6117             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6118             gtk_entry_set_text (GTK_ENTRY (entry), "");
6120         } else if (query->text->font_specification.value || query->text->font_family.value) {
6122             Gtk::ComboBoxEntry *combo = (Gtk::ComboBoxEntry *) (g_object_get_data (G_OBJECT (tbl), "family-entry-combo"));
6123             GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6125             // Get the font that corresponds
6126             Glib::ustring familyName;
6128             font_instance * font = font_factory::Default()->FaceFromStyle(query);
6129             if (font) {
6130                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
6131                 font->Unref();
6132                 font = NULL;
6133             }
6135             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
6137             Gtk::TreeIter iter;
6138             try {
6139                 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
6140                 Glib::RefPtr<Gtk::TreeModel> model = combo->get_model();
6141                 iter = model->get_iter(path);
6142             } catch (...) {
6143                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
6144                 sp_style_unref(query);
6145                 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6146                 return;
6147             }
6149             combo->set_active (iter);
6150         }
6152         //Size
6153         {
6154             GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
6155             gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
6156             gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
6157             g_free(str);
6158         }
6160         //Anchor
6161         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
6162         {
6163             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
6164             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6165             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6166             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6167         }
6168         else
6169         {
6170             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
6171             {
6172                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
6173                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6174                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6175                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6176             }
6177             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
6178             {
6179                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
6180                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6181                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6182                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6183             }
6184             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
6185             {
6186                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
6187                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6188                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6189                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6190             }
6191         }
6193         //Style
6194         {
6195             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
6197             gboolean active = gtk_toggle_button_get_active (button);
6198             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));
6200             if (active != check)
6201             {
6202                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6203                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6204                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6205             }
6206         }
6208         {
6209             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
6211             gboolean active = gtk_toggle_button_get_active (button);
6212             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
6214             if (active != check)
6215             {
6216                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6217                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6218                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6219             }
6220         }
6222         //Orientation
6223         //locking both buttons, changing one affect all group (both)
6224         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
6225         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6227         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
6228         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
6230         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
6231         {
6232             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6233         }
6234         else
6235         {
6236             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
6237         }
6238         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6239         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
6240     }
6242     sp_style_unref(query);
6244     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6247 void
6248 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
6250     sp_text_toolbox_selection_changed (selection, tbl);
6253 void
6254 sp_text_toolbox_subselection_changed (gpointer /*tc*/, GObject *tbl)
6256     sp_text_toolbox_selection_changed (NULL, tbl);
6259 void
6260 sp_text_toolbox_family_changed (GtkComboBoxEntry    *,
6261                                 GObject             *tbl)
6263     // quit if run by the _changed callbacks
6264     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6265         return;
6266     }
6268     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6270     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
6271     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6272     const gchar* family = gtk_entry_get_text (GTK_ENTRY (entry));
6274     //g_print ("family changed to: %s\n", family);
6276     SPStyle *query =
6277         sp_style_new (SP_ACTIVE_DOCUMENT);
6279     int result_fontspec =
6280         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6282     SPCSSAttr *css = sp_repr_css_attr_new ();
6284     // First try to get the font spec from the stored value
6285     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
6287     if (fontSpec.empty()) {
6288         // Construct a new font specification if it does not yet exist
6289         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6290         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6291         fontFromStyle->Unref();
6292     }
6294     if (!fontSpec.empty()) {
6296         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
6298         if (!newFontSpec.empty()) {
6300             if (fontSpec != newFontSpec) {
6302                 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
6304                 if (font) {
6305                     sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6307                     // Set all the these just in case they were altered when finding the best
6308                     // match for the new family and old style...
6310                     gchar c[256];
6312                     font->Family(c, 256);
6314                     sp_repr_css_set_property (css, "font-family", c);
6316                     font->Attribute( "weight", c, 256);
6317                     sp_repr_css_set_property (css, "font-weight", c);
6319                     font->Attribute("style", c, 256);
6320                     sp_repr_css_set_property (css, "font-style", c);
6322                     font->Attribute("stretch", c, 256);
6323                     sp_repr_css_set_property (css, "font-stretch", c);
6325                     font->Attribute("variant", c, 256);
6326                     sp_repr_css_set_property (css, "font-variant", c);
6328                     font->Unref();
6329                 }
6330             }
6332         } else {
6333             // If the old font on selection (or default) was not existing on the system,
6334             // ReplaceFontSpecificationFamily does not work. In that case we fall back to blindly
6335             // setting the family reported by the family chooser.
6337             //g_print ("fallback setting family: %s\n", family);
6338             sp_repr_css_set_property (css, "-inkscape-font-specification", family);
6339             sp_repr_css_set_property (css, "font-family", family);
6340         }
6341     }
6343     // If querying returned nothing, set the default style of the tool (for new texts)
6344     if (result_fontspec == QUERY_STYLE_NOTHING)
6345     {
6346         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6347         prefs->mergeStyle("/tools/text/style", css);
6348         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
6349     }
6350     else
6351     {
6352         sp_desktop_set_style (desktop, css, true, true);
6353     }
6355     sp_style_unref(query);
6357     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6358                                    _("Text: Change font family"));
6359     sp_repr_css_attr_unref (css);
6361     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6363     // unfreeze
6364     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6366     // focus to canvas
6367     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6371 void
6372 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
6373                                    gpointer          data)
6375     if (g_object_get_data (G_OBJECT (button), "block")) return;
6376     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
6377     int prop = GPOINTER_TO_INT(data);
6379     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6381     // move the x of all texts to preserve the same bbox
6382     Inkscape::Selection *selection = sp_desktop_selection(desktop);
6383     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
6384         if (SP_IS_TEXT((SPItem *) items->data)) {
6385             SPItem *item = SP_ITEM(items->data);
6387             unsigned writing_mode = SP_OBJECT_STYLE(item)->writing_mode.value;
6388             // below, variable names suggest horizontal move, but we check the writing direction
6389             // and move in the corresponding axis
6390             int axis;
6391             if (writing_mode == SP_CSS_WRITING_MODE_LR_TB || writing_mode == SP_CSS_WRITING_MODE_RL_TB) {
6392                 axis = NR::X;
6393             } else {
6394                 axis = NR::Y;
6395             }
6397             Geom::OptRect bbox
6398                   = item->getBounds(Geom::identity(), SPItem::GEOMETRIC_BBOX);
6399             if (!bbox)
6400                 continue;
6401             double width = bbox->dimensions()[axis];
6402             // If you want to align within some frame, other than the text's own bbox, calculate
6403             // the left and right (or top and bottom for tb text) slacks of the text inside that
6404             // frame (currently unused)
6405             double left_slack = 0;
6406             double right_slack = 0;
6407             unsigned old_align = SP_OBJECT_STYLE(item)->text_align.value;
6408             double move = 0;
6409             if (old_align == SP_CSS_TEXT_ALIGN_START || old_align == SP_CSS_TEXT_ALIGN_LEFT) {
6410                 switch (prop) {
6411                     case 0:
6412                         move = -left_slack;
6413                         break;
6414                     case 1:
6415                         move = width/2 + (right_slack - left_slack)/2;
6416                         break;
6417                     case 2:
6418                         move = width + right_slack;
6419                         break;
6420                 }
6421             } else if (old_align == SP_CSS_TEXT_ALIGN_CENTER) {
6422                 switch (prop) {
6423                     case 0:
6424                         move = -width/2 - left_slack;
6425                         break;
6426                     case 1:
6427                         move = (right_slack - left_slack)/2;
6428                         break;
6429                     case 2:
6430                         move = width/2 + right_slack;
6431                         break;
6432                 }
6433             } else if (old_align == SP_CSS_TEXT_ALIGN_END || old_align == SP_CSS_TEXT_ALIGN_RIGHT) {
6434                 switch (prop) {
6435                     case 0:
6436                         move = -width - left_slack;
6437                         break;
6438                     case 1:
6439                         move = -width/2 + (right_slack - left_slack)/2;
6440                         break;
6441                     case 2:
6442                         move = right_slack;
6443                         break;
6444                 }
6445             }
6446             Geom::Point XY = SP_TEXT(item)->attributes.firstXY();
6447             if (axis == NR::X) {
6448                 XY = XY + Geom::Point (move, 0);
6449             } else {
6450                 XY = XY + Geom::Point (0, move);
6451             }
6452             SP_TEXT(item)->attributes.setFirstXY(XY);
6453             SP_OBJECT(item)->updateRepr();
6454             SP_OBJECT(item)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
6455         }
6456     }
6458     SPCSSAttr *css = sp_repr_css_attr_new ();
6459     switch (prop)
6460     {
6461         case 0:
6462         {
6463             sp_repr_css_set_property (css, "text-anchor", "start");
6464             sp_repr_css_set_property (css, "text-align", "start");
6465             break;
6466         }
6467         case 1:
6468         {
6469             sp_repr_css_set_property (css, "text-anchor", "middle");
6470             sp_repr_css_set_property (css, "text-align", "center");
6471             break;
6472         }
6474         case 2:
6475         {
6476             sp_repr_css_set_property (css, "text-anchor", "end");
6477             sp_repr_css_set_property (css, "text-align", "end");
6478             break;
6479         }
6481         case 3:
6482         {
6483             sp_repr_css_set_property (css, "text-anchor", "start");
6484             sp_repr_css_set_property (css, "text-align", "justify");
6485             break;
6486         }
6487     }
6489     SPStyle *query =
6490         sp_style_new (SP_ACTIVE_DOCUMENT);
6491     int result_numbers =
6492         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6494     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6495     if (result_numbers == QUERY_STYLE_NOTHING)
6496     {
6497         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6498         prefs->mergeStyle("/tools/text/style", css);
6499     }
6501     sp_style_unref(query);
6503     sp_desktop_set_style (desktop, css, true, true);
6504     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6505                                    _("Text: Change alignment"));
6506     sp_repr_css_attr_unref (css);
6508     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6511 void
6512 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
6513                                gpointer          data)
6515     if (g_object_get_data (G_OBJECT (button), "block")) return;
6517     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
6518     SPCSSAttr   *css        = sp_repr_css_attr_new ();
6519     int          prop       = GPOINTER_TO_INT(data);
6520     bool         active     = gtk_toggle_button_get_active (button);
6522     SPStyle *query =
6523         sp_style_new (SP_ACTIVE_DOCUMENT);
6525     int result_fontspec =
6526         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6528     //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6529     //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6530     //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6532     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
6533     Glib::ustring newFontSpec = "";
6535     if (fontSpec.empty()) {
6536         // Construct a new font specification if it does not yet exist
6537         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6538         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6539         fontFromStyle->Unref();
6540     }
6542     bool nochange = true;
6543     switch (prop)
6544     {
6545         case 0:
6546         {
6547             if (!fontSpec.empty()) {
6548                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
6549                 if (!newFontSpec.empty()) {
6550                     // Don't even set the bold if the font didn't exist on the system
6551                     sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
6552                     nochange = false;
6553                 }
6554             }
6555             // set or reset the button according
6556             if(nochange) {
6557                 gboolean check = gtk_toggle_button_get_active (button);
6559                 if (active != check)
6560                 {
6561                     g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6562                     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6563                     g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6564                 }
6565             }
6567             break;
6568         }
6570         case 1:
6571         {
6572             if (!fontSpec.empty()) {
6573                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
6574                 if (!newFontSpec.empty()) {
6575                     // Don't even set the italic if the font didn't exist on the system
6576                     sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
6577                     nochange = false;
6578                 }
6579             }
6580             if(nochange) {
6581                 gboolean check = gtk_toggle_button_get_active (button);
6583                 if (active != check)
6584                 {
6585                     g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6586                     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6587                     g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6588                 }
6589             }
6590             break;
6591         }
6592     }
6594     if (!newFontSpec.empty()) {
6595         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6596     }
6598     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6599     if (result_fontspec == QUERY_STYLE_NOTHING)
6600     {
6601         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6602         prefs->mergeStyle("/tools/text/style", css);
6603     }
6605     sp_style_unref(query);
6607     sp_desktop_set_style (desktop, css, true, true);
6608     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6609                                    _("Text: Change font style"));
6610     sp_repr_css_attr_unref (css);
6612     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6615 void
6616 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
6617                                      gpointer         data)
6619     if (g_object_get_data (G_OBJECT (button), "block")) {
6620         return;
6621     }
6623     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
6624     SPCSSAttr   *css        = sp_repr_css_attr_new ();
6625     int          prop       = GPOINTER_TO_INT(data);
6627     switch (prop)
6628     {
6629         case 0:
6630         {
6631             sp_repr_css_set_property (css, "writing-mode", "lr");
6632             break;
6633         }
6635         case 1:
6636         {
6637             sp_repr_css_set_property (css, "writing-mode", "tb");
6638             break;
6639         }
6640     }
6642     SPStyle *query =
6643         sp_style_new (SP_ACTIVE_DOCUMENT);
6644     int result_numbers =
6645         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6647     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6648     if (result_numbers == QUERY_STYLE_NOTHING)
6649     {
6650         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6651         prefs->mergeStyle("/tools/text/style", css);
6652     }
6654     sp_desktop_set_style (desktop, css, true, true);
6655     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6656                                    _("Text: Change orientation"));
6657     sp_repr_css_attr_unref (css);
6659     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6662 gboolean
6663 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6665     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6666     if (!desktop) return FALSE;
6668     switch (get_group0_keyval (event)) {
6669         case GDK_KP_Enter: // chosen
6670         case GDK_Return:
6671             // unfreeze and update, which will defocus
6672             g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6673             sp_text_toolbox_family_changed (NULL, tbl);
6674             return TRUE; // I consumed the event
6675             break;
6676         case GDK_Escape:
6677             // defocus
6678             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6679             return TRUE; // I consumed the event
6680             break;
6681     }
6682     return FALSE;
6685 gboolean
6686 sp_text_toolbox_family_list_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject */*tbl*/)
6688     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6689     if (!desktop) return FALSE;
6691     switch (get_group0_keyval (event)) {
6692         case GDK_KP_Enter:
6693         case GDK_Return:
6694         case GDK_Escape: // defocus
6695             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6696             return TRUE; // I consumed the event
6697             break;
6698         case GDK_w:
6699         case GDK_W:
6700             if (event->state & GDK_CONTROL_MASK) {
6701                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6702                 return TRUE; // I consumed the event
6703             }
6704             break;
6705     }
6706     return FALSE;
6710 void
6711 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
6712                                GObject     *tbl)
6714      // quit if run by the _changed callbacks
6715     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6716         return;
6717     }
6719     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6721    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6723     // If this is not from selecting a size in the list (in which case get_active will give the
6724     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
6725     // process this event. This fixes GTK's stupid insistence on sending an activate change every
6726     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
6727    if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed")) {
6728         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6729         return;
6730    }
6732     gdouble value = -1;
6733     {
6734         gchar *endptr;
6735         gchar *const text = gtk_combo_box_get_active_text(cbox);
6736         if (text) {
6737             value = g_strtod(text, &endptr);
6738             if (endptr == text) {  // Conversion failed, non-numeric input.
6739                 value = -1;
6740             }
6741             g_free(text);
6742         }
6743     }
6744     if (value <= 0) {
6745         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6746         return; // could not parse value
6747     }
6749     SPCSSAttr *css = sp_repr_css_attr_new ();
6750     Inkscape::CSSOStringStream osfs;
6751     osfs << value;
6752     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
6754     SPStyle *query =
6755         sp_style_new (SP_ACTIVE_DOCUMENT);
6756     int result_numbers =
6757         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6759     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6760     if (result_numbers == QUERY_STYLE_NOTHING)
6761     {
6762         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6763         prefs->mergeStyle("/tools/text/style", css);
6764     }
6766     sp_style_unref(query);
6768     sp_desktop_set_style (desktop, css, true, true);
6769     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
6770                                    _("Text: Change font size"));
6771     sp_repr_css_attr_unref (css);
6773     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6775     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6778 gboolean
6779 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
6781     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6782     if (!desktop) return FALSE;
6784     if (!g_object_get_data (tbl, "esc-pressed")) {
6785         g_object_set_data (tbl, "enter-pressed", gpointer(1));
6786         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6787         sp_text_toolbox_size_changed (cbox, tbl);
6788         g_object_set_data (tbl, "enter-pressed", gpointer(0));
6789     }
6790     return FALSE; // I consumed the event
6794 gboolean
6795 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6797     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6798     if (!desktop) return FALSE;
6800     switch (get_group0_keyval (event)) {
6801         case GDK_Escape: // defocus
6802             g_object_set_data (tbl, "esc-pressed", gpointer(1));
6803             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6804             g_object_set_data (tbl, "esc-pressed", gpointer(0));
6805             return TRUE; // I consumed the event
6806             break;
6807         case GDK_Return: // defocus
6808         case GDK_KP_Enter:
6809             g_object_set_data (tbl, "enter-pressed", gpointer(1));
6810             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6811             sp_text_toolbox_size_changed (cbox, tbl);
6812             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6813             g_object_set_data (tbl, "enter-pressed", gpointer(0));
6814             return TRUE; // I consumed the event
6815             break;
6816     }
6817     return FALSE;
6820 // While editing font name in the entry, disable family_changed by freezing, otherwise completion
6821 // does not work!
6822 gboolean
6823 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
6824                                  GdkEventFocus    */*event*/,
6825                                  GObject          *tbl)
6827     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6828     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1); // select all
6829     return FALSE;
6832 gboolean
6833 sp_text_toolbox_entry_focus_out  (GtkWidget        *entry,
6834                                  GdkEventFocus    */*event*/,
6835                                  GObject          *tbl)
6837     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6838     gtk_entry_select_region (GTK_ENTRY (entry), 0, 0); // deselect
6839     return FALSE;
6842 void
6843 cell_data_func  (GtkCellLayout */*cell_layout*/,
6844                  GtkCellRenderer   *cell,
6845                  GtkTreeModel      *tree_model,
6846                  GtkTreeIter       *iter,
6847                  gpointer           /*data*/)
6849     gchar *family;
6850     gtk_tree_model_get(tree_model, iter, 0, &family, -1);
6851     gchar *const family_escaped = g_markup_escape_text(family, -1);
6853     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6854     int show_sample = prefs->getInt("/tools/text/show_sample_in_list", 1);
6855     if (show_sample) {
6857         Glib::ustring sample = prefs->getString("/tools/text/font_sample");
6858         gchar *const sample_escaped = g_markup_escape_text(sample.data(), -1);
6860     std::stringstream markup;
6861     markup << family_escaped << "  <span foreground='darkgray' font_family='"
6862            << family_escaped << "'>" << sample_escaped << "</span>";
6863     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6865         g_free(sample_escaped);
6866     } else {
6867         g_object_set (G_OBJECT (cell), "markup", family_escaped, NULL);
6868     }
6870     g_free(family);
6871     g_free(family_escaped);
6874 gboolean text_toolbox_completion_match_selected(GtkEntryCompletion */*widget*/,
6875                                                 GtkTreeModel       *model,
6876                                                 GtkTreeIter        *iter,
6877                                                 GObject            *tbl)
6879     // We intercept this signal so as to fire family_changed at once (without it, you'd have to
6880     // press Enter again after choosing a completion)
6881     gchar *family = 0;
6882     gtk_tree_model_get(model, iter, 0, &family, -1);
6884     GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6885     gtk_entry_set_text (GTK_ENTRY (entry), family);
6887     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6888     sp_text_toolbox_family_changed (NULL, tbl);
6889     return TRUE;
6893 static void
6894 cbe_add_completion (GtkComboBoxEntry *cbe, GObject *tbl){
6895     GtkEntry *entry;
6896     GtkEntryCompletion *completion;
6897     GtkTreeModel *model;
6899     entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(cbe)));
6900     completion = gtk_entry_completion_new();
6901     model = gtk_combo_box_get_model(GTK_COMBO_BOX(cbe));
6902     gtk_entry_completion_set_model(completion, model);
6903     gtk_entry_completion_set_text_column(completion, 0);
6904     gtk_entry_completion_set_inline_completion(completion, FALSE);
6905     gtk_entry_completion_set_inline_selection(completion, FALSE);
6906     gtk_entry_completion_set_popup_completion(completion, TRUE);
6907     gtk_entry_set_completion(entry, completion);
6909     g_signal_connect (G_OBJECT (completion),  "match-selected", G_CALLBACK (text_toolbox_completion_match_selected), tbl);
6911     g_object_unref(completion);
6914 void sp_text_toolbox_family_popnotify(GtkComboBox *widget,
6915                                       void */*property*/,
6916                                       GObject *tbl)
6918   // while the drop-down is open, we disable font family changing, reenabling it only when it closes
6920   gboolean shown;
6921   g_object_get (G_OBJECT(widget), "popup-shown", &shown, NULL);
6922   if (shown) {
6923          g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6924          //g_print("POP: notify: SHOWN\n");
6925   } else {
6926          g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6928          // stupid GTK doesn't let us attach to events in the drop-down window, so we peek here to
6929          // find out if the drop down was closed by Enter and if so, manually update (only
6930          // necessary on Windows, on Linux it updates itself - what a mess, but we'll manage)
6931          GdkEvent *ev = gtk_get_current_event();
6932          if (ev) {
6933              //g_print ("ev type: %d\n", ev->type);
6934              if (ev->type == GDK_KEY_PRESS) {
6935                  switch (get_group0_keyval ((GdkEventKey *) ev)) {
6936                      case GDK_KP_Enter: // chosen
6937                      case GDK_Return:
6938                      {
6939                          // make sure the chosen one is inserted into the entry
6940                          GtkComboBox  *combo = GTK_COMBO_BOX (((Gtk::ComboBox *) (g_object_get_data (tbl, "family-entry-combo")))->gobj());
6941                          GtkTreeModel *model = gtk_combo_box_get_model(combo);
6942                          GtkTreeIter iter;
6943                          gboolean has_active = gtk_combo_box_get_active_iter (combo, &iter);
6944                          if (has_active) {
6945                              gchar *family;
6946                              gtk_tree_model_get(model, &iter, 0, &family, -1);
6947                              GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6948                              gtk_entry_set_text (GTK_ENTRY (entry), family);
6949                          }
6951                          // update
6952                          sp_text_toolbox_family_changed (NULL, tbl);
6953                          break;
6954                      }
6955                  }
6956              }
6957          }
6959          // regardless of whether we updated, defocus the widget
6960          SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6961          if (desktop)
6962              gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6963          //g_print("POP: notify: HIDDEN\n");
6964   }
6967 GtkWidget *sp_text_toolbox_new (SPDesktop *desktop)
6969     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
6970     GtkIconSize secondarySize = static_cast<GtkIconSize>(ToolboxFactory::prefToSize("/toolbox/secondary", 1));
6972     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
6973     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
6975     GtkTooltips *tt = gtk_tooltips_new();
6977     ////////////Family
6978     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
6979     Gtk::ComboBoxEntry *font_sel = Gtk::manage(new Gtk::ComboBoxEntry(store));
6981     gtk_rc_parse_string (
6982        "style \"dropdown-as-list-style\"\n"
6983        "{\n"
6984        "    GtkComboBox::appears-as-list = 1\n"
6985        "}\n"
6986        "widget \"*.toolbox-fontfamily-list\" style \"dropdown-as-list-style\"");
6987     gtk_widget_set_name(GTK_WIDGET (font_sel->gobj()), "toolbox-fontfamily-list");
6988     gtk_tooltips_set_tip (tt, GTK_WIDGET (font_sel->gobj()), _("Select font family (Alt+X to access)"), "");
6990     g_signal_connect (G_OBJECT (font_sel->gobj()), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
6992     cbe_add_completion(font_sel->gobj(), G_OBJECT(tbl));
6994     gtk_toolbar_append_widget( tbl, (GtkWidget*) font_sel->gobj(), "", "");
6995     g_object_set_data (G_OBJECT (tbl), "family-entry-combo", font_sel);
6997     // expand the field a bit so as to view more of the previews in the drop-down
6998     GtkRequisition req;
6999     gtk_widget_size_request (GTK_WIDGET (font_sel->gobj()), &req);
7000     gtk_widget_set_size_request  (GTK_WIDGET (font_sel->gobj()), MIN(req.width + 50, 500), -1);
7002     GtkWidget* entry = (GtkWidget*) font_sel->get_entry()->gobj();
7003     g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
7005     g_signal_connect (G_OBJECT (font_sel->gobj()), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
7006     g_signal_connect (G_OBJECT (font_sel->gobj()), "notify::popup-shown",
7007              G_CALLBACK (sp_text_toolbox_family_popnotify), tbl);
7008     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
7009     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
7010     g_signal_connect (G_OBJECT (entry),  "focus-out-event", G_CALLBACK (sp_text_toolbox_entry_focus_out), tbl);
7012     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
7013     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
7015     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
7016     gtk_cell_layout_clear( GTK_CELL_LAYOUT(font_sel->gobj()) );
7017     gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(font_sel->gobj()) , cell , TRUE );
7018     gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT(font_sel->gobj()), cell, GtkCellLayoutDataFunc (cell_data_func), NULL, NULL);
7020     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
7021     GtkWidget *box = gtk_event_box_new ();
7022     gtk_container_add (GTK_CONTAINER (box), image);
7023     gtk_toolbar_append_widget( tbl, box, "", "");
7024     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
7025     gtk_tooltips_set_tip (tt, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
7026     gtk_widget_hide (GTK_WIDGET (box));
7027     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
7029     ////////////Size
7030     gchar const *const sizes[] = {
7031         "4", "6", "8", "9", "10", "11", "12", "13", "14",
7032         "16", "18", "20", "22", "24", "28",
7033         "32", "36", "40", "48", "56", "64", "72", "144"
7034     };
7036     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
7037     for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
7038         gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
7039     }
7040     gtk_widget_set_size_request (cbox, 80, -1);
7041     gtk_toolbar_append_widget( tbl, cbox, "", "");
7042     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
7043     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
7044     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
7045     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
7047     ////////////Text anchor
7048     GtkWidget *group   = gtk_radio_button_new (NULL);
7049     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
7050     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
7052     // left
7053     GtkWidget *rbutton = group;
7054     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7055     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
7056     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7058     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7059     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
7060     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
7061     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
7063     // center
7064     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7065     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7066     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
7067     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7069     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7070     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
7071     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
7072     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
7074     // right
7075     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7076     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7077     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
7078     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7080     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7081     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
7082     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
7083     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
7085     // fill
7086     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7087     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7088     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
7089     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7091     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7092     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
7093     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
7094     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
7096     gtk_toolbar_append_widget( tbl, row, "", "");
7098     //spacer
7099     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
7101     ////////////Text style
7102     row = gtk_hbox_new (FALSE, 4);
7104     // bold
7105     rbutton = gtk_toggle_button_new ();
7106     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7107     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
7108     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7109     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
7111     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7112     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
7113     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
7115     // italic
7116     rbutton = gtk_toggle_button_new ();
7117     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7118     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
7119     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7120     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
7122     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7123     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
7124     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
7126     gtk_toolbar_append_widget( tbl, row, "", "");
7128     //spacer
7129     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
7131     // Text orientation
7132     group   = gtk_radio_button_new (NULL);
7133     row     = gtk_hbox_new (FALSE, 4);
7134     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
7136     // horizontal
7137     rbutton = group;
7138     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7139     gtk_container_add           (GTK_CONTAINER (rbutton),
7140                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_HORIZONTAL));
7141     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7142     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
7144     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7145     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
7146     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
7148     // vertical
7149     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7150     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7151     gtk_container_add           (GTK_CONTAINER (rbutton),
7152                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_VERTICAL));
7153     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7154     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
7156     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7157     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
7158     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
7159     gtk_toolbar_append_widget( tbl, row, "", "" );
7162     //watch selection
7163     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
7165     sigc::connection *c_selection_changed =
7166         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
7167                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
7168     pool->add_connection ("selection-changed", c_selection_changed);
7170     sigc::connection *c_selection_modified =
7171         new sigc::connection (sp_desktop_selection (desktop)->connectModified
7172                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
7173     pool->add_connection ("selection-modified", c_selection_modified);
7175     sigc::connection *c_subselection_changed =
7176         new sigc::connection (desktop->connectToolSubselectionChanged
7177                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
7178     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
7180     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
7183     gtk_widget_show_all( GTK_WIDGET(tbl) );
7185     return GTK_WIDGET(tbl);
7186 } // end of sp_text_toolbox_new()
7188 }//<unnamed> namespace
7191 //#########################
7192 //##      Connector      ##
7193 //#########################
7195 static void sp_connector_mode_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7197     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7198     prefs->setBool("/tools/connector/mode",
7199                 gtk_toggle_action_get_active( act ));
7202 static void sp_connector_path_set_avoid(void)
7204     cc_selection_set_avoid(true);
7208 static void sp_connector_path_set_ignore(void)
7210     cc_selection_set_avoid(false);
7213 static void sp_connector_orthogonal_toggled( GtkToggleAction* act, GObject *tbl )
7215     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7216     Inkscape::Selection * selection = sp_desktop_selection(desktop);
7217     SPDocument *doc = sp_desktop_document(desktop);
7219     if (!sp_document_get_undo_sensitive(doc))
7220     {
7221         return;
7222     }
7225     // quit if run by the _changed callbacks
7226     if (g_object_get_data( tbl, "freeze" )) {
7227         return;
7228     }
7230     // in turn, prevent callbacks from responding
7231     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7233     bool is_orthog = gtk_toggle_action_get_active( act );
7234     gchar orthog_str[] = "orthogonal";
7235     gchar polyline_str[] = "polyline";
7236     gchar *value = is_orthog ? orthog_str : polyline_str ;
7238     bool modmade = false;
7239     GSList *l = (GSList *) selection->itemList();
7240     while (l) {
7241         SPItem *item = (SPItem *) l->data;
7243         if (cc_item_is_connector(item)) {
7244             sp_object_setAttribute(item, "inkscape:connector-type",
7245                     value, false);
7246             item->avoidRef->handleSettingChange();
7247             modmade = true;
7248         }
7249         l = l->next;
7250     }
7252     if (!modmade)
7253     {
7254         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7255         prefs->setBool("/tools/connector/orthogonal", is_orthog);
7256     }
7258     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7259             is_orthog ? _("Set connector type: orthogonal"): _("Set connector type: polyline"));
7261     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7264 static void connector_curvature_changed(GtkAdjustment *adj, GObject* tbl)
7266     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7267     Inkscape::Selection * selection = sp_desktop_selection(desktop);
7268     SPDocument *doc = sp_desktop_document(desktop);
7270     if (!sp_document_get_undo_sensitive(doc))
7271     {
7272         return;
7273     }
7276     // quit if run by the _changed callbacks
7277     if (g_object_get_data( tbl, "freeze" )) {
7278         return;
7279     }
7281     // in turn, prevent callbacks from responding
7282     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7284     gdouble newValue = gtk_adjustment_get_value(adj);
7285     gchar value[G_ASCII_DTOSTR_BUF_SIZE];
7286     g_ascii_dtostr(value, G_ASCII_DTOSTR_BUF_SIZE, newValue);
7288     bool modmade = false;
7289     GSList *l = (GSList *) selection->itemList();
7290     while (l) {
7291         SPItem *item = (SPItem *) l->data;
7293         if (cc_item_is_connector(item)) {
7294             sp_object_setAttribute(item, "inkscape:connector-curvature",
7295                     value, false);
7296             item->avoidRef->handleSettingChange();
7297             modmade = true;
7298         }
7299         l = l->next;
7300     }
7302     if (!modmade)
7303     {
7304         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7305         prefs->setDouble(Glib::ustring("/tools/connector/curvature"), newValue);
7306     }
7308     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7309             _("Change connector curvature"));
7311     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7315 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
7317     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7318     SPDocument *doc = sp_desktop_document(desktop);
7320     if (!sp_document_get_undo_sensitive(doc))
7321     {
7322         return;
7323     }
7325     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7327     if ( !repr->attribute("inkscape:connector-spacing") &&
7328             ( adj->value == defaultConnSpacing )) {
7329         // Don't need to update the repr if the attribute doesn't
7330         // exist and it is being set to the default value -- as will
7331         // happen at startup.
7332         return;
7333     }
7335     // quit if run by the attr_changed listener
7336     if (g_object_get_data( tbl, "freeze" )) {
7337         return;
7338     }
7340     // in turn, prevent listener from responding
7341     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
7343     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
7344     SP_OBJECT(desktop->namedview)->updateRepr();
7346     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
7347     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
7348         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
7349         Geom::Matrix m = Geom::identity();
7350         avoid_item_move(&m, item);
7351     }
7353     if (items) {
7354         g_slist_free(items);
7355     }
7357     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7358             _("Change connector spacing"));
7360     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7363 static void sp_connector_graph_layout(void)
7365     if (!SP_ACTIVE_DESKTOP) return;
7366     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7368     // hack for clones, see comment in align-and-distribute.cpp
7369     int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7370     prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7372     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
7374     prefs->setInt("/options/clonecompensation/value", saved_compensation);
7376     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
7379 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7381     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7382     prefs->setBool("/tools/connector/directedlayout",
7383                 gtk_toggle_action_get_active( act ));
7386 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7388     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7389     prefs->setBool("/tools/connector/avoidoverlaplayout",
7390                 gtk_toggle_action_get_active( act ));
7394 static void connector_length_changed(GtkAdjustment *adj, GObject* /*tbl*/)
7396     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7397     prefs->setDouble("/tools/connector/length", adj->value);
7400 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
7401                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
7402                                             bool /*is_interactive*/, gpointer data)
7404     GtkWidget *tbl = GTK_WIDGET(data);
7406     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
7407         return;
7408     }
7409     if (strcmp(name, "inkscape:connector-spacing") == 0)
7410     {
7411         GtkAdjustment *adj = (GtkAdjustment*)
7412                 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
7413         gdouble spacing = defaultConnSpacing;
7414         sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
7416         gtk_adjustment_set_value(adj, spacing);
7417         gtk_adjustment_value_changed(adj);
7418     }
7420     spinbutton_defocus(GTK_OBJECT(tbl));
7423 static void sp_connector_new_connection_point(GtkWidget *, GObject *tbl)
7425     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7426     SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7428     if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE)
7429         cc_create_connection_point(cc);
7432 static void sp_connector_remove_connection_point(GtkWidget *, GObject *tbl)
7434     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7435     SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7437     if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE)
7438         cc_remove_connection_point(cc);
7441 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
7442     NULL, /* child_added */
7443     NULL, /* child_removed */
7444     connector_tb_event_attr_changed,
7445     NULL, /* content_changed */
7446     NULL  /* order_changed */
7447 };
7449 static void sp_connector_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
7451     GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "curvature" ) );
7452     GtkToggleAction *act = GTK_TOGGLE_ACTION( g_object_get_data( tbl, "orthogonal" ) );
7453     SPItem *item = selection->singleItem();
7454     if (SP_IS_PATH(item))
7455     {
7456         gdouble curvature = SP_PATH(item)->connEndPair.getCurvature();
7457         bool is_orthog = SP_PATH(item)->connEndPair.isOrthogonal();
7458         gtk_toggle_action_set_active(act, is_orthog);
7459         gtk_adjustment_set_value(adj, curvature);
7460     }
7464 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
7466     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7467     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
7469     // Editing mode toggle button
7470     {
7471         InkToggleAction* act = ink_toggle_action_new( "ConnectorEditModeAction",
7472                                                       _("EditMode"),
7473                                                       _("Switch between connection point editing and connector drawing mode"),
7474                                                       INKSCAPE_ICON_CONNECTOR_EDIT,
7475                                                       Inkscape::ICON_SIZE_DECORATION );
7476         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7478         bool tbuttonstate = prefs->getBool("/tools/connector/mode");
7479         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7480         g_object_set_data( holder, "mode", act );
7481         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_mode_toggled), holder );
7482     }
7485     {
7486         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
7487                                           _("Avoid"),
7488                                           _("Make connectors avoid selected objects"),
7489                                           INKSCAPE_ICON_CONNECTOR_AVOID,
7490                                           secondarySize );
7491         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
7492         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7493     }
7495     {
7496         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
7497                                           _("Ignore"),
7498                                           _("Make connectors ignore selected objects"),
7499                                           INKSCAPE_ICON_CONNECTOR_IGNORE,
7500                                           secondarySize );
7501         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
7502         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7503     }
7505     // Orthogonal connectors toggle button
7506     {
7507         InkToggleAction* act = ink_toggle_action_new( "ConnectorOrthogonalAction",
7508                                                       _("Orthogonal"),
7509                                                       _("Make connector orthogonal or polyline"),
7510                                                       INKSCAPE_ICON_CONNECTOR_ORTHOGONAL,
7511                                                       Inkscape::ICON_SIZE_DECORATION );
7512         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7514         bool tbuttonstate = prefs->getBool("/tools/connector/orthogonal");
7515         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7516         g_object_set_data( holder, "orthogonal", act );
7517         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_orthogonal_toggled), holder );
7518     }
7520     EgeAdjustmentAction* eact = 0;
7521     // Curvature spinbox
7522     eact = create_adjustment_action( "ConnectorCurvatureAction",
7523                                     _("Connector Curvature"), _("Curvature:"),
7524                                     _("The amount of connectors curvature"),
7525                                     "/tools/connector/curvature", defaultConnCurvature,
7526                                     GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-curvature",
7527                                     0, 100, 1.0, 10.0,
7528                                     0, 0, 0,
7529                                     connector_curvature_changed, 1, 0 );
7530     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7532     // Spacing spinbox
7533     eact = create_adjustment_action( "ConnectorSpacingAction",
7534                                     _("Connector Spacing"), _("Spacing:"),
7535                                     _("The amount of space left around objects by auto-routing connectors"),
7536                                     "/tools/connector/spacing", defaultConnSpacing,
7537                                     GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
7538                                     0, 100, 1.0, 10.0,
7539                                     0, 0, 0,
7540                                     connector_spacing_changed, 1, 0 );
7541     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7543     // Graph (connector network) layout
7544     {
7545         InkAction* inky = ink_action_new( "ConnectorGraphAction",
7546                                           _("Graph"),
7547                                           _("Nicely arrange selected connector network"),
7548                                           INKSCAPE_ICON_DISTRIBUTE_GRAPH,
7549                                           secondarySize );
7550         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
7551         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7552     }
7554     // Default connector length spinbox
7555     eact = create_adjustment_action( "ConnectorLengthAction",
7556                                      _("Connector Length"), _("Length:"),
7557                                      _("Ideal length for connectors when layout is applied"),
7558                                      "/tools/connector/length", 100,
7559                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
7560                                      10, 1000, 10.0, 100.0,
7561                                      0, 0, 0,
7562                                      connector_length_changed, 1, 0 );
7563     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7566     // Directed edges toggle button
7567     {
7568         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
7569                                                       _("Downwards"),
7570                                                       _("Make connectors with end-markers (arrows) point downwards"),
7571                                                       INKSCAPE_ICON_DISTRIBUTE_GRAPH_DIRECTED,
7572                                                       Inkscape::ICON_SIZE_DECORATION );
7573         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7575         bool tbuttonstate = prefs->getBool("/tools/connector/directedlayout");
7576         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7578         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
7579         sigc::connection *connection = new sigc::connection(sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_connector_toolbox_selection_changed), (GObject *)holder))
7580         );
7581     }
7583     // Avoid overlaps toggle button
7584     {
7585         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
7586                                                       _("Remove overlaps"),
7587                                                       _("Do not allow overlapping shapes"),
7588                                                       INKSCAPE_ICON_DISTRIBUTE_REMOVE_OVERLAPS,
7589                                                       Inkscape::ICON_SIZE_DECORATION );
7590         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7592         bool tbuttonstate = prefs->getBool("/tools/connector/avoidoverlaplayout");
7593         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), (tbuttonstate ? TRUE : FALSE ));
7595         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
7596     }
7599     // New connection point button
7600     {
7601         InkAction* inky = ink_action_new( "ConnectorNewConnPointAction",
7602                                           _("New connection point"),
7603                                           _("Add a new connection point to the currently selected item"),
7604                                           INKSCAPE_ICON_CONNECTOR_NEW_CONNPOINT,
7605                                           secondarySize );
7606         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_new_connection_point), holder );
7607         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7608     }
7610     // Remove selected connection point button
7612     {
7613         InkAction* inky = ink_action_new( "ConnectorRemoveConnPointAction",
7614                                           _("Remove connection point"),
7615                                           _("Remove the currently selected connection point"),
7616                                           INKSCAPE_ICON_CONNECTOR_REMOVE_CONNPOINT,
7617                                           secondarySize );
7618         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_remove_connection_point), holder );
7619         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7620     }
7623     // Code to watch for changes to the connector-spacing attribute in
7624     // the XML.
7625     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7626     g_assert(repr != NULL);
7628     purge_repr_listener( holder, holder );
7630     if (repr) {
7631         g_object_set_data( holder, "repr", repr );
7632         Inkscape::GC::anchor(repr);
7633         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
7634         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
7635     }
7636 } // end of sp_connector_toolbox_prep()
7639 //#########################
7640 //##     Paintbucket     ##
7641 //#########################
7643 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
7645     gint channels = ege_select_one_action_get_active( act );
7646     flood_channels_set_channels( channels );
7649 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
7651     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7652     prefs->setInt("/tools/paintbucket/threshold", (gint)adj->value);
7655 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
7657     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7658     prefs->setBool("/tools/paintbucket/autogap", ege_select_one_action_get_active( act ));
7661 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
7663     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
7664     SPUnit const *unit = tracker->getActiveUnit();
7665     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7667     prefs->setDouble("/tools/paintbucket/offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
7668     prefs->setString("/tools/paintbucket/offsetunits", sp_unit_get_abbreviation(unit));
7671 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
7673     // FIXME: make defaults settable via Inkscape Options
7674     struct KeyValue {
7675         char const *key;
7676         double value;
7677     } const key_values[] = {
7678         {"threshold", 15},
7679         {"offset", 0.0}
7680     };
7682     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
7683         KeyValue const &kv = key_values[i];
7684         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
7685         if ( adj ) {
7686             gtk_adjustment_set_value(adj, kv.value);
7687         }
7688     }
7690     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
7691     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
7692     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
7693     ege_select_one_action_set_active( autogap_action, 0 );
7696 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
7698     EgeAdjustmentAction* eact = 0;
7699     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7701     {
7702         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7704         GList* items = 0;
7705         gint count = 0;
7706         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
7707         {
7708             GtkTreeIter iter;
7709             gtk_list_store_append( model, &iter );
7710             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7711             count++;
7712         }
7713         g_list_free( items );
7714         items = 0;
7715         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
7716         g_object_set( act1, "short_label", _("Fill by:"), NULL );
7717         ege_select_one_action_set_appearance( act1, "compact" );
7718         ege_select_one_action_set_active( act1, prefs->getInt("/tools/paintbucket/channels", 0) );
7719         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
7720         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
7721         g_object_set_data( holder, "channels_action", act1 );
7722     }
7724     // Spacing spinbox
7725     {
7726         eact = create_adjustment_action(
7727             "ThresholdAction",
7728             _("Fill Threshold"), _("Threshold:"),
7729             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
7730             "/tools/paintbucket/threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
7731             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
7732             0, 0, 0,
7733             paintbucket_threshold_changed, 1, 0 );
7735         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
7736         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7737     }
7739     // Create the units menu.
7740     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
7741     Glib::ustring stored_unit = prefs->getString("/tools/paintbucket/offsetunits");
7742     if (!stored_unit.empty())
7743         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit.data()));
7744     g_object_set_data( holder, "tracker", tracker );
7745     {
7746         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
7747         gtk_action_group_add_action( mainActions, act );
7748     }
7750     // Offset spinbox
7751     {
7752         eact = create_adjustment_action(
7753             "OffsetAction",
7754             _("Grow/shrink by"), _("Grow/shrink by:"),
7755             _("The amount to grow (positive) or shrink (negative) the created fill path"),
7756             "/tools/paintbucket/offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
7757             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
7758             0, 0, 0,
7759             paintbucket_offset_changed, 1, 2);
7760         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
7762         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7763     }
7765     /* Auto Gap */
7766     {
7767         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7769         GList* items = 0;
7770         gint count = 0;
7771         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
7772         {
7773             GtkTreeIter iter;
7774             gtk_list_store_append( model, &iter );
7775             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7776             count++;
7777         }
7778         g_list_free( items );
7779         items = 0;
7780         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
7781         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
7782         ege_select_one_action_set_appearance( act2, "compact" );
7783         ege_select_one_action_set_active( act2, prefs->getBool("/tools/paintbucket/autogap") );
7784         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
7785         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
7786         g_object_set_data( holder, "autogap_action", act2 );
7787     }
7789     /* Reset */
7790     {
7791         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
7792                                           _("Defaults"),
7793                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
7794                                           GTK_STOCK_CLEAR );
7795         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
7796         gtk_action_group_add_action( mainActions, act );
7797         gtk_action_set_sensitive( act, TRUE );
7798     }
7802 /*
7803   Local Variables:
7804   mode:c++
7805   c-file-style:"stroustrup"
7806   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
7807   indent-tabs-mode:nil
7808   fill-column:99
7809   End:
7810 */
7811 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :