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@scislac.com>
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 "../ui/dialog/calligraphic-profile-rename.h"
90 #include "../ui/icon-names.h"
91 #include "../ui/widget/style-swatch.h"
92 #include "../verbs.h"
93 #include "../widgets/button.h"
94 #include "../widgets/spinbutton-events.h"
95 #include "../widgets/spw-utilities.h"
96 #include "../widgets/widget-sizes.h"
97 #include "../xml/attribute-record.h"
98 #include "../xml/node-event-vector.h"
99 #include "../xml/repr.h"
101 #include "toolbox.h"
103 using Inkscape::UnitTracker;
105 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
106 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
108 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
109 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
110 static void sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
111 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
112 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
113 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
114 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
115 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
116 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
117 static void sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
118 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
119 static void sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
120 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
121 static void sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
122 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
123 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
126 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
129 Inkscape::IconSize prefToSize( Glib::ustring const &path, int base ) {
130 static Inkscape::IconSize sizeChoices[] = {
131 Inkscape::ICON_SIZE_LARGE_TOOLBAR,
132 Inkscape::ICON_SIZE_SMALL_TOOLBAR,
133 Inkscape::ICON_SIZE_MENU
134 };
135 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
136 int index = prefs->getIntLimited( path, base, 0, G_N_ELEMENTS(sizeChoices) );
137 return sizeChoices[index];
138 }
140 static struct {
141 gchar const *type_name;
142 gchar const *data_name;
143 sp_verb_t verb;
144 sp_verb_t doubleclick_verb;
145 } const tools[] = {
146 { "SPSelectContext", "select_tool", SP_VERB_CONTEXT_SELECT, SP_VERB_CONTEXT_SELECT_PREFS},
147 { "SPNodeContext", "node_tool", SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
148 { "SPTweakContext", "tweak_tool", SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
149 { "SPZoomContext", "zoom_tool", SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
150 { "SPRectContext", "rect_tool", SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
151 { "Box3DContext", "3dbox_tool", SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
152 { "SPArcContext", "arc_tool", SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
153 { "SPStarContext", "star_tool", SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
154 { "SPSpiralContext", "spiral_tool", SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
155 { "SPPencilContext", "pencil_tool", SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
156 { "SPPenContext", "pen_tool", SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
157 { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
158 { "SPLPEToolContext", "lpetool_tool", SP_VERB_CONTEXT_LPETOOL, SP_VERB_CONTEXT_LPETOOL_PREFS },
159 { "SPEraserContext", "eraser_tool", SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
160 { "SPFloodContext", "paintbucket_tool", SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
161 { "SPTextContext", "text_tool", SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
162 { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
163 { "SPGradientContext", "gradient_tool", SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
164 { "SPDropperContext", "dropper_tool", SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
165 { NULL, NULL, 0, 0 }
166 };
168 static struct {
169 gchar const *type_name;
170 gchar const *data_name;
171 GtkWidget *(*create_func)(SPDesktop *desktop);
172 void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
173 gchar const *ui_name;
174 gint swatch_verb_id;
175 gchar const *swatch_tool;
176 gchar const *swatch_tip;
177 } const aux_toolboxes[] = {
178 { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep, "SelectToolbar",
179 SP_VERB_INVALID, 0, 0},
180 { "SPNodeContext", "node_toolbox", 0, sp_node_toolbox_prep, "NodeToolbar",
181 SP_VERB_INVALID, 0, 0},
182 { "SPTweakContext", "tweak_toolbox", 0, sp_tweak_toolbox_prep, "TweakToolbar",
183 SP_VERB_CONTEXT_TWEAK_PREFS, "/tools/tweak", N_("Color/opacity used for color tweaking")},
184 { "SPZoomContext", "zoom_toolbox", 0, sp_zoom_toolbox_prep, "ZoomToolbar",
185 SP_VERB_INVALID, 0, 0},
186 { "SPStarContext", "star_toolbox", 0, sp_star_toolbox_prep, "StarToolbar",
187 SP_VERB_CONTEXT_STAR_PREFS, "/tools/shapes/star", N_("Style of new stars")},
188 { "SPRectContext", "rect_toolbox", 0, sp_rect_toolbox_prep, "RectToolbar",
189 SP_VERB_CONTEXT_RECT_PREFS, "/tools/shapes/rect", N_("Style of new rectangles")},
190 { "Box3DContext", "3dbox_toolbox", 0, box3d_toolbox_prep, "3DBoxToolbar",
191 SP_VERB_CONTEXT_3DBOX_PREFS, "/tools/shapes/3dbox", N_("Style of new 3D boxes")},
192 { "SPArcContext", "arc_toolbox", 0, sp_arc_toolbox_prep, "ArcToolbar",
193 SP_VERB_CONTEXT_ARC_PREFS, "/tools/shapes/arc", N_("Style of new ellipses")},
194 { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep, "SpiralToolbar",
195 SP_VERB_CONTEXT_SPIRAL_PREFS, "/tools/shapes/spiral", N_("Style of new spirals")},
196 { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep, "PencilToolbar",
197 SP_VERB_CONTEXT_PENCIL_PREFS, "/tools/freehand/pencil", N_("Style of new paths created by Pencil")},
198 { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep, "PenToolbar",
199 SP_VERB_CONTEXT_PEN_PREFS, "/tools/freehand/pen", N_("Style of new paths created by Pen")},
200 { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
201 SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "/tools/calligraphic", N_("Style of new calligraphic strokes")},
202 { "SPEraserContext", "eraser_toolbox", 0, sp_eraser_toolbox_prep,"EraserToolbar",
203 SP_VERB_CONTEXT_ERASER_PREFS, "/tools/eraser", _("TBD")},
204 { "SPLPEToolContext", "lpetool_toolbox", 0, sp_lpetool_toolbox_prep, "LPEToolToolbar",
205 SP_VERB_CONTEXT_LPETOOL_PREFS, "/tools/lpetool", _("TBD")},
206 { "SPTextContext", "text_toolbox", sp_text_toolbox_new, 0, 0,
207 SP_VERB_INVALID, 0, 0},
208 { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep, "DropperToolbar",
209 SP_VERB_INVALID, 0, 0},
210 { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0, 0,
211 SP_VERB_INVALID, 0, 0},
212 { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep, "ConnectorToolbar",
213 SP_VERB_INVALID, 0, 0},
214 { "SPFloodContext", "paintbucket_toolbox", 0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
215 SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "/tools/paintbucket", N_("Style of Paint Bucket fill objects")},
216 { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
217 };
219 #define TOOLBAR_SLIDER_HINT "full"
221 static gchar const * ui_descr =
222 "<ui>"
223 " <toolbar name='SelectToolbar'>"
224 " <toolitem action='EditSelectAll' />"
225 " <toolitem action='EditSelectAllInAllLayers' />"
226 " <toolitem action='EditDeselect' />"
227 " <separator />"
228 " <toolitem action='ObjectRotate90CCW' />"
229 " <toolitem action='ObjectRotate90' />"
230 " <toolitem action='ObjectFlipHorizontally' />"
231 " <toolitem action='ObjectFlipVertically' />"
232 " <separator />"
233 " <toolitem action='SelectionToBack' />"
234 " <toolitem action='SelectionLower' />"
235 " <toolitem action='SelectionRaise' />"
236 " <toolitem action='SelectionToFront' />"
237 " <separator />"
238 " <toolitem action='XAction' />"
239 " <toolitem action='YAction' />"
240 " <toolitem action='WidthAction' />"
241 " <toolitem action='LockAction' />"
242 " <toolitem action='HeightAction' />"
243 " <toolitem action='UnitsAction' />"
244 " <separator />"
245 " <toolitem action='transform_affect_label' />"
246 " <toolitem action='transform_stroke' />"
247 " <toolitem action='transform_corners' />"
248 " <toolitem action='transform_gradient' />"
249 " <toolitem action='transform_pattern' />"
250 " </toolbar>"
252 " <toolbar name='NodeToolbar'>"
253 " <toolitem action='NodeInsertAction' />"
254 " <toolitem action='NodeDeleteAction' />"
255 " <separator />"
256 " <toolitem action='NodeJoinAction' />"
257 " <toolitem action='NodeBreakAction' />"
258 " <separator />"
259 " <toolitem action='NodeJoinSegmentAction' />"
260 " <toolitem action='NodeDeleteSegmentAction' />"
261 " <separator />"
262 " <toolitem action='NodeCuspAction' />"
263 " <toolitem action='NodeSmoothAction' />"
264 " <toolitem action='NodeSymmetricAction' />"
265 " <toolitem action='NodeAutoAction' />"
266 " <separator />"
267 " <toolitem action='NodeLineAction' />"
268 " <toolitem action='NodeCurveAction' />"
269 " <separator />"
270 " <toolitem action='ObjectToPath' />"
271 " <toolitem action='StrokeToPath' />"
272 " <separator />"
273 " <toolitem action='NodeXAction' />"
274 " <toolitem action='NodeYAction' />"
275 " <toolitem action='NodeUnitsAction' />"
276 " <separator />"
277 " <toolitem action='ObjectEditClipPathAction' />"
278 " <toolitem action='ObjectEditMaskPathAction' />"
279 " <toolitem action='EditNextLPEParameterAction' />"
280 " <separator />"
281 " <toolitem action='NodesShowHandlesAction' />"
282 " <toolitem action='NodesShowHelperpath' />"
283 " </toolbar>"
285 " <toolbar name='TweakToolbar'>"
286 " <toolitem action='TweakWidthAction' />"
287 " <separator />"
288 " <toolitem action='TweakForceAction' />"
289 " <toolitem action='TweakPressureAction' />"
290 " <separator />"
291 " <toolitem action='TweakModeAction' />"
292 " <separator />"
293 " <toolitem action='TweakFidelityAction' />"
294 " <separator />"
295 " <toolitem action='TweakChannelsLabel' />"
296 " <toolitem action='TweakDoH' />"
297 " <toolitem action='TweakDoS' />"
298 " <toolitem action='TweakDoL' />"
299 " <toolitem action='TweakDoO' />"
300 " </toolbar>"
302 " <toolbar name='ZoomToolbar'>"
303 " <toolitem action='ZoomIn' />"
304 " <toolitem action='ZoomOut' />"
305 " <separator />"
306 " <toolitem action='Zoom1:0' />"
307 " <toolitem action='Zoom1:2' />"
308 " <toolitem action='Zoom2:1' />"
309 " <separator />"
310 " <toolitem action='ZoomSelection' />"
311 " <toolitem action='ZoomDrawing' />"
312 " <toolitem action='ZoomPage' />"
313 " <toolitem action='ZoomPageWidth' />"
314 " <separator />"
315 " <toolitem action='ZoomPrev' />"
316 " <toolitem action='ZoomNext' />"
317 " </toolbar>"
319 " <toolbar name='StarToolbar'>"
320 " <separator />"
321 " <toolitem action='StarStateAction' />"
322 " <separator />"
323 " <toolitem action='FlatAction' />"
324 " <separator />"
325 " <toolitem action='MagnitudeAction' />"
326 " <toolitem action='SpokeAction' />"
327 " <toolitem action='RoundednessAction' />"
328 " <toolitem action='RandomizationAction' />"
329 " <separator />"
330 " <toolitem action='StarResetAction' />"
331 " </toolbar>"
333 " <toolbar name='RectToolbar'>"
334 " <toolitem action='RectStateAction' />"
335 " <toolitem action='RectWidthAction' />"
336 " <toolitem action='RectHeightAction' />"
337 " <toolitem action='RadiusXAction' />"
338 " <toolitem action='RadiusYAction' />"
339 " <toolitem action='RectUnitsAction' />"
340 " <separator />"
341 " <toolitem action='RectResetAction' />"
342 " </toolbar>"
344 " <toolbar name='3DBoxToolbar'>"
345 " <toolitem action='3DBoxAngleXAction' />"
346 " <toolitem action='3DBoxVPXStateAction' />"
347 " <separator />"
348 " <toolitem action='3DBoxAngleYAction' />"
349 " <toolitem action='3DBoxVPYStateAction' />"
350 " <separator />"
351 " <toolitem action='3DBoxAngleZAction' />"
352 " <toolitem action='3DBoxVPZStateAction' />"
353 " </toolbar>"
355 " <toolbar name='SpiralToolbar'>"
356 " <toolitem action='SpiralStateAction' />"
357 " <toolitem action='SpiralRevolutionAction' />"
358 " <toolitem action='SpiralExpansionAction' />"
359 " <toolitem action='SpiralT0Action' />"
360 " <separator />"
361 " <toolitem action='SpiralResetAction' />"
362 " </toolbar>"
364 " <toolbar name='PenToolbar'>"
365 " <toolitem action='FreehandModeActionPen' />"
366 " <separator />"
367 " <toolitem action='SetPenShapeAction'/>"
368 " </toolbar>"
370 " <toolbar name='PencilToolbar'>"
371 " <toolitem action='FreehandModeActionPencil' />"
372 " <separator />"
373 " <toolitem action='PencilToleranceAction' />"
374 " <separator />"
375 " <toolitem action='PencilResetAction' />"
376 " <separator />"
377 " <toolitem action='SetPencilShapeAction'/>"
378 " </toolbar>"
380 " <toolbar name='CalligraphyToolbar'>"
381 " <separator />"
382 " <toolitem action='SetProfileAction'/>"
383 " <separator />"
384 " <toolitem action='CalligraphyWidthAction' />"
385 " <toolitem action='PressureAction' />"
386 " <toolitem action='TraceAction' />"
387 " <toolitem action='ThinningAction' />"
388 " <separator />"
389 " <toolitem action='AngleAction' />"
390 " <toolitem action='TiltAction' />"
391 " <toolitem action='FixationAction' />"
392 " <separator />"
393 " <toolitem action='CapRoundingAction' />"
394 " <separator />"
395 " <toolitem action='TremorAction' />"
396 " <toolitem action='WiggleAction' />"
397 " <toolitem action='MassAction' />"
398 " <separator />"
399 " </toolbar>"
401 " <toolbar name='ArcToolbar'>"
402 " <toolitem action='ArcStateAction' />"
403 " <separator />"
404 " <toolitem action='ArcStartAction' />"
405 " <toolitem action='ArcEndAction' />"
406 " <separator />"
407 " <toolitem action='ArcOpenAction' />"
408 " <separator />"
409 " <toolitem action='ArcResetAction' />"
410 " <separator />"
411 " </toolbar>"
413 " <toolbar name='PaintbucketToolbar'>"
414 " <toolitem action='ChannelsAction' />"
415 " <separator />"
416 " <toolitem action='ThresholdAction' />"
417 " <separator />"
418 " <toolitem action='OffsetAction' />"
419 " <toolitem action='PaintbucketUnitsAction' />"
420 " <separator />"
421 " <toolitem action='AutoGapAction' />"
422 " <separator />"
423 " <toolitem action='PaintbucketResetAction' />"
424 " </toolbar>"
426 " <toolbar name='EraserToolbar'>"
427 " <toolitem action='EraserWidthAction' />"
428 " <separator />"
429 " <toolitem action='EraserModeAction' />"
430 " </toolbar>"
432 " <toolbar name='LPEToolToolbar'>"
433 " <toolitem action='LPEToolModeAction' />"
434 " <separator />"
435 " <toolitem action='LPEShowBBoxAction' />"
436 " <toolitem action='LPEBBoxFromSelectionAction' />"
437 " <separator />"
438 " <toolitem action='LPELineSegmentAction' />"
439 " <separator />"
440 " <toolitem action='LPEMeasuringAction' />"
441 " <toolitem action='LPEToolUnitsAction' />"
442 " <separator />"
443 " <toolitem action='LPEOpenLPEDialogAction' />"
444 " </toolbar>"
446 " <toolbar name='DropperToolbar'>"
447 " <toolitem action='DropperOpacityAction' />"
448 " <toolitem action='DropperPickAlphaAction' />"
449 " <toolitem action='DropperSetAlphaAction' />"
450 " </toolbar>"
452 " <toolbar name='ConnectorToolbar'>"
453 " <toolitem action='ConnectorAvoidAction' />"
454 " <toolitem action='ConnectorIgnoreAction' />"
455 " <toolitem action='ConnectorSpacingAction' />"
456 " <toolitem action='ConnectorGraphAction' />"
457 " <toolitem action='ConnectorLengthAction' />"
458 " <toolitem action='ConnectorDirectedAction' />"
459 " <toolitem action='ConnectorOverlapAction' />"
460 " </toolbar>"
462 "</ui>"
463 ;
465 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
467 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
469 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
470 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
472 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
473 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
475 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
476 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
478 GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick( GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
479 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
480 Inkscape::UI::View::View *view, GtkTooltips *tt);
482 class VerbAction : public Gtk::Action {
483 public:
484 static Glib::RefPtr<VerbAction> create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
486 virtual ~VerbAction();
487 virtual void set_active(bool active = true);
489 protected:
490 virtual Gtk::Widget* create_menu_item_vfunc();
491 virtual Gtk::Widget* create_tool_item_vfunc();
493 virtual void connect_proxy_vfunc(Gtk::Widget* proxy);
494 virtual void disconnect_proxy_vfunc(Gtk::Widget* proxy);
496 virtual void on_activate();
498 private:
499 Inkscape::Verb* verb;
500 Inkscape::Verb* verb2;
501 Inkscape::UI::View::View *view;
502 GtkTooltips *tooltips;
503 bool active;
505 VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
506 };
509 Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips)
510 {
511 Glib::RefPtr<VerbAction> result;
512 SPAction *action = verb->get_action(view);
513 if ( action ) {
514 //SPAction* action2 = verb2 ? verb2->get_action(view) : 0;
515 result = Glib::RefPtr<VerbAction>(new VerbAction(verb, verb2, view, tooltips));
516 }
518 return result;
519 }
521 VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips) :
522 Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(verb->get_image()), Glib::ustring(_(verb->get_name())), Glib::ustring(_(verb->get_tip()))),
523 verb(verb),
524 verb2(verb2),
525 view(view),
526 tooltips(tooltips),
527 active(false)
528 {
529 }
531 VerbAction::~VerbAction()
532 {
533 }
535 Gtk::Widget* VerbAction::create_menu_item_vfunc()
536 {
537 // First call in to get the icon rendered if present in SVG
538 Gtk::Widget *widget = sp_icon_get_icon( property_stock_id().get_value().get_string(), Inkscape::ICON_SIZE_MENU );
539 delete widget;
540 widget = 0;
542 Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
543 // g_message("create_menu_item_vfunc() = %p for '%s'", widg, verb->get_id());
544 return widg;
545 }
547 Gtk::Widget* VerbAction::create_tool_item_vfunc()
548 {
549 // Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
550 Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
551 GtkWidget* toolbox = 0;
552 GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
553 SP_BUTTON_TYPE_TOGGLE,
554 verb,
555 verb2,
556 view,
557 tooltips );
558 if ( active ) {
559 sp_button_toggle_set_down( SP_BUTTON(button), active);
560 }
561 gtk_widget_show_all( button );
562 Gtk::Widget* wrapped = Glib::wrap(button);
563 Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
564 holder->add(*wrapped);
566 // g_message("create_tool_item_vfunc() = %p for '%s'", holder, verb->get_id());
567 return holder;
568 }
570 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
571 {
572 // g_message("connect_proxy_vfunc(%p) for '%s'", proxy, verb->get_id());
573 Gtk::Action::connect_proxy_vfunc(proxy);
574 }
576 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
577 {
578 // g_message("disconnect_proxy_vfunc(%p) for '%s'", proxy, verb->get_id());
579 Gtk::Action::disconnect_proxy_vfunc(proxy);
580 }
582 void VerbAction::set_active(bool active)
583 {
584 this->active = active;
585 Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
586 for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
587 Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
588 if (ti) {
589 // *should* have one child that is the SPButton
590 Gtk::Widget* child = ti->get_child();
591 if ( child && SP_IS_BUTTON(child->gobj()) ) {
592 SPButton* button = SP_BUTTON(child->gobj());
593 sp_button_toggle_set_down( button, active );
594 }
595 }
596 }
597 }
599 void VerbAction::on_activate()
600 {
601 if ( verb ) {
602 SPAction *action = verb->get_action(view);
603 if ( action ) {
604 sp_action_perform(action, 0);
605 }
606 }
607 }
609 /* Global text entry widgets necessary for update */
610 /* GtkWidget *dropper_rgb_entry,
611 *dropper_opacity_entry ; */
612 // should be made a private member once this is converted to class
614 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
615 connection->disconnect();
616 delete connection;
617 }
619 static void purge_repr_listener( GObject* obj, GObject* tbl )
620 {
621 (void)obj;
622 Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
623 if (oldrepr) { // remove old listener
624 sp_repr_remove_listener_by_data(oldrepr, tbl);
625 Inkscape::GC::release(oldrepr);
626 oldrepr = 0;
627 g_object_set_data( tbl, "repr", NULL );
628 }
629 }
631 GtkWidget *
632 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
633 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
634 Inkscape::UI::View::View *view, GtkTooltips *tt)
635 {
636 SPAction *action = verb->get_action(view);
637 if (!action) return NULL;
639 SPAction *doubleclick_action;
640 if (doubleclick_verb)
641 doubleclick_action = doubleclick_verb->get_action(view);
642 else
643 doubleclick_action = NULL;
645 /* fixme: Handle sensitive/unsensitive */
646 /* fixme: Implement sp_button_new_from_action */
647 GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
648 gtk_widget_show(b);
651 unsigned int shortcut = sp_shortcut_get_primary(verb);
652 if (shortcut) {
653 gchar key[256];
654 sp_ui_shortcut_string(shortcut, key);
655 gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
656 if ( t ) {
657 gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
658 }
659 g_free(tip);
660 } else {
661 if ( t ) {
662 gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
663 }
664 }
666 return b;
667 }
670 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
671 {
672 SPAction* targetAction = SP_ACTION(user_data);
673 if ( targetAction ) {
674 sp_action_perform( targetAction, NULL );
675 }
676 }
678 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
679 {
680 if ( data ) {
681 GtkAction* act = GTK_ACTION(data);
682 gtk_action_set_sensitive( act, sensitive );
683 }
684 }
686 static SPActionEventVector action_event_vector = {
687 {NULL},
688 NULL,
689 NULL,
690 sp_action_action_set_sensitive,
691 NULL,
692 NULL
693 };
695 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
696 {
697 GtkAction* act = 0;
699 SPAction* targetAction = verb->get_action(view);
700 InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size );
701 act = GTK_ACTION(inky);
702 gtk_action_set_sensitive( act, targetAction->sensitive );
704 g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
706 SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
707 nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
709 return act;
710 }
712 Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
713 {
714 Inkscape::UI::View::View *view = desktop;
715 gint verbsToUse[] = {
716 // disabled until we have icons for them:
717 //find
718 //SP_VERB_EDIT_TILE,
719 //SP_VERB_EDIT_UNTILE,
720 SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
721 SP_VERB_DIALOG_DISPLAY,
722 SP_VERB_DIALOG_FILL_STROKE,
723 SP_VERB_DIALOG_NAMEDVIEW,
724 SP_VERB_DIALOG_TEXT,
725 SP_VERB_DIALOG_XML_EDITOR,
726 SP_VERB_DIALOG_LAYERS,
727 SP_VERB_EDIT_CLONE,
728 SP_VERB_EDIT_COPY,
729 SP_VERB_EDIT_CUT,
730 SP_VERB_EDIT_DUPLICATE,
731 SP_VERB_EDIT_PASTE,
732 SP_VERB_EDIT_REDO,
733 SP_VERB_EDIT_UNDO,
734 SP_VERB_EDIT_UNLINK_CLONE,
735 SP_VERB_FILE_EXPORT,
736 SP_VERB_FILE_IMPORT,
737 SP_VERB_FILE_NEW,
738 SP_VERB_FILE_OPEN,
739 SP_VERB_FILE_PRINT,
740 SP_VERB_FILE_SAVE,
741 SP_VERB_OBJECT_TO_CURVE,
742 SP_VERB_SELECTION_GROUP,
743 SP_VERB_SELECTION_OUTLINE,
744 SP_VERB_SELECTION_UNGROUP,
745 SP_VERB_ZOOM_1_1,
746 SP_VERB_ZOOM_1_2,
747 SP_VERB_ZOOM_2_1,
748 SP_VERB_ZOOM_DRAWING,
749 SP_VERB_ZOOM_IN,
750 SP_VERB_ZOOM_NEXT,
751 SP_VERB_ZOOM_OUT,
752 SP_VERB_ZOOM_PAGE,
753 SP_VERB_ZOOM_PAGE_WIDTH,
754 SP_VERB_ZOOM_PREV,
755 SP_VERB_ZOOM_SELECTION,
756 };
758 Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
760 static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
761 Glib::RefPtr<Gtk::ActionGroup> mainActions;
762 if ( groups.find(desktop) != groups.end() ) {
763 mainActions = groups[desktop];
764 }
766 if ( !mainActions ) {
767 mainActions = Gtk::ActionGroup::create("main");
768 groups[desktop] = mainActions;
769 }
771 for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
772 Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
773 if ( verb ) {
774 if (!mainActions->get_action(verb->get_id())) {
775 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
776 mainActions->add(Glib::wrap(act));
777 }
778 }
779 }
781 if ( !mainActions->get_action("ToolZoom") ) {
782 GtkTooltips *tt = gtk_tooltips_new();
783 for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
784 Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
785 if ( va ) {
786 mainActions->add(va);
787 if ( i == 0 ) {
788 va->set_active(true);
789 }
790 }
791 }
792 }
795 return mainActions;
796 }
799 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
800 {
801 gtk_widget_set_size_request( widget,
802 widget->allocation.width,
803 widget->allocation.height );
804 }
806 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
807 {
808 gtk_widget_set_size_request( widget, -1, -1 );
809 }
813 GtkWidget *
814 sp_tool_toolbox_new()
815 {
816 GtkTooltips *tt = gtk_tooltips_new();
817 GtkWidget* tb = gtk_toolbar_new();
818 gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
819 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
821 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
822 g_object_set_data(G_OBJECT(tb), "tooltips", tt);
824 gtk_widget_set_sensitive(tb, FALSE);
826 GtkWidget *hb = gtk_handle_box_new();
827 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
828 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
829 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
831 gtk_container_add(GTK_CONTAINER(hb), tb);
832 gtk_widget_show(GTK_WIDGET(tb));
834 sigc::connection* conn = new sigc::connection;
835 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
837 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
838 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
840 return hb;
841 }
843 GtkWidget *
844 sp_aux_toolbox_new()
845 {
846 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
848 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
850 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
852 gtk_widget_set_sensitive(tb, FALSE);
854 GtkWidget *hb = gtk_handle_box_new();
855 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
856 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
857 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
859 gtk_container_add(GTK_CONTAINER(hb), tb);
860 gtk_widget_show(GTK_WIDGET(tb));
862 sigc::connection* conn = new sigc::connection;
863 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
865 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
866 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
868 return hb;
869 }
871 //####################################
872 //# Commands Bar
873 //####################################
875 GtkWidget *
876 sp_commands_toolbox_new()
877 {
878 GtkWidget *tb = gtk_toolbar_new();
880 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
881 gtk_widget_set_sensitive(tb, FALSE);
883 GtkWidget *hb = gtk_handle_box_new();
884 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
885 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
886 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
888 gtk_container_add(GTK_CONTAINER(hb), tb);
889 gtk_widget_show(GTK_WIDGET(tb));
891 sigc::connection* conn = new sigc::connection;
892 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
894 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
895 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
897 return hb;
898 }
900 GtkWidget *
901 sp_snap_toolbox_new()
902 {
903 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
904 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
905 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
907 //GtkWidget *tb = gtk_toolbar_new();
908 //g_object_set_data(G_OBJECT(tb), "desktop", NULL);
910 gtk_widget_set_sensitive(tb, FALSE);
912 GtkWidget *hb = gtk_handle_box_new();
913 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
914 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
915 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
917 gtk_container_add(GTK_CONTAINER(hb), tb);
918 gtk_widget_show(GTK_WIDGET(tb));
920 sigc::connection* conn = new sigc::connection;
921 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
923 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
924 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
926 return hb;
927 }
929 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
930 gchar const *label, gchar const *shortLabel, gchar const *tooltip,
931 Glib::ustring const &path, gdouble def,
932 GtkWidget *focusTarget,
933 GtkWidget *us,
934 GObject *dataKludge,
935 gboolean altx, gchar const *altx_mark,
936 gdouble lower, gdouble upper, gdouble step, gdouble page,
937 gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
938 void (*callback)(GtkAdjustment *, GObject *),
939 gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
940 {
941 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
942 GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs->getDouble(path, def) * factor,
943 lower, upper, step, page, 0 ) );
944 if (us) {
945 sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
946 }
948 gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
950 EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
951 if ( shortLabel ) {
952 g_object_set( act, "short_label", shortLabel, NULL );
953 }
955 if ( (descrCount > 0) && descrLabels && descrValues ) {
956 ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
957 }
959 if ( focusTarget ) {
960 ege_adjustment_action_set_focuswidget( act, focusTarget );
961 }
963 if ( altx && altx_mark ) {
964 g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
965 }
967 if ( dataKludge ) {
968 // Rather lame, but it's the only place where we need to get the entry name
969 // but we don't have an Entry
970 g_object_set_data( dataKludge, prefs->getEntry(path).getEntryName().data(), adj );
971 }
973 // Using a cast just to make sure we pass in the right kind of function pointer
974 g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
976 return act;
977 }
980 //####################################
981 //# node editing callbacks
982 //####################################
984 /**
985 * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
986 */
987 static ShapeEditor *get_current_shape_editor()
988 {
989 if (!SP_ACTIVE_DESKTOP) {
990 return NULL;
991 }
993 SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
995 if (!SP_IS_NODE_CONTEXT(event_context)) {
996 return NULL;
997 }
999 return event_context->shape_editor;
1000 }
1003 void
1004 sp_node_path_edit_add(void)
1005 {
1006 ShapeEditor *shape_editor = get_current_shape_editor();
1007 if (shape_editor) shape_editor->add_node();
1008 }
1010 void
1011 sp_node_path_edit_delete(void)
1012 {
1013 ShapeEditor *shape_editor = get_current_shape_editor();
1014 if (shape_editor) shape_editor->delete_nodes_preserving_shape();
1015 }
1017 void
1018 sp_node_path_edit_delete_segment(void)
1019 {
1020 ShapeEditor *shape_editor = get_current_shape_editor();
1021 if (shape_editor) shape_editor->delete_segment();
1022 }
1024 void
1025 sp_node_path_edit_break(void)
1026 {
1027 ShapeEditor *shape_editor = get_current_shape_editor();
1028 if (shape_editor) shape_editor->break_at_nodes();
1029 }
1031 void
1032 sp_node_path_edit_join(void)
1033 {
1034 ShapeEditor *shape_editor = get_current_shape_editor();
1035 if (shape_editor) shape_editor->join_nodes();
1036 }
1038 void
1039 sp_node_path_edit_join_segment(void)
1040 {
1041 ShapeEditor *shape_editor = get_current_shape_editor();
1042 if (shape_editor) shape_editor->join_segments();
1043 }
1045 void
1046 sp_node_path_edit_toline(void)
1047 {
1048 ShapeEditor *shape_editor = get_current_shape_editor();
1049 if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1050 }
1052 void
1053 sp_node_path_edit_tocurve(void)
1054 {
1055 ShapeEditor *shape_editor = get_current_shape_editor();
1056 if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1057 }
1059 void
1060 sp_node_path_edit_cusp(void)
1061 {
1062 ShapeEditor *shape_editor = get_current_shape_editor();
1063 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1064 }
1066 void
1067 sp_node_path_edit_smooth(void)
1068 {
1069 ShapeEditor *shape_editor = get_current_shape_editor();
1070 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1071 }
1073 void
1074 sp_node_path_edit_symmetrical(void)
1075 {
1076 ShapeEditor *shape_editor = get_current_shape_editor();
1077 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1078 }
1080 void
1081 sp_node_path_edit_auto(void)
1082 {
1083 ShapeEditor *shape_editor = get_current_shape_editor();
1084 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_AUTO);
1085 }
1087 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1088 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1089 bool show = gtk_toggle_action_get_active( act );
1090 prefs->setBool("/tools/nodes/show_handles", show);
1091 ShapeEditor *shape_editor = get_current_shape_editor();
1092 if (shape_editor) shape_editor->show_handles(show);
1093 }
1095 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1096 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1097 bool show = gtk_toggle_action_get_active( act );
1098 prefs->setBool("/tools/nodes/show_helperpath", show);
1099 ShapeEditor *shape_editor = get_current_shape_editor();
1100 if (shape_editor) shape_editor->show_helperpath(show);
1101 }
1103 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1104 sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1105 }
1107 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1108 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1109 }
1111 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1112 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1113 }
1115 /* is called when the node selection is modified */
1116 static void
1117 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1118 {
1119 GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1120 GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1121 GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1122 GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1124 // quit if run by the attr_changed listener
1125 if (g_object_get_data( tbl, "freeze" )) {
1126 return;
1127 }
1129 // in turn, prevent listener from responding
1130 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1132 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1133 SPUnit const *unit = tracker->getActiveUnit();
1135 ShapeEditor *shape_editor = get_current_shape_editor();
1136 if (shape_editor && shape_editor->has_nodepath()) {
1137 Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1138 int n_selected = 0;
1139 if (nodepath) {
1140 n_selected = nodepath->numSelected();
1141 }
1143 if (n_selected == 0) {
1144 gtk_action_set_sensitive(xact, FALSE);
1145 gtk_action_set_sensitive(yact, FALSE);
1146 } else {
1147 gtk_action_set_sensitive(xact, TRUE);
1148 gtk_action_set_sensitive(yact, TRUE);
1149 Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1150 Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1152 if (n_selected == 1) {
1153 Geom::Point sel_node = nodepath->singleSelectedCoords();
1154 if (oldx != sel_node[Geom::X] || oldy != sel_node[Geom::Y]) {
1155 gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[Geom::X], *unit));
1156 gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[Geom::Y], *unit));
1157 }
1158 } else {
1159 boost::optional<Geom::Coord> x = sp_node_selected_common_coord(nodepath, Geom::X);
1160 boost::optional<Geom::Coord> y = sp_node_selected_common_coord(nodepath, Geom::Y);
1161 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1162 /* Note: Currently x and y will always have a value, even if the coordinates of the
1163 selected nodes don't coincide (in this case we use the coordinates of the center
1164 of the bounding box). So the entries are never set to zero. */
1165 // FIXME: Maybe we should clear the entry if several nodes are selected
1166 // instead of providing a kind of average value
1167 gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1168 gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1169 }
1170 }
1171 }
1172 } else {
1173 // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1174 gtk_action_set_sensitive(xact, FALSE);
1175 gtk_action_set_sensitive(yact, FALSE);
1176 }
1178 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1179 }
1181 static void
1182 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1183 {
1184 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1185 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1187 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1188 SPUnit const *unit = tracker->getActiveUnit();
1190 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1191 prefs->setDouble(Glib::ustring("/tools/nodes/") + value_name, sp_units_get_pixels(adj->value, *unit));
1192 }
1194 // quit if run by the attr_changed listener
1195 if (g_object_get_data( tbl, "freeze" )) {
1196 return;
1197 }
1199 // in turn, prevent listener from responding
1200 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1202 ShapeEditor *shape_editor = get_current_shape_editor();
1203 if (shape_editor && shape_editor->has_nodepath()) {
1204 double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1205 if (!strcmp(value_name, "x")) {
1206 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::X);
1207 }
1208 if (!strcmp(value_name, "y")) {
1209 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::Y);
1210 }
1211 }
1213 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1214 }
1216 static void
1217 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1218 {
1219 sp_node_path_value_changed(adj, tbl, "x");
1220 }
1222 static void
1223 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1224 {
1225 sp_node_path_value_changed(adj, tbl, "y");
1226 }
1228 void
1229 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1230 {
1231 {
1232 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1233 SPItem *item = selection->singleItem();
1234 if (item && SP_IS_LPE_ITEM(item)) {
1235 if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1236 gtk_action_set_sensitive(w, TRUE);
1237 } else {
1238 gtk_action_set_sensitive(w, FALSE);
1239 }
1240 } else {
1241 gtk_action_set_sensitive(w, FALSE);
1242 }
1243 }
1245 {
1246 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1247 SPItem *item = selection->singleItem();
1248 if (item && item->clip_ref && item->clip_ref->getObject()) {
1249 gtk_action_set_sensitive(w, TRUE);
1250 } else {
1251 gtk_action_set_sensitive(w, FALSE);
1252 }
1253 }
1255 {
1256 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1257 SPItem *item = selection->singleItem();
1258 if (item && item->mask_ref && item->mask_ref->getObject()) {
1259 gtk_action_set_sensitive(w, TRUE);
1260 } else {
1261 gtk_action_set_sensitive(w, FALSE);
1262 }
1263 }
1264 }
1266 void
1267 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1268 {
1269 sp_node_toolbox_sel_changed (selection, tbl);
1270 }
1274 //################################
1275 //## Node Editing Toolbox ##
1276 //################################
1278 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1279 {
1280 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1281 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1282 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1283 g_object_set_data( holder, "tracker", tracker );
1285 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
1287 {
1288 InkAction* inky = ink_action_new( "NodeInsertAction",
1289 _("Insert node"),
1290 _("Insert new nodes into selected segments"),
1291 INKSCAPE_ICON_NODE_ADD,
1292 secondarySize );
1293 g_object_set( inky, "short_label", _("Insert"), NULL );
1294 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1295 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1296 }
1298 {
1299 InkAction* inky = ink_action_new( "NodeDeleteAction",
1300 _("Delete node"),
1301 _("Delete selected nodes"),
1302 INKSCAPE_ICON_NODE_DELETE,
1303 secondarySize );
1304 g_object_set( inky, "short_label", _("Delete"), NULL );
1305 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1306 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1307 }
1309 {
1310 InkAction* inky = ink_action_new( "NodeJoinAction",
1311 _("Join endnodes"),
1312 _("Join selected endnodes"),
1313 INKSCAPE_ICON_NODE_JOIN,
1314 secondarySize );
1315 g_object_set( inky, "short_label", _("Join"), NULL );
1316 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1317 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1318 }
1320 {
1321 InkAction* inky = ink_action_new( "NodeBreakAction",
1322 _("Break nodes"),
1323 _("Break path at selected nodes"),
1324 INKSCAPE_ICON_NODE_BREAK,
1325 secondarySize );
1326 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1327 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1328 }
1331 {
1332 InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1333 _("Join with segment"),
1334 _("Join selected endnodes with a new segment"),
1335 INKSCAPE_ICON_NODE_JOIN_SEGMENT,
1336 secondarySize );
1337 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1338 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1339 }
1341 {
1342 InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1343 _("Delete segment"),
1344 _("Delete segment between two non-endpoint nodes"),
1345 INKSCAPE_ICON_NODE_DELETE_SEGMENT,
1346 secondarySize );
1347 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1348 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1349 }
1351 {
1352 InkAction* inky = ink_action_new( "NodeCuspAction",
1353 _("Node Cusp"),
1354 _("Make selected nodes corner"),
1355 INKSCAPE_ICON_NODE_TYPE_CUSP,
1356 secondarySize );
1357 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1358 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1359 }
1361 {
1362 InkAction* inky = ink_action_new( "NodeSmoothAction",
1363 _("Node Smooth"),
1364 _("Make selected nodes smooth"),
1365 INKSCAPE_ICON_NODE_TYPE_SMOOTH,
1366 secondarySize );
1367 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1368 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1369 }
1371 {
1372 InkAction* inky = ink_action_new( "NodeSymmetricAction",
1373 _("Node Symmetric"),
1374 _("Make selected nodes symmetric"),
1375 INKSCAPE_ICON_NODE_TYPE_SYMMETRIC,
1376 secondarySize );
1377 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1378 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1379 }
1381 {
1382 InkAction* inky = ink_action_new( "NodeAutoAction",
1383 _("Node Auto"),
1384 _("Make selected nodes auto-smooth"),
1385 INKSCAPE_ICON_NODE_TYPE_AUTO_SMOOTH,
1386 secondarySize );
1387 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_auto), 0 );
1388 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1389 }
1391 {
1392 InkAction* inky = ink_action_new( "NodeLineAction",
1393 _("Node Line"),
1394 _("Make selected segments lines"),
1395 INKSCAPE_ICON_NODE_SEGMENT_LINE,
1396 secondarySize );
1397 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1398 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1399 }
1401 {
1402 InkAction* inky = ink_action_new( "NodeCurveAction",
1403 _("Node Curve"),
1404 _("Make selected segments curves"),
1405 INKSCAPE_ICON_NODE_SEGMENT_CURVE,
1406 secondarySize );
1407 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1408 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1409 }
1411 {
1412 InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1413 _("Show Handles"),
1414 _("Show the Bezier handles of selected nodes"),
1415 INKSCAPE_ICON_SHOW_NODE_HANDLES,
1416 Inkscape::ICON_SIZE_DECORATION );
1417 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1418 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1419 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_handles", true) );
1420 }
1422 {
1423 InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1424 _("Show Outline"),
1425 _("Show the outline of the path"),
1426 INKSCAPE_ICON_SHOW_PATH_OUTLINE,
1427 Inkscape::ICON_SIZE_DECORATION );
1428 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1429 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1430 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_helperpath", false) );
1431 }
1433 {
1434 InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1435 _("Next path effect parameter"),
1436 _("Show next path effect parameter for editing"),
1437 INKSCAPE_ICON_PATH_EFFECT_PARAMETER_NEXT,
1438 Inkscape::ICON_SIZE_DECORATION );
1439 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1440 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1441 g_object_set_data( holder, "nodes_lpeedit", inky);
1442 }
1444 {
1445 InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1446 _("Edit clipping path"),
1447 _("Edit the clipping path of the object"),
1448 INKSCAPE_ICON_PATH_CLIP_EDIT,
1449 Inkscape::ICON_SIZE_DECORATION );
1450 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1451 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1452 g_object_set_data( holder, "nodes_clippathedit", inky);
1453 }
1455 {
1456 InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1457 _("Edit mask path"),
1458 _("Edit the mask of the object"),
1459 INKSCAPE_ICON_PATH_MASK_EDIT,
1460 Inkscape::ICON_SIZE_DECORATION );
1461 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1462 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1463 g_object_set_data( holder, "nodes_maskedit", inky);
1464 }
1466 /* X coord of selected node(s) */
1467 {
1468 EgeAdjustmentAction* eact = 0;
1469 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1470 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1471 eact = create_adjustment_action( "NodeXAction",
1472 _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1473 "/tools/nodes/Xcoord", 0,
1474 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1475 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1476 labels, values, G_N_ELEMENTS(labels),
1477 sp_node_path_x_value_changed );
1478 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1479 g_object_set_data( holder, "nodes_x_action", eact );
1480 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1481 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1482 }
1484 /* Y coord of selected node(s) */
1485 {
1486 EgeAdjustmentAction* eact = 0;
1487 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1488 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1489 eact = create_adjustment_action( "NodeYAction",
1490 _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1491 "/tools/nodes/Ycoord", 0,
1492 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1493 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1494 labels, values, G_N_ELEMENTS(labels),
1495 sp_node_path_y_value_changed );
1496 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1497 g_object_set_data( holder, "nodes_y_action", eact );
1498 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1499 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1500 }
1502 // add the units menu
1503 {
1504 GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1505 gtk_action_group_add_action( mainActions, act );
1506 }
1509 sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1511 //watch selection
1512 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1514 sigc::connection *c_selection_changed =
1515 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1516 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1517 pool->add_connection ("selection-changed", c_selection_changed);
1519 sigc::connection *c_selection_modified =
1520 new sigc::connection (sp_desktop_selection (desktop)->connectModified
1521 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1522 pool->add_connection ("selection-modified", c_selection_modified);
1524 sigc::connection *c_subselection_changed =
1525 new sigc::connection (desktop->connectToolSubselectionChanged
1526 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1527 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1529 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1531 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1532 } // end of sp_node_toolbox_prep()
1535 //########################
1536 //## Zoom Toolbox ##
1537 //########################
1539 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1540 {
1541 // no custom GtkAction setup needed
1542 } // end of sp_zoom_toolbox_prep()
1544 void
1545 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1546 {
1547 toolbox_set_desktop(toolbox,
1548 desktop,
1549 setup_tool_toolbox,
1550 update_tool_toolbox,
1551 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1552 "event_context_connection")));
1553 }
1556 void
1557 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1558 {
1559 toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1560 desktop,
1561 setup_aux_toolbox,
1562 update_aux_toolbox,
1563 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1564 "event_context_connection")));
1565 }
1567 void
1568 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1569 {
1570 toolbox_set_desktop(toolbox,
1571 desktop,
1572 setup_commands_toolbox,
1573 update_commands_toolbox,
1574 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1575 "event_context_connection")));
1576 }
1578 void
1579 sp_snap_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1580 {
1581 toolbox_set_desktop(toolbox,
1582 desktop,
1583 setup_snap_toolbox,
1584 update_snap_toolbox,
1585 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1586 "event_context_connection")));
1587 }
1590 static void
1591 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1592 {
1593 gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1594 SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1596 if (old_desktop) {
1597 GList *children, *iter;
1599 children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1600 for ( iter = children ; iter ; iter = iter->next ) {
1601 gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1602 }
1603 g_list_free(children);
1604 }
1606 g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1608 if (desktop) {
1609 gtk_widget_set_sensitive(toolbox, TRUE);
1610 setup_func(toolbox, desktop);
1611 update_func(desktop, desktop->event_context, toolbox);
1612 *conn = desktop->connectEventContextChanged
1613 (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1614 } else {
1615 gtk_widget_set_sensitive(toolbox, FALSE);
1616 }
1618 } // end of toolbox_set_desktop()
1621 static void
1622 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1623 {
1624 gchar const * descr =
1625 "<ui>"
1626 " <toolbar name='ToolToolbar'>"
1627 " <toolitem action='ToolSelector' />"
1628 " <toolitem action='ToolNode' />"
1629 " <toolitem action='ToolTweak' />"
1630 " <toolitem action='ToolZoom' />"
1631 " <toolitem action='ToolRect' />"
1632 " <toolitem action='Tool3DBox' />"
1633 " <toolitem action='ToolArc' />"
1634 " <toolitem action='ToolStar' />"
1635 " <toolitem action='ToolSpiral' />"
1636 " <toolitem action='ToolPencil' />"
1637 " <toolitem action='ToolPen' />"
1638 " <toolitem action='ToolCalligraphic' />"
1639 " <toolitem action='ToolEraser' />"
1640 // " <toolitem action='ToolLPETool' />"
1641 " <toolitem action='ToolPaintBucket' />"
1642 " <toolitem action='ToolText' />"
1643 " <toolitem action='ToolConnector' />"
1644 " <toolitem action='ToolGradient' />"
1645 " <toolitem action='ToolDropper' />"
1646 " </toolbar>"
1647 "</ui>";
1648 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1649 GtkUIManager* mgr = gtk_ui_manager_new();
1650 GError* errVal = 0;
1651 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1653 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1654 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1656 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" );
1657 if ( prefs->getBool("/toolbox/icononly", true) ) {
1658 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1659 }
1660 Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
1661 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1663 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
1664 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1666 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1668 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1669 if ( child ) {
1670 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1671 }
1673 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1674 // Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
1675 }
1678 static void
1679 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1680 {
1681 gchar const *const tname = ( eventcontext
1682 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1683 : NULL );
1684 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1686 for (int i = 0 ; tools[i].type_name ; i++ ) {
1687 Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1688 if ( act ) {
1689 bool setActive = tname && !strcmp(tname, tools[i].type_name);
1690 Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1691 if ( verbAct ) {
1692 verbAct->set_active(setActive);
1693 }
1694 }
1695 }
1696 }
1698 static void
1699 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1700 {
1701 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1702 GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1703 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1704 GtkUIManager* mgr = gtk_ui_manager_new();
1705 GError* errVal = 0;
1706 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1707 gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1709 std::map<std::string, GtkWidget*> dataHolders;
1711 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1712 if ( aux_toolboxes[i].prep_func ) {
1713 // converted to GtkActions and UIManager
1715 GtkWidget* kludge = gtk_toolbar_new();
1716 g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1717 g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1718 dataHolders[aux_toolboxes[i].type_name] = kludge;
1719 aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1720 } else {
1722 GtkWidget *sub_toolbox = 0;
1723 if (aux_toolboxes[i].create_func == NULL)
1724 sub_toolbox = sp_empty_toolbox_new(desktop);
1725 else {
1726 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1727 }
1729 gtk_size_group_add_widget( grouper, sub_toolbox );
1731 gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1732 g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1734 }
1735 }
1737 // Second pass to create toolbars *after* all GtkActions are created
1738 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1739 if ( aux_toolboxes[i].prep_func ) {
1740 // converted to GtkActions and UIManager
1742 GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1744 GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1745 gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1747 gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1748 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1749 g_free( tmp );
1750 tmp = 0;
1752 if ( prefs->getBool( "/toolbox/icononly", true) ) {
1753 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1754 }
1756 Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
1757 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1759 gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1761 if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1762 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1763 swatch->setDesktop( desktop );
1764 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1765 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1766 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1767 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 );
1768 }
1770 gtk_widget_show_all( holder );
1771 sp_set_font_size_smaller( holder );
1773 gtk_size_group_add_widget( grouper, holder );
1775 gtk_container_add( GTK_CONTAINER(toolbox), holder );
1776 g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1777 }
1778 }
1780 g_object_unref( G_OBJECT(grouper) );
1781 }
1783 static void
1784 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1785 {
1786 gchar const *tname = ( eventcontext
1787 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1788 : NULL );
1789 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1790 GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1791 if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1792 gtk_widget_show_all(sub_toolbox);
1793 g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1794 } else {
1795 gtk_widget_hide(sub_toolbox);
1796 }
1797 }
1798 }
1800 static void
1801 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1802 {
1803 gchar const * descr =
1804 "<ui>"
1805 " <toolbar name='CommandsToolbar'>"
1806 " <toolitem action='FileNew' />"
1807 " <toolitem action='FileOpen' />"
1808 " <toolitem action='FileSave' />"
1809 " <toolitem action='FilePrint' />"
1810 " <separator />"
1811 " <toolitem action='FileImport' />"
1812 " <toolitem action='FileExport' />"
1813 " <separator />"
1814 " <toolitem action='EditUndo' />"
1815 " <toolitem action='EditRedo' />"
1816 " <separator />"
1817 " <toolitem action='EditCopy' />"
1818 " <toolitem action='EditCut' />"
1819 " <toolitem action='EditPaste' />"
1820 " <separator />"
1821 " <toolitem action='ZoomSelection' />"
1822 " <toolitem action='ZoomDrawing' />"
1823 " <toolitem action='ZoomPage' />"
1824 " <separator />"
1825 " <toolitem action='EditDuplicate' />"
1826 " <toolitem action='EditClone' />"
1827 " <toolitem action='EditUnlinkClone' />"
1828 " <separator />"
1829 " <toolitem action='SelectionGroup' />"
1830 " <toolitem action='SelectionUnGroup' />"
1831 " <separator />"
1832 " <toolitem action='DialogFillStroke' />"
1833 " <toolitem action='DialogText' />"
1834 " <toolitem action='DialogLayers' />"
1835 " <toolitem action='DialogXMLEditor' />"
1836 " <toolitem action='DialogAlignDistribute' />"
1837 " <separator />"
1838 " <toolitem action='DialogPreferences' />"
1839 " <toolitem action='DialogDocumentProperties' />"
1840 " </toolbar>"
1841 "</ui>";
1842 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1843 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1845 GtkUIManager* mgr = gtk_ui_manager_new();
1846 GError* errVal = 0;
1848 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1849 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1851 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1852 if ( prefs->getBool("/toolbox/icononly", true) ) {
1853 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1854 }
1856 Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
1857 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1859 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1860 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1863 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1865 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1866 if ( child ) {
1867 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1868 }
1870 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1871 }
1873 static void
1874 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1875 {
1876 }
1878 void toggle_snap_callback (GtkToggleAction *act, gpointer data) { //data points to the toolbox
1880 if (g_object_get_data(G_OBJECT(data), "freeze" )) {
1881 return;
1882 }
1884 gpointer ptr = g_object_get_data(G_OBJECT(data), "desktop");
1885 g_assert(ptr != NULL);
1887 SPDesktop *dt = reinterpret_cast<SPDesktop*>(ptr);
1888 SPNamedView *nv = sp_desktop_namedview(dt);
1889 SPDocument *doc = SP_OBJECT_DOCUMENT(nv);
1891 if (dt == NULL || nv == NULL) {
1892 g_warning("No desktop or namedview specified (in toggle_snap_callback)!");
1893 return;
1894 }
1896 Inkscape::XML::Node *repr = SP_OBJECT_REPR(nv);
1898 if (repr == NULL) {
1899 g_warning("This namedview doesn't have a xml representation attached!");
1900 return;
1901 }
1903 bool saved = sp_document_get_undo_sensitive(doc);
1904 sp_document_set_undo_sensitive(doc, false);
1906 bool v = false;
1907 SPAttributeEnum attr = (SPAttributeEnum) GPOINTER_TO_INT(g_object_get_data(G_OBJECT(act), "SP_ATTR_INKSCAPE"));
1909 switch (attr) {
1910 case SP_ATTR_INKSCAPE_SNAP_GLOBAL:
1911 dt->toggleSnapGlobal();
1912 break;
1913 case SP_ATTR_INKSCAPE_SNAP_BBOX:
1914 v = nv->snap_manager.snapprefs.getSnapModeBBox();
1915 sp_repr_set_boolean(repr, "inkscape:snap-bbox", !v);
1916 break;
1917 case SP_ATTR_INKSCAPE_BBOX_PATHS:
1918 v = nv->snap_manager.snapprefs.getSnapToBBoxPath();
1919 sp_repr_set_boolean(repr, "inkscape:bbox-paths", !v);
1920 break;
1921 case SP_ATTR_INKSCAPE_BBOX_NODES:
1922 v = nv->snap_manager.snapprefs.getSnapToBBoxNode();
1923 sp_repr_set_boolean(repr, "inkscape:bbox-nodes", !v);
1924 break;
1925 case SP_ATTR_INKSCAPE_SNAP_NODES:
1926 v = nv->snap_manager.snapprefs.getSnapModeNode();
1927 sp_repr_set_boolean(repr, "inkscape:snap-nodes", !v);
1928 break;
1929 case SP_ATTR_INKSCAPE_OBJECT_PATHS:
1930 v = nv->snap_manager.snapprefs.getSnapToItemPath();
1931 sp_repr_set_boolean(repr, "inkscape:object-paths", !v);
1932 break;
1933 case SP_ATTR_INKSCAPE_OBJECT_NODES:
1934 v = nv->snap_manager.snapprefs.getSnapToItemNode();
1935 sp_repr_set_boolean(repr, "inkscape:object-nodes", !v);
1936 break;
1937 case SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES:
1938 v = nv->snap_manager.snapprefs.getSnapSmoothNodes();
1939 sp_repr_set_boolean(repr, "inkscape:snap-smooth-nodes", !v);
1940 break;
1941 case SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS:
1942 v = nv->snap_manager.snapprefs.getSnapIntersectionCS();
1943 sp_repr_set_boolean(repr, "inkscape:snap-intersection-paths", !v);
1944 break;
1945 case SP_ATTR_INKSCAPE_SNAP_CENTER:
1946 v = nv->snap_manager.snapprefs.getIncludeItemCenter();
1947 sp_repr_set_boolean(repr, "inkscape:snap-center", !v);
1948 break;
1949 case SP_ATTR_INKSCAPE_SNAP_GRIDS:
1950 v = nv->snap_manager.snapprefs.getSnapToGrids();
1951 sp_repr_set_boolean(repr, "inkscape:snap-grids", !v);
1952 break;
1953 case SP_ATTR_INKSCAPE_SNAP_TO_GUIDES:
1954 v = nv->snap_manager.snapprefs.getSnapToGuides();
1955 sp_repr_set_boolean(repr, "inkscape:snap-to-guides", !v);
1956 break;
1957 case SP_ATTR_INKSCAPE_SNAP_PAGE:
1958 v = nv->snap_manager.snapprefs.getSnapToPageBorder();
1959 sp_repr_set_boolean(repr, "inkscape:snap-page", !v);
1960 break;
1961 /*case SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE:
1962 v = nv->snap_manager.snapprefs.getSnapIntersectionGG();
1963 sp_repr_set_boolean(repr, "inkscape:snap-intersection-grid-guide", !v);
1964 break;*/
1965 case SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS:
1966 v = nv->snap_manager.snapprefs.getSnapLineMidpoints();
1967 sp_repr_set_boolean(repr, "inkscape:snap-midpoints", !v);
1968 break;
1969 case SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS:
1970 v = nv->snap_manager.snapprefs.getSnapObjectMidpoints();
1971 sp_repr_set_boolean(repr, "inkscape:snap-object-midpoints", !v);
1972 break;
1973 case SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS:
1974 v = nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints();
1975 sp_repr_set_boolean(repr, "inkscape:snap-bbox-edge-midpoints", !v);
1976 break;
1977 case SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS:
1978 v = nv->snap_manager.snapprefs.getSnapBBoxMidpoints();
1979 sp_repr_set_boolean(repr, "inkscape:snap-bbox-midpoints", !v);
1980 break;
1981 default:
1982 g_warning("toggle_snap_callback has been called with an ID for which no action has been defined");
1983 break;
1984 }
1986 // The snapping preferences are stored in the document, and therefore toggling makes the document dirty
1987 doc->setModifiedSinceSave();
1989 sp_document_set_undo_sensitive(doc, saved);
1990 }
1992 void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1993 {
1994 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1995 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
1997 gchar const * descr =
1998 "<ui>"
1999 " <toolbar name='SnapToolbar'>"
2000 " <toolitem action='ToggleSnapGlobal' />"
2001 " <separator />"
2002 " <toolitem action='ToggleSnapFromBBoxCorner' />"
2003 " <toolitem action='ToggleSnapToBBoxPath' />"
2004 " <toolitem action='ToggleSnapToBBoxNode' />"
2005 " <toolitem action='ToggleSnapToFromBBoxEdgeMidpoints' />"
2006 " <toolitem action='ToggleSnapToFromBBoxCenters' />"
2007 " <separator />"
2008 " <toolitem action='ToggleSnapFromNode' />"
2009 " <toolitem action='ToggleSnapToItemPath' />"
2010 " <toolitem action='ToggleSnapToPathIntersections' />"
2011 " <toolitem action='ToggleSnapToItemNode' />"
2012 " <toolitem action='ToggleSnapToSmoothNodes' />"
2013 " <toolitem action='ToggleSnapToFromLineMidpoints' />"
2014 " <toolitem action='ToggleSnapToFromObjectCenters' />"
2015 " <toolitem action='ToggleSnapToFromRotationCenter' />"
2016 " <separator />"
2017 " <toolitem action='ToggleSnapToPageBorder' />"
2018 " <toolitem action='ToggleSnapToGrids' />"
2019 " <toolitem action='ToggleSnapToGuides' />"
2020 //" <toolitem action='ToggleSnapToGridGuideIntersections' />"
2021 " </toolbar>"
2022 "</ui>";
2024 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
2026 {
2027 InkToggleAction* act = ink_toggle_action_new("ToggleSnapGlobal",
2028 _("Snap"), _("Enable snapping"), INKSCAPE_ICON_SNAP, secondarySize,
2029 SP_ATTR_INKSCAPE_SNAP_GLOBAL);
2031 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2032 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2033 }
2035 {
2036 InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromBBoxCorner",
2037 _("Bounding box"), _("Snap bounding box corners"), INKSCAPE_ICON_SNAP_BOUNDING_BOX,
2038 secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX);
2040 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2041 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2042 }
2044 {
2045 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxPath",
2046 _("Bounding box edges"), _("Snap to edges of a bounding box"),
2047 INKSCAPE_ICON_SNAP_BOUNDING_BOX_EDGES, secondarySize, SP_ATTR_INKSCAPE_BBOX_PATHS);
2049 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2050 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2051 }
2053 {
2054 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxNode",
2055 _("Bounding box corners"), _("Snap to bounding box corners"),
2056 INKSCAPE_ICON_SNAP_BOUNDING_BOX_CORNERS, secondarySize, SP_ATTR_INKSCAPE_BBOX_NODES);
2058 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2059 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2060 }
2062 {
2063 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxEdgeMidpoints",
2064 _("BBox Edge Midpoints"), _("Snap from and to midpoints of bounding box edges"),
2065 INKSCAPE_ICON_SNAP_BOUNDING_BOX_MIDPOINTS, secondarySize,
2066 SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS);
2068 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2069 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2070 }
2072 {
2073 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxCenters",
2074 _("BBox Centers"), _("Snapping from and to centers of bounding boxes"),
2075 INKSCAPE_ICON_SNAP_BOUNDING_BOX_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS);
2077 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2078 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2079 }
2081 {
2082 InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromNode",
2083 _("Nodes"), _("Snap nodes or handles"), INKSCAPE_ICON_SNAP_NODES, secondarySize, SP_ATTR_INKSCAPE_SNAP_NODES);
2085 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2086 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2087 }
2089 {
2090 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemPath",
2091 _("Paths"), _("Snap to paths"), INKSCAPE_ICON_SNAP_NODES_PATH, secondarySize,
2092 SP_ATTR_INKSCAPE_OBJECT_PATHS);
2094 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2095 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2096 }
2098 {
2099 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPathIntersections",
2100 _("Path intersections"), _("Snap to path intersections"),
2101 INKSCAPE_ICON_SNAP_NODES_INTERSECTION, secondarySize, SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS);
2103 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2104 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2105 }
2107 {
2108 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemNode",
2109 _("To nodes"), _("Snap to cusp nodes"), INKSCAPE_ICON_SNAP_NODES_CUSP, secondarySize,
2110 SP_ATTR_INKSCAPE_OBJECT_NODES);
2112 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2113 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2114 }
2116 {
2117 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToSmoothNodes",
2118 _("Smooth nodes"), _("Snap to smooth nodes"), INKSCAPE_ICON_SNAP_NODES_SMOOTH,
2119 secondarySize, SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES);
2121 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2122 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2123 }
2125 {
2126 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromLineMidpoints",
2127 _("Line Midpoints"), _("Snap from and to midpoints of line segments"),
2128 INKSCAPE_ICON_SNAP_NODES_MIDPOINT, secondarySize, SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS);
2130 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2131 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2132 }
2134 {
2135 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromObjectCenters",
2136 _("Object Centers"), _("Snap from and to centers of objects"),
2137 INKSCAPE_ICON_SNAP_NODES_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS);
2139 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2140 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2141 }
2143 {
2144 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromRotationCenter",
2145 _("Rotation Centers"), _("Snap from and to an item's rotation center"),
2146 INKSCAPE_ICON_SNAP_NODES_ROTATION_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_CENTER);
2148 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2149 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2150 }
2152 {
2153 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPageBorder",
2154 _("Page border"), _("Snap to the page border"), INKSCAPE_ICON_SNAP_PAGE,
2155 secondarySize, SP_ATTR_INKSCAPE_SNAP_PAGE);
2157 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2158 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2159 }
2161 {
2162 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGrids",
2163 _("Grids"), _("Snap to grids"), INKSCAPE_ICON_GRID_RECTANGULAR, secondarySize,
2164 SP_ATTR_INKSCAPE_SNAP_GRIDS);
2166 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2167 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2168 }
2170 {
2171 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGuides",
2172 _("Guides"), _("Snap to guides"), INKSCAPE_ICON_GUIDES, secondarySize,
2173 SP_ATTR_INKSCAPE_SNAP_TO_GUIDES);
2175 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2176 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2177 }
2179 /*{
2180 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGridGuideIntersections",
2181 _("Grid/guide intersections"), _("Snap to intersections of a grid with a guide"),
2182 INKSCAPE_ICON_SNAP_GRID_GUIDE_INTERSECTIONS, secondarySize,
2183 SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE);
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 GtkUIManager* mgr = gtk_ui_manager_new();
2190 GError* errVal = 0;
2192 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
2193 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
2195 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/SnapToolbar" );
2196 if ( prefs->getBool("/toolbox/icononly", true) ) {
2197 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
2198 }
2200 Inkscape::IconSize toolboxSize = prefToSize("/toolbox/secondary");
2201 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
2203 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
2204 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
2206 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
2208 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
2209 if ( child ) {
2210 gtk_container_remove( GTK_CONTAINER(toolbox), child );
2211 }
2213 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
2215 }
2217 void update_snap_toolbox(SPDesktop *desktop, SPEventContext */*eventcontext*/, GtkWidget *toolbox)
2218 {
2219 g_assert(desktop != NULL);
2220 g_assert(toolbox != NULL);
2222 SPNamedView *nv = sp_desktop_namedview(desktop);
2223 if (nv == NULL) {
2224 g_warning("Namedview cannot be retrieved (in update_snap_toolbox)!");
2225 return;
2226 }
2228 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2230 Glib::RefPtr<Gtk::Action> act1 = mainActions->get_action("ToggleSnapGlobal");
2231 Glib::RefPtr<Gtk::Action> act2 = mainActions->get_action("ToggleSnapFromBBoxCorner");
2232 Glib::RefPtr<Gtk::Action> act3 = mainActions->get_action("ToggleSnapToBBoxPath");
2233 Glib::RefPtr<Gtk::Action> act4 = mainActions->get_action("ToggleSnapToBBoxNode");
2234 Glib::RefPtr<Gtk::Action> act4b = mainActions->get_action("ToggleSnapToFromBBoxEdgeMidpoints");
2235 Glib::RefPtr<Gtk::Action> act4c = mainActions->get_action("ToggleSnapToFromBBoxCenters");
2236 Glib::RefPtr<Gtk::Action> act5 = mainActions->get_action("ToggleSnapFromNode");
2237 Glib::RefPtr<Gtk::Action> act6 = mainActions->get_action("ToggleSnapToItemPath");
2238 Glib::RefPtr<Gtk::Action> act6b = mainActions->get_action("ToggleSnapToPathIntersections");
2239 Glib::RefPtr<Gtk::Action> act7 = mainActions->get_action("ToggleSnapToItemNode");
2240 Glib::RefPtr<Gtk::Action> act8 = mainActions->get_action("ToggleSnapToSmoothNodes");
2241 Glib::RefPtr<Gtk::Action> act9 = mainActions->get_action("ToggleSnapToFromLineMidpoints");
2242 Glib::RefPtr<Gtk::Action> act10 = mainActions->get_action("ToggleSnapToFromObjectCenters");
2243 Glib::RefPtr<Gtk::Action> act11 = mainActions->get_action("ToggleSnapToFromRotationCenter");
2244 Glib::RefPtr<Gtk::Action> act12 = mainActions->get_action("ToggleSnapToPageBorder");
2245 //Glib::RefPtr<Gtk::Action> act13 = mainActions->get_action("ToggleSnapToGridGuideIntersections");
2246 Glib::RefPtr<Gtk::Action> act14 = mainActions->get_action("ToggleSnapToGrids");
2247 Glib::RefPtr<Gtk::Action> act15 = mainActions->get_action("ToggleSnapToGuides");
2250 if (!act1) {
2251 return; // The snap actions haven't been defined yet (might be the case during startup)
2252 }
2254 // The ..._set_active calls below will toggle the buttons, but this shouldn't lead to
2255 // changes in our document because we're only updating the UI;
2256 // Setting the "freeze" parameter to true will block the code in toggle_snap_callback()
2257 g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(TRUE));
2259 bool const c1 = nv->snap_manager.snapprefs.getSnapEnabledGlobally();
2260 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act1->gobj()), c1);
2262 bool const c2 = nv->snap_manager.snapprefs.getSnapModeBBox();
2263 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act2->gobj()), c2);
2264 gtk_action_set_sensitive(GTK_ACTION(act2->gobj()), c1);
2266 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act3->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxPath());
2267 gtk_action_set_sensitive(GTK_ACTION(act3->gobj()), c1 && c2);
2268 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxNode());
2269 gtk_action_set_sensitive(GTK_ACTION(act4->gobj()), c1 && c2);
2270 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4b->gobj()), nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints());
2271 gtk_action_set_sensitive(GTK_ACTION(act4b->gobj()), c1 && c2);
2272 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4c->gobj()), nv->snap_manager.snapprefs.getSnapBBoxMidpoints());
2273 gtk_action_set_sensitive(GTK_ACTION(act4c->gobj()), c1 && c2);
2275 bool const c3 = nv->snap_manager.snapprefs.getSnapModeNode();
2276 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act5->gobj()), c3);
2277 gtk_action_set_sensitive(GTK_ACTION(act5->gobj()), c1);
2279 bool const c4 = nv->snap_manager.snapprefs.getSnapToItemPath();
2280 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6->gobj()), c4);
2281 gtk_action_set_sensitive(GTK_ACTION(act6->gobj()), c1 && c3);
2282 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6b->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionCS());
2283 gtk_action_set_sensitive(GTK_ACTION(act6b->gobj()), c1 && c3 && c4);
2284 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act7->gobj()), nv->snap_manager.snapprefs.getSnapToItemNode());
2285 gtk_action_set_sensitive(GTK_ACTION(act7->gobj()), c1 && c3);
2286 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act8->gobj()), nv->snap_manager.snapprefs.getSnapSmoothNodes());
2287 gtk_action_set_sensitive(GTK_ACTION(act8->gobj()), c1 && c3);
2288 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act9->gobj()), nv->snap_manager.snapprefs.getSnapLineMidpoints());
2289 gtk_action_set_sensitive(GTK_ACTION(act9->gobj()), c1 && c3);
2290 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act10->gobj()), nv->snap_manager.snapprefs.getSnapObjectMidpoints());
2291 gtk_action_set_sensitive(GTK_ACTION(act10->gobj()), c1 && c3);
2292 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act11->gobj()), nv->snap_manager.snapprefs.getIncludeItemCenter());
2293 gtk_action_set_sensitive(GTK_ACTION(act11->gobj()), c1 && c3);
2295 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act12->gobj()), nv->snap_manager.snapprefs.getSnapToPageBorder());
2296 gtk_action_set_sensitive(GTK_ACTION(act12->gobj()), c1);
2297 //gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act13->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionGG());
2298 //gtk_action_set_sensitive(GTK_ACTION(act13->gobj()), c1);
2300 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act14->gobj()), nv->snap_manager.snapprefs.getSnapToGrids());
2301 gtk_action_set_sensitive(GTK_ACTION(act14->gobj()), c1);
2302 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act15->gobj()), nv->snap_manager.snapprefs.getSnapToGuides());
2303 gtk_action_set_sensitive(GTK_ACTION(act15->gobj()), c1);
2306 g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(FALSE)); // unfreeze (see above)
2307 }
2309 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
2310 {
2311 gtk_widget_show(toolbox_toplevel);
2312 GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
2314 GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
2315 if (!shown_toolbox) {
2316 return;
2317 }
2318 gtk_widget_show(toolbox);
2320 gtk_widget_show_all(shown_toolbox);
2321 }
2323 static GtkWidget *
2324 sp_empty_toolbox_new(SPDesktop *desktop)
2325 {
2326 GtkWidget *tbl = gtk_toolbar_new();
2327 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
2328 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
2330 gtk_widget_show_all(tbl);
2331 sp_set_font_size_smaller (tbl);
2333 return tbl;
2334 }
2336 #define MODE_LABEL_WIDTH 70
2338 //########################
2339 //## Star ##
2340 //########################
2342 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2343 {
2344 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2346 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2347 // do not remember prefs if this call is initiated by an undo change, because undoing object
2348 // creation sets bogus values to its attributes before it is deleted
2349 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2350 prefs->setInt("/tools/shapes/star/magnitude", (gint)adj->value);
2351 }
2353 // quit if run by the attr_changed listener
2354 if (g_object_get_data( dataKludge, "freeze" )) {
2355 return;
2356 }
2358 // in turn, prevent listener from responding
2359 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2361 bool modmade = false;
2363 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2364 GSList const *items = selection->itemList();
2365 for (; items != NULL; items = items->next) {
2366 if (SP_IS_STAR((SPItem *) items->data)) {
2367 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2368 sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
2369 sp_repr_set_svg_double(repr, "sodipodi:arg2",
2370 (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
2371 + M_PI / (gint)adj->value));
2372 SP_OBJECT((SPItem *) items->data)->updateRepr();
2373 modmade = true;
2374 }
2375 }
2376 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2377 _("Star: Change number of corners"));
2379 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2380 }
2382 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2383 {
2384 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2386 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2387 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2388 prefs->setDouble("/tools/shapes/star/proportion", adj->value);
2389 }
2391 // quit if run by the attr_changed listener
2392 if (g_object_get_data( dataKludge, "freeze" )) {
2393 return;
2394 }
2396 // in turn, prevent listener from responding
2397 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2399 bool modmade = false;
2400 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2401 GSList const *items = selection->itemList();
2402 for (; items != NULL; items = items->next) {
2403 if (SP_IS_STAR((SPItem *) items->data)) {
2404 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2406 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2407 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2408 if (r2 < r1) {
2409 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
2410 } else {
2411 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
2412 }
2414 SP_OBJECT((SPItem *) items->data)->updateRepr();
2415 modmade = true;
2416 }
2417 }
2419 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2420 _("Star: Change spoke ratio"));
2422 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2423 }
2425 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
2426 {
2427 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2428 bool flat = ege_select_one_action_get_active( act ) == 0;
2430 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2431 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2432 prefs->setBool( "/tools/shapes/star/isflatsided", flat);
2433 }
2435 // quit if run by the attr_changed listener
2436 if (g_object_get_data( dataKludge, "freeze" )) {
2437 return;
2438 }
2440 // in turn, prevent listener from responding
2441 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2443 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2444 GSList const *items = selection->itemList();
2445 GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2446 bool modmade = false;
2448 if ( prop_action ) {
2449 gtk_action_set_sensitive( prop_action, !flat );
2450 }
2452 for (; items != NULL; items = items->next) {
2453 if (SP_IS_STAR((SPItem *) items->data)) {
2454 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2455 repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
2456 SP_OBJECT((SPItem *) items->data)->updateRepr();
2457 modmade = true;
2458 }
2459 }
2461 if (modmade) {
2462 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2463 flat ? _("Make polygon") : _("Make star"));
2464 }
2466 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2467 }
2469 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2470 {
2471 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2473 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2474 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2475 prefs->setDouble("/tools/shapes/star/rounded", (gdouble) adj->value);
2476 }
2478 // quit if run by the attr_changed listener
2479 if (g_object_get_data( dataKludge, "freeze" )) {
2480 return;
2481 }
2483 // in turn, prevent listener from responding
2484 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2486 bool modmade = false;
2488 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2489 GSList const *items = selection->itemList();
2490 for (; items != NULL; items = items->next) {
2491 if (SP_IS_STAR((SPItem *) items->data)) {
2492 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2493 sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2494 SP_OBJECT(items->data)->updateRepr();
2495 modmade = true;
2496 }
2497 }
2498 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2499 _("Star: Change rounding"));
2501 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2502 }
2504 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2505 {
2506 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2508 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2509 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2510 prefs->setDouble("/tools/shapes/star/randomized", (gdouble) adj->value);
2511 }
2513 // quit if run by the attr_changed listener
2514 if (g_object_get_data( dataKludge, "freeze" )) {
2515 return;
2516 }
2518 // in turn, prevent listener from responding
2519 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2521 bool modmade = false;
2523 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2524 GSList const *items = selection->itemList();
2525 for (; items != NULL; items = items->next) {
2526 if (SP_IS_STAR((SPItem *) items->data)) {
2527 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2528 sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2529 SP_OBJECT(items->data)->updateRepr();
2530 modmade = true;
2531 }
2532 }
2533 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2534 _("Star: Change randomization"));
2536 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2537 }
2540 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2541 gchar const */*old_value*/, gchar const */*new_value*/,
2542 bool /*is_interactive*/, gpointer data)
2543 {
2544 GtkWidget *tbl = GTK_WIDGET(data);
2546 // quit if run by the _changed callbacks
2547 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2548 return;
2549 }
2551 // in turn, prevent callbacks from responding
2552 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2554 GtkAdjustment *adj = 0;
2556 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2557 bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2559 if (!strcmp(name, "inkscape:randomized")) {
2560 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2561 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2562 } else if (!strcmp(name, "inkscape:rounded")) {
2563 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2564 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2565 } else if (!strcmp(name, "inkscape:flatsided")) {
2566 GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2567 char const *flatsides = repr->attribute("inkscape:flatsided");
2568 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2569 if ( flatsides && !strcmp(flatsides,"false") ) {
2570 ege_select_one_action_set_active( flat_action, 1 );
2571 gtk_action_set_sensitive( prop_action, TRUE );
2572 } else {
2573 ege_select_one_action_set_active( flat_action, 0 );
2574 gtk_action_set_sensitive( prop_action, FALSE );
2575 }
2576 } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2577 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2578 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2579 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2580 if (r2 < r1) {
2581 gtk_adjustment_set_value(adj, r2/r1);
2582 } else {
2583 gtk_adjustment_set_value(adj, r1/r2);
2584 }
2585 } else if (!strcmp(name, "sodipodi:sides")) {
2586 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2587 gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2588 }
2590 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2591 }
2594 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2595 {
2596 NULL, /* child_added */
2597 NULL, /* child_removed */
2598 star_tb_event_attr_changed,
2599 NULL, /* content_changed */
2600 NULL /* order_changed */
2601 };
2604 /**
2605 * \param selection Should not be NULL.
2606 */
2607 static void
2608 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2609 {
2610 int n_selected = 0;
2611 Inkscape::XML::Node *repr = NULL;
2613 purge_repr_listener( tbl, tbl );
2615 for (GSList const *items = selection->itemList();
2616 items != NULL;
2617 items = items->next)
2618 {
2619 if (SP_IS_STAR((SPItem *) items->data)) {
2620 n_selected++;
2621 repr = SP_OBJECT_REPR((SPItem *) items->data);
2622 }
2623 }
2625 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2627 if (n_selected == 0) {
2628 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2629 } else if (n_selected == 1) {
2630 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2632 if (repr) {
2633 g_object_set_data( tbl, "repr", repr );
2634 Inkscape::GC::anchor(repr);
2635 sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2636 sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2637 }
2638 } else {
2639 // FIXME: implement averaging of all parameters for multiple selected stars
2640 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2641 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2642 }
2643 }
2646 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2647 {
2648 // FIXME: in this and all other _default functions, set some flag telling the value_changed
2649 // callbacks to lump all the changes for all selected objects in one undo step
2651 GtkAdjustment *adj = 0;
2653 // fixme: make settable in prefs!
2654 gint mag = 5;
2655 gdouble prop = 0.5;
2656 gboolean flat = FALSE;
2657 gdouble randomized = 0;
2658 gdouble rounded = 0;
2660 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2661 ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2663 GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2664 gtk_action_set_sensitive( sb2, !flat );
2666 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2667 gtk_adjustment_set_value(adj, mag);
2668 gtk_adjustment_value_changed(adj);
2670 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2671 gtk_adjustment_set_value(adj, prop);
2672 gtk_adjustment_value_changed(adj);
2674 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2675 gtk_adjustment_set_value(adj, rounded);
2676 gtk_adjustment_value_changed(adj);
2678 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2679 gtk_adjustment_set_value(adj, randomized);
2680 gtk_adjustment_value_changed(adj);
2681 }
2684 void
2685 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2686 {
2687 GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2688 if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2689 GtkWidget *l = gtk_label_new(NULL);
2690 gtk_label_set_markup(GTK_LABEL(l), title);
2691 gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2692 if ( GTK_IS_TOOLBAR(tbl) ) {
2693 gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2694 } else {
2695 gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2696 }
2697 gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2698 }
2701 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2702 {
2703 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
2705 {
2706 EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2707 ege_output_action_set_use_markup( act, TRUE );
2708 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2709 g_object_set_data( holder, "mode_action", act );
2710 }
2712 {
2713 EgeAdjustmentAction* eact = 0;
2714 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2715 bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2717 /* Flatsided checkbox */
2718 {
2719 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2721 GtkTreeIter iter;
2722 gtk_list_store_append( model, &iter );
2723 gtk_list_store_set( model, &iter,
2724 0, _("Polygon"),
2725 1, _("Regular polygon (with one handle) instead of a star"),
2726 2, INKSCAPE_ICON_DRAW_POLYGON,
2727 -1 );
2729 gtk_list_store_append( model, &iter );
2730 gtk_list_store_set( model, &iter,
2731 0, _("Star"),
2732 1, _("Star instead of a regular polygon (with one handle)"),
2733 2, INKSCAPE_ICON_DRAW_STAR,
2734 -1 );
2736 EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2737 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2738 g_object_set_data( holder, "flat_action", act );
2740 ege_select_one_action_set_appearance( act, "full" );
2741 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2742 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2743 ege_select_one_action_set_icon_column( act, 2 );
2744 ege_select_one_action_set_icon_size( act, secondarySize );
2745 ege_select_one_action_set_tooltip_column( act, 1 );
2747 ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2748 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2749 }
2751 /* Magnitude */
2752 {
2753 gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2754 gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2755 eact = create_adjustment_action( "MagnitudeAction",
2756 _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2757 "/tools/shapes/star/magnitude", 3,
2758 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2759 3, 1024, 1, 5,
2760 labels, values, G_N_ELEMENTS(labels),
2761 sp_stb_magnitude_value_changed,
2762 1.0, 0 );
2763 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2764 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2765 }
2767 /* Spoke ratio */
2768 {
2769 gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2770 gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2771 eact = create_adjustment_action( "SpokeAction",
2772 _("Spoke ratio"), _("Spoke ratio:"),
2773 // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2774 // Base radius is the same for the closest handle.
2775 _("Base radius to tip radius ratio"),
2776 "/tools/shapes/star/proportion", 0.5,
2777 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2778 0.01, 1.0, 0.01, 0.1,
2779 labels, values, G_N_ELEMENTS(labels),
2780 sp_stb_proportion_value_changed );
2781 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2782 g_object_set_data( holder, "prop_action", eact );
2783 }
2785 if ( !isFlatSided ) {
2786 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2787 } else {
2788 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2789 }
2791 /* Roundedness */
2792 {
2793 gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2794 gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2795 eact = create_adjustment_action( "RoundednessAction",
2796 _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2797 "/tools/shapes/star/rounded", 0.0,
2798 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2799 -10.0, 10.0, 0.01, 0.1,
2800 labels, values, G_N_ELEMENTS(labels),
2801 sp_stb_rounded_value_changed );
2802 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2803 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2804 }
2806 /* Randomization */
2807 {
2808 gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2809 gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2810 eact = create_adjustment_action( "RandomizationAction",
2811 _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2812 "/tools/shapes/star/randomized", 0.0,
2813 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2814 -10.0, 10.0, 0.001, 0.01,
2815 labels, values, G_N_ELEMENTS(labels),
2816 sp_stb_randomized_value_changed, 0.1, 3 );
2817 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2818 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2819 }
2820 }
2822 {
2823 /* Reset */
2824 {
2825 GtkAction* act = gtk_action_new( "StarResetAction",
2826 _("Defaults"),
2827 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2828 GTK_STOCK_CLEAR );
2829 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2830 gtk_action_group_add_action( mainActions, act );
2831 gtk_action_set_sensitive( act, TRUE );
2832 }
2833 }
2835 sigc::connection *connection = new sigc::connection(
2836 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2837 );
2838 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2839 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2840 }
2843 //########################
2844 //## Rect ##
2845 //########################
2847 static void sp_rtb_sensitivize( GObject *tbl )
2848 {
2849 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2850 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2851 GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2853 if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2854 gtk_action_set_sensitive( not_rounded, FALSE );
2855 } else {
2856 gtk_action_set_sensitive( not_rounded, TRUE );
2857 }
2858 }
2861 static void
2862 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2863 void (*setter)(SPRect *, gdouble))
2864 {
2865 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2867 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2868 SPUnit const *unit = tracker->getActiveUnit();
2870 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2871 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2872 prefs->setDouble(Glib::ustring("/tools/shapes/rect/") + value_name, sp_units_get_pixels(adj->value, *unit));
2873 }
2875 // quit if run by the attr_changed listener
2876 if (g_object_get_data( tbl, "freeze" )) {
2877 return;
2878 }
2880 // in turn, prevent listener from responding
2881 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2883 bool modmade = false;
2884 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2885 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2886 if (SP_IS_RECT(items->data)) {
2887 if (adj->value != 0) {
2888 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2889 } else {
2890 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2891 }
2892 modmade = true;
2893 }
2894 }
2896 sp_rtb_sensitivize( tbl );
2898 if (modmade) {
2899 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2900 _("Change rectangle"));
2901 }
2903 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2904 }
2906 static void
2907 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2908 {
2909 sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2910 }
2912 static void
2913 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2914 {
2915 sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2916 }
2918 static void
2919 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2920 {
2921 sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2922 }
2924 static void
2925 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2926 {
2927 sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2928 }
2932 static void
2933 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2934 {
2935 GtkAdjustment *adj = 0;
2937 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2938 gtk_adjustment_set_value(adj, 0.0);
2939 // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2940 gtk_adjustment_value_changed(adj);
2942 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2943 gtk_adjustment_set_value(adj, 0.0);
2944 gtk_adjustment_value_changed(adj);
2946 sp_rtb_sensitivize( obj );
2947 }
2949 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2950 gchar const */*old_value*/, gchar const */*new_value*/,
2951 bool /*is_interactive*/, gpointer data)
2952 {
2953 GObject *tbl = G_OBJECT(data);
2955 // quit if run by the _changed callbacks
2956 if (g_object_get_data( tbl, "freeze" )) {
2957 return;
2958 }
2960 // in turn, prevent callbacks from responding
2961 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2963 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2964 SPUnit const *unit = tracker->getActiveUnit();
2966 gpointer item = g_object_get_data( tbl, "item" );
2967 if (item && SP_IS_RECT(item)) {
2968 {
2969 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2970 gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2971 gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2972 }
2974 {
2975 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2976 gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2977 gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2978 }
2980 {
2981 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2982 gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2983 gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2984 }
2986 {
2987 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2988 gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2989 gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2990 }
2991 }
2993 sp_rtb_sensitivize( tbl );
2995 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2996 }
2999 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
3000 NULL, /* child_added */
3001 NULL, /* child_removed */
3002 rect_tb_event_attr_changed,
3003 NULL, /* content_changed */
3004 NULL /* order_changed */
3005 };
3007 /**
3008 * \param selection should not be NULL.
3009 */
3010 static void
3011 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3012 {
3013 int n_selected = 0;
3014 Inkscape::XML::Node *repr = NULL;
3015 SPItem *item = NULL;
3017 if ( g_object_get_data( tbl, "repr" ) ) {
3018 g_object_set_data( tbl, "item", NULL );
3019 }
3020 purge_repr_listener( tbl, tbl );
3022 for (GSList const *items = selection->itemList();
3023 items != NULL;
3024 items = items->next) {
3025 if (SP_IS_RECT((SPItem *) items->data)) {
3026 n_selected++;
3027 item = (SPItem *) items->data;
3028 repr = SP_OBJECT_REPR(item);
3029 }
3030 }
3032 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3034 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3036 if (n_selected == 0) {
3037 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3039 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3040 gtk_action_set_sensitive(w, FALSE);
3041 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3042 gtk_action_set_sensitive(h, FALSE);
3044 } else if (n_selected == 1) {
3045 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3046 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3048 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3049 gtk_action_set_sensitive(w, TRUE);
3050 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3051 gtk_action_set_sensitive(h, TRUE);
3053 if (repr) {
3054 g_object_set_data( tbl, "repr", repr );
3055 g_object_set_data( tbl, "item", item );
3056 Inkscape::GC::anchor(repr);
3057 sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
3058 sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
3059 }
3060 } else {
3061 // FIXME: implement averaging of all parameters for multiple selected
3062 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3063 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3064 sp_rtb_sensitivize( tbl );
3065 }
3066 }
3069 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3070 {
3071 EgeAdjustmentAction* eact = 0;
3072 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3074 {
3075 EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
3076 ege_output_action_set_use_markup( act, TRUE );
3077 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3078 g_object_set_data( holder, "mode_action", act );
3079 }
3081 // rx/ry units menu: create
3082 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
3083 //tracker->addUnit( SP_UNIT_PERCENT, 0 );
3084 // fixme: add % meaning per cent of the width/height
3085 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
3086 g_object_set_data( holder, "tracker", tracker );
3088 /* W */
3089 {
3090 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3091 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3092 eact = create_adjustment_action( "RectWidthAction",
3093 _("Width"), _("W:"), _("Width of rectangle"),
3094 "/tools/shapes/rect/width", 0,
3095 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
3096 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3097 labels, values, G_N_ELEMENTS(labels),
3098 sp_rtb_width_value_changed );
3099 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3100 g_object_set_data( holder, "width_action", eact );
3101 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3102 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3103 }
3105 /* H */
3106 {
3107 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3108 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3109 eact = create_adjustment_action( "RectHeightAction",
3110 _("Height"), _("H:"), _("Height of rectangle"),
3111 "/tools/shapes/rect/height", 0,
3112 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3113 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3114 labels, values, G_N_ELEMENTS(labels),
3115 sp_rtb_height_value_changed );
3116 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3117 g_object_set_data( holder, "height_action", eact );
3118 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3119 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3120 }
3122 /* rx */
3123 {
3124 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3125 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3126 eact = create_adjustment_action( "RadiusXAction",
3127 _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
3128 "/tools/shapes/rect/rx", 0,
3129 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3130 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3131 labels, values, G_N_ELEMENTS(labels),
3132 sp_rtb_rx_value_changed);
3133 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3134 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3135 }
3137 /* ry */
3138 {
3139 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3140 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3141 eact = create_adjustment_action( "RadiusYAction",
3142 _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
3143 "/tools/shapes/rect/ry", 0,
3144 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3145 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3146 labels, values, G_N_ELEMENTS(labels),
3147 sp_rtb_ry_value_changed);
3148 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3149 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3150 }
3152 // add the units menu
3153 {
3154 GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
3155 gtk_action_group_add_action( mainActions, act );
3156 }
3158 /* Reset */
3159 {
3160 InkAction* inky = ink_action_new( "RectResetAction",
3161 _("Not rounded"),
3162 _("Make corners sharp"),
3163 INKSCAPE_ICON_RECTANGLE_MAKE_CORNERS_SHARP,
3164 secondarySize );
3165 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
3166 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3167 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3168 g_object_set_data( holder, "not_rounded", inky );
3169 }
3171 g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
3172 sp_rtb_sensitivize( holder );
3174 sigc::connection *connection = new sigc::connection(
3175 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
3176 );
3177 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3178 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3179 }
3181 //########################
3182 //## 3D Box ##
3183 //########################
3185 // normalize angle so that it lies in the interval [0,360]
3186 static double box3d_normalize_angle (double a) {
3187 double angle = a + ((int) (a/360.0))*360;
3188 if (angle < 0) {
3189 angle += 360.0;
3190 }
3191 return angle;
3192 }
3194 static void
3195 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
3196 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
3197 // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
3198 // have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
3199 // are reset).
3200 bool is_infinite = !persp3d_VP_is_finite(persp, axis);
3202 if (is_infinite) {
3203 gtk_toggle_action_set_active(tact, TRUE);
3204 gtk_action_set_sensitive(act, TRUE);
3206 double angle = persp3d_get_infinite_angle(persp, axis);
3207 if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
3208 gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
3209 }
3210 } else {
3211 gtk_toggle_action_set_active(tact, FALSE);
3212 gtk_action_set_sensitive(act, FALSE);
3213 }
3214 }
3216 static void
3217 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
3218 if (!persp_repr) {
3219 g_print ("No perspective given to box3d_resync_toolbar().\n");
3220 return;
3221 }
3223 GtkWidget *tbl = GTK_WIDGET(data);
3224 GtkAdjustment *adj = 0;
3225 GtkAction *act = 0;
3226 GtkToggleAction *tact = 0;
3227 Persp3D *persp = persp3d_get_from_repr(persp_repr);
3228 {
3229 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
3230 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
3231 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
3233 box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
3234 }
3235 {
3236 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
3237 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
3238 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
3240 box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
3241 }
3242 {
3243 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
3244 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
3245 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
3247 box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
3248 }
3249 }
3251 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3252 gchar const */*old_value*/, gchar const */*new_value*/,
3253 bool /*is_interactive*/, gpointer data)
3254 {
3255 GtkWidget *tbl = GTK_WIDGET(data);
3257 // quit if run by the attr_changed or selection changed listener
3258 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3259 return;
3260 }
3262 // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
3263 // sp_document_maybe_done() when the document is undo insensitive)
3264 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3266 // TODO: Only update the appropriate part of the toolbar
3267 // if (!strcmp(name, "inkscape:vp_z")) {
3268 box3d_resync_toolbar(repr, G_OBJECT(tbl));
3269 // }
3271 Persp3D *persp = persp3d_get_from_repr(repr);
3272 persp3d_update_box_reprs(persp);
3274 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3275 }
3277 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
3278 {
3279 NULL, /* child_added */
3280 NULL, /* child_removed */
3281 box3d_persp_tb_event_attr_changed,
3282 NULL, /* content_changed */
3283 NULL /* order_changed */
3284 };
3286 /**
3287 * \param selection Should not be NULL.
3288 */
3289 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
3290 // Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
3291 static void
3292 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3293 {
3294 // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
3295 // disable the angle entry fields for this direction (otherwise entering a value in them should only
3296 // update the perspectives with infinite VPs and leave the other ones untouched).
3298 Inkscape::XML::Node *persp_repr = NULL;
3299 purge_repr_listener(tbl, tbl);
3301 SPItem *item = selection->singleItem();
3302 if (item && SP_IS_BOX3D(item)) {
3303 // FIXME: Also deal with multiple selected boxes
3304 SPBox3D *box = SP_BOX3D(item);
3305 Persp3D *persp = box3d_get_perspective(box);
3306 persp_repr = SP_OBJECT_REPR(persp);
3307 if (persp_repr) {
3308 g_object_set_data(tbl, "repr", persp_repr);
3309 Inkscape::GC::anchor(persp_repr);
3310 sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
3311 sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
3312 }
3314 inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
3315 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3316 prefs->setString("/tools/shapes/3dbox/persp", persp_repr->attribute("id"));
3318 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
3319 box3d_resync_toolbar(persp_repr, tbl);
3320 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
3321 }
3322 }
3324 static void
3325 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
3326 {
3327 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3328 SPDocument *document = sp_desktop_document(desktop);
3330 // quit if run by the attr_changed or selection changed listener
3331 if (g_object_get_data( dataKludge, "freeze" )) {
3332 return;
3333 }
3335 // in turn, prevent listener from responding
3336 g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
3338 //Persp3D *persp = document->current_persp3d;
3339 std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
3340 if (sel_persps.empty()) {
3341 // this can happen when the document is created; we silently ignore it
3342 return;
3343 }
3344 Persp3D *persp = sel_persps.front();
3346 persp->tmat.set_infinite_direction (axis, adj->value);
3347 SP_OBJECT(persp)->updateRepr();
3349 // TODO: use the correct axis here, too
3350 sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
3352 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
3353 }
3356 static void
3357 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3358 {
3359 box3d_angle_value_changed(adj, dataKludge, Proj::X);
3360 }
3362 static void
3363 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3364 {
3365 box3d_angle_value_changed(adj, dataKludge, Proj::Y);
3366 }
3368 static void
3369 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3370 {
3371 box3d_angle_value_changed(adj, dataKludge, Proj::Z);
3372 }
3375 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
3376 {
3377 // TODO: Take all selected perspectives into account
3378 std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
3379 if (sel_persps.empty()) {
3380 // this can happen when the document is created; we silently ignore it
3381 return;
3382 }
3383 Persp3D *persp = sel_persps.front();
3385 bool set_infinite = gtk_toggle_action_get_active(act);
3386 persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
3387 }
3389 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3390 {
3391 box3d_vp_state_changed(act, box3d_angle, Proj::X);
3392 }
3394 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3395 {
3396 box3d_vp_state_changed(act, box3d_angle, Proj::Y);
3397 }
3399 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3400 {
3401 box3d_vp_state_changed(act, box3d_angle, Proj::Z);
3402 }
3404 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3405 {
3406 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3407 EgeAdjustmentAction* eact = 0;
3408 SPDocument *document = sp_desktop_document (desktop);
3409 Persp3D *persp = document->current_persp3d;
3411 EgeAdjustmentAction* box3d_angle_x = 0;
3412 EgeAdjustmentAction* box3d_angle_y = 0;
3413 EgeAdjustmentAction* box3d_angle_z = 0;
3415 /* Angle X */
3416 {
3417 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3418 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3419 eact = create_adjustment_action( "3DBoxAngleXAction",
3420 _("Angle in X direction"), _("Angle X:"),
3421 // Translators: PL is short for 'perspective line'
3422 _("Angle of PLs in X direction"),
3423 "/tools/shapes/3dbox/box3d_angle_x", 30,
3424 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
3425 -360.0, 360.0, 1.0, 10.0,
3426 labels, values, G_N_ELEMENTS(labels),
3427 box3d_angle_x_value_changed );
3428 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3429 g_object_set_data( holder, "box3d_angle_x_action", eact );
3430 box3d_angle_x = eact;
3431 }
3433 if (!persp3d_VP_is_finite(persp, Proj::X)) {
3434 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3435 } else {
3436 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3437 }
3440 /* VP X state */
3441 {
3442 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
3443 // Translators: VP is short for 'vanishing point'
3444 _("State of VP in X direction"),
3445 _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
3446 INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3447 Inkscape::ICON_SIZE_DECORATION );
3448 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3449 g_object_set_data( holder, "box3d_vp_x_state_action", act );
3450 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
3451 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3452 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3453 }
3455 /* Angle Y */
3456 {
3457 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3458 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3459 eact = create_adjustment_action( "3DBoxAngleYAction",
3460 _("Angle in Y direction"), _("Angle Y:"),
3461 // Translators: PL is short for 'perspective line'
3462 _("Angle of PLs in Y direction"),
3463 "/tools/shapes/3dbox/box3d_angle_y", 30,
3464 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3465 -360.0, 360.0, 1.0, 10.0,
3466 labels, values, G_N_ELEMENTS(labels),
3467 box3d_angle_y_value_changed );
3468 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3469 g_object_set_data( holder, "box3d_angle_y_action", eact );
3470 box3d_angle_y = eact;
3471 }
3473 if (!persp3d_VP_is_finite(persp, Proj::Y)) {
3474 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3475 } else {
3476 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3477 }
3479 /* VP Y state */
3480 {
3481 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
3482 // Translators: VP is short for 'vanishing point'
3483 _("State of VP in Y direction"),
3484 _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
3485 INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3486 Inkscape::ICON_SIZE_DECORATION );
3487 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3488 g_object_set_data( holder, "box3d_vp_y_state_action", act );
3489 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
3490 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3491 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3492 }
3494 /* Angle Z */
3495 {
3496 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3497 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3498 eact = create_adjustment_action( "3DBoxAngleZAction",
3499 _("Angle in Z direction"), _("Angle Z:"),
3500 // Translators: PL is short for 'perspective line'
3501 _("Angle of PLs in Z direction"),
3502 "/tools/shapes/3dbox/box3d_angle_z", 30,
3503 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3504 -360.0, 360.0, 1.0, 10.0,
3505 labels, values, G_N_ELEMENTS(labels),
3506 box3d_angle_z_value_changed );
3507 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3508 g_object_set_data( holder, "box3d_angle_z_action", eact );
3509 box3d_angle_z = eact;
3510 }
3512 if (!persp3d_VP_is_finite(persp, Proj::Z)) {
3513 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3514 } else {
3515 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3516 }
3518 /* VP Z state */
3519 {
3520 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3521 // Translators: VP is short for 'vanishing point'
3522 _("State of VP in Z direction"),
3523 _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3524 INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3525 Inkscape::ICON_SIZE_DECORATION );
3526 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3527 g_object_set_data( holder, "box3d_vp_z_state_action", act );
3528 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3529 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3530 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3531 }
3533 sigc::connection *connection = new sigc::connection(
3534 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3535 );
3536 g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3537 g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3538 }
3540 //########################
3541 //## Spiral ##
3542 //########################
3544 static void
3545 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, Glib::ustring const &value_name)
3546 {
3547 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3549 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3550 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3551 prefs->setDouble("/tools/shapes/spiral/" + value_name, adj->value);
3552 }
3554 // quit if run by the attr_changed listener
3555 if (g_object_get_data( tbl, "freeze" )) {
3556 return;
3557 }
3559 // in turn, prevent listener from responding
3560 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3562 gchar* namespaced_name = g_strconcat("sodipodi:", value_name.data(), NULL);
3564 bool modmade = false;
3565 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3566 items != NULL;
3567 items = items->next)
3568 {
3569 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3570 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3571 sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3572 SP_OBJECT((SPItem *) items->data)->updateRepr();
3573 modmade = true;
3574 }
3575 }
3577 g_free(namespaced_name);
3579 if (modmade) {
3580 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3581 _("Change spiral"));
3582 }
3584 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3585 }
3587 static void
3588 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3589 {
3590 sp_spl_tb_value_changed(adj, tbl, "revolution");
3591 }
3593 static void
3594 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3595 {
3596 sp_spl_tb_value_changed(adj, tbl, "expansion");
3597 }
3599 static void
3600 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3601 {
3602 sp_spl_tb_value_changed(adj, tbl, "t0");
3603 }
3605 static void
3606 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3607 {
3608 GtkWidget *tbl = GTK_WIDGET(obj);
3610 GtkAdjustment *adj;
3612 // fixme: make settable
3613 gdouble rev = 5;
3614 gdouble exp = 1.0;
3615 gdouble t0 = 0.0;
3617 adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3618 gtk_adjustment_set_value(adj, rev);
3619 gtk_adjustment_value_changed(adj);
3621 adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3622 gtk_adjustment_set_value(adj, exp);
3623 gtk_adjustment_value_changed(adj);
3625 adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3626 gtk_adjustment_set_value(adj, t0);
3627 gtk_adjustment_value_changed(adj);
3629 spinbutton_defocus(GTK_OBJECT(tbl));
3630 }
3633 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3634 gchar const */*old_value*/, gchar const */*new_value*/,
3635 bool /*is_interactive*/, gpointer data)
3636 {
3637 GtkWidget *tbl = GTK_WIDGET(data);
3639 // quit if run by the _changed callbacks
3640 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3641 return;
3642 }
3644 // in turn, prevent callbacks from responding
3645 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3647 GtkAdjustment *adj;
3648 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3649 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3651 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3652 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3654 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3655 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3657 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3658 }
3661 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3662 NULL, /* child_added */
3663 NULL, /* child_removed */
3664 spiral_tb_event_attr_changed,
3665 NULL, /* content_changed */
3666 NULL /* order_changed */
3667 };
3669 static void
3670 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3671 {
3672 int n_selected = 0;
3673 Inkscape::XML::Node *repr = NULL;
3675 purge_repr_listener( tbl, tbl );
3677 for (GSList const *items = selection->itemList();
3678 items != NULL;
3679 items = items->next)
3680 {
3681 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3682 n_selected++;
3683 repr = SP_OBJECT_REPR((SPItem *) items->data);
3684 }
3685 }
3687 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3689 if (n_selected == 0) {
3690 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3691 } else if (n_selected == 1) {
3692 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3694 if (repr) {
3695 g_object_set_data( tbl, "repr", repr );
3696 Inkscape::GC::anchor(repr);
3697 sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3698 sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3699 }
3700 } else {
3701 // FIXME: implement averaging of all parameters for multiple selected
3702 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3703 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3704 }
3705 }
3708 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3709 {
3710 EgeAdjustmentAction* eact = 0;
3711 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3713 {
3714 EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3715 ege_output_action_set_use_markup( act, TRUE );
3716 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3717 g_object_set_data( holder, "mode_action", act );
3718 }
3720 /* Revolution */
3721 {
3722 gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3723 gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3724 eact = create_adjustment_action( "SpiralRevolutionAction",
3725 _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3726 "/tools/shapes/spiral/revolution", 3.0,
3727 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3728 0.01, 1024.0, 0.1, 1.0,
3729 labels, values, G_N_ELEMENTS(labels),
3730 sp_spl_tb_revolution_value_changed, 1, 2);
3731 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3732 }
3734 /* Expansion */
3735 {
3736 gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3737 gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3738 eact = create_adjustment_action( "SpiralExpansionAction",
3739 _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3740 "/tools/shapes/spiral/expansion", 1.0,
3741 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3742 0.0, 1000.0, 0.01, 1.0,
3743 labels, values, G_N_ELEMENTS(labels),
3744 sp_spl_tb_expansion_value_changed);
3745 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3746 }
3748 /* T0 */
3749 {
3750 gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3751 gdouble values[] = {0, 0.5, 0.9};
3752 eact = create_adjustment_action( "SpiralT0Action",
3753 _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3754 "/tools/shapes/spiral/t0", 0.0,
3755 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3756 0.0, 0.999, 0.01, 1.0,
3757 labels, values, G_N_ELEMENTS(labels),
3758 sp_spl_tb_t0_value_changed);
3759 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3760 }
3762 /* Reset */
3763 {
3764 InkAction* inky = ink_action_new( "SpiralResetAction",
3765 _("Defaults"),
3766 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3767 GTK_STOCK_CLEAR,
3768 secondarySize );
3769 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3770 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3771 }
3774 sigc::connection *connection = new sigc::connection(
3775 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3776 );
3777 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3778 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3779 }
3781 //########################
3782 //## Pen/Pencil ##
3783 //########################
3785 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3786 static Glib::ustring const
3787 freehand_tool_name(GObject *dataKludge)
3788 {
3789 SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3790 return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3791 ? "/tools/freehand/pen"
3792 : "/tools/freehand/pencil" );
3793 }
3795 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3796 {
3797 gint mode = ege_select_one_action_get_active(act);
3799 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3800 prefs->setInt(freehand_tool_name(tbl) + "/freehand-mode", mode);
3802 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3804 // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3805 // preparatory work here
3806 if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3807 SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3808 sp_pen_context_set_polyline_mode(pc);
3809 }
3810 }
3812 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3813 {
3814 /* Freehand mode toggle buttons */
3815 {
3816 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3817 guint freehandMode = prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/freehand-mode" : "/tools/freehand/pen/freehand-mode" ), 0);
3818 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3820 {
3821 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3823 GtkTreeIter iter;
3824 gtk_list_store_append( model, &iter );
3825 gtk_list_store_set( model, &iter,
3826 0, _("Bezier"),
3827 1, _("Create regular Bezier path"),
3828 2, INKSCAPE_ICON_PATH_MODE_BEZIER,
3829 -1 );
3831 gtk_list_store_append( model, &iter );
3832 gtk_list_store_set( model, &iter,
3833 0, _("Spiro"),
3834 1, _("Create Spiro path"),
3835 2, INKSCAPE_ICON_PATH_MODE_SPIRO,
3836 -1 );
3838 if (!tool_is_pencil) {
3839 gtk_list_store_append( model, &iter );
3840 gtk_list_store_set( model, &iter,
3841 0, _("Zigzag"),
3842 1, _("Create a sequence of straight line segments"),
3843 2, INKSCAPE_ICON_PATH_MODE_POLYLINE,
3844 -1 );
3846 gtk_list_store_append( model, &iter );
3847 gtk_list_store_set( model, &iter,
3848 0, _("Paraxial"),
3849 1, _("Create a sequence of paraxial line segments"),
3850 2, INKSCAPE_ICON_PATH_MODE_POLYLINE_PARAXIAL,
3851 -1 );
3852 }
3854 EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3855 "FreehandModeActionPencil" :
3856 "FreehandModeActionPen",
3857 (_("Mode:")), (_("Mode of new lines drawn by this tool")), NULL, GTK_TREE_MODEL(model) );
3858 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3860 ege_select_one_action_set_appearance( act, "full" );
3861 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3862 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3863 ege_select_one_action_set_icon_column( act, 2 );
3864 ege_select_one_action_set_icon_size( act, secondarySize );
3865 ege_select_one_action_set_tooltip_column( act, 1 );
3867 ege_select_one_action_set_active( act, freehandMode);
3868 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3869 }
3870 }
3871 }
3873 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3874 gint shape = ege_select_one_action_get_active( act );
3875 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3876 prefs->setInt(freehand_tool_name(dataKludge) + "/shape", shape);
3877 }
3879 /**
3880 * \brief Generate the list of freehand advanced shape option entries.
3881 */
3882 GList * freehand_shape_dropdown_items_list() {
3883 GList *glist = NULL;
3885 glist = g_list_append (glist, _("None"));
3886 glist = g_list_append (glist, _("Triangle in"));
3887 glist = g_list_append (glist, _("Triangle out"));
3888 glist = g_list_append (glist, _("Ellipse"));
3889 glist = g_list_append (glist, _("From clipboard"));
3891 return glist;
3892 }
3894 static void
3895 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3896 /*advanced shape options */
3897 {
3898 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3899 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3901 GList* items = 0;
3902 gint count = 0;
3903 for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3904 {
3905 GtkTreeIter iter;
3906 gtk_list_store_append( model, &iter );
3907 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3908 count++;
3909 }
3910 g_list_free( items );
3911 items = 0;
3912 EgeSelectOneAction* act1 = ege_select_one_action_new(
3913 tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3914 _("Shape:"), (_("Shape of new paths drawn by this tool")), NULL, GTK_TREE_MODEL(model));
3915 g_object_set( act1, "short_label", _("Shape:"), NULL );
3916 ege_select_one_action_set_appearance( act1, "compact" );
3917 ege_select_one_action_set_active( act1, prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/shape" : "/tools/freehand/pen/shape" ), 0) );
3918 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3919 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3920 g_object_set_data( holder, "shape_action", act1 );
3921 }
3922 }
3924 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3925 {
3926 sp_add_freehand_mode_toggle(mainActions, holder, false);
3927 freehand_add_advanced_shape_options(mainActions, holder, false);
3928 }
3931 static void
3932 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3933 {
3934 GtkWidget *tbl = GTK_WIDGET(obj);
3936 GtkAdjustment *adj;
3938 // fixme: make settable
3939 gdouble tolerance = 4;
3941 adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3942 gtk_adjustment_set_value(adj, tolerance);
3943 gtk_adjustment_value_changed(adj);
3945 spinbutton_defocus(GTK_OBJECT(tbl));
3946 }
3948 static void
3949 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3950 {
3951 // quit if run by the attr_changed listener
3952 if (g_object_get_data( tbl, "freeze" )) {
3953 return;
3954 }
3955 // in turn, prevent listener from responding
3956 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3957 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3958 prefs->setDouble("/tools/freehand/pencil/tolerance", adj->value);
3959 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3960 }
3962 class PencilToleranceObserver : public Inkscape::Preferences::Observer {
3963 public:
3964 PencilToleranceObserver(Glib::ustring const &path, GObject *x) : Observer(path), _obj(x)
3965 {
3966 g_object_set_data(_obj, "prefobserver", this);
3967 }
3968 virtual ~PencilToleranceObserver() {
3969 if (g_object_get_data(_obj, "prefobserver") == this) {
3970 g_object_set_data(_obj, "prefobserver", NULL);
3971 }
3972 }
3973 virtual void notify(Inkscape::Preferences::Entry const &val) {
3974 GObject* tbl = _obj;
3975 if (g_object_get_data( tbl, "freeze" )) {
3976 return;
3977 }
3978 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3980 GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl, "tolerance");
3982 double v = val.getDouble(adj->value);
3983 gtk_adjustment_set_value(adj, v);
3984 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3985 }
3986 private:
3987 GObject *_obj;
3988 };
3991 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3992 {
3993 sp_add_freehand_mode_toggle(mainActions, holder, true);
3995 EgeAdjustmentAction* eact = 0;
3997 /* Tolerance */
3998 {
3999 gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
4000 gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
4001 eact = create_adjustment_action( "PencilToleranceAction",
4002 _("Smoothing:"), _("Smoothing: "),
4003 _("How much smoothing (simplifying) is applied to the line"),
4004 "/tools/freehand/pencil/tolerance",
4005 3.0,
4006 GTK_WIDGET(desktop->canvas), NULL,
4007 holder, TRUE, "altx-pencil",
4008 1, 100.0, 0.5, 1.0,
4009 labels, values, G_N_ELEMENTS(labels),
4010 sp_pencil_tb_tolerance_value_changed,
4011 1, 2);
4012 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4013 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4015 PencilToleranceObserver *obs =
4016 new PencilToleranceObserver("/tools/freehand/pencil/tolerance", G_OBJECT(holder));
4017 }
4019 /* advanced shape options */
4020 freehand_add_advanced_shape_options(mainActions, holder, true);
4022 /* Reset */
4023 {
4024 InkAction* inky = ink_action_new( "PencilResetAction",
4025 _("Defaults"),
4026 _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
4027 GTK_STOCK_CLEAR,
4028 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4029 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
4030 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4031 }
4033 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4035 }
4038 //########################
4039 //## Tweak ##
4040 //########################
4042 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4043 {
4044 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4045 prefs->setDouble( "/tools/tweak/width", adj->value * 0.01 );
4046 }
4048 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4049 {
4050 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4051 prefs->setDouble( "/tools/tweak/force", adj->value * 0.01 );
4052 }
4054 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4055 {
4056 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4057 prefs->setBool("/tools/tweak/usepressure", gtk_toggle_action_get_active(act));
4058 }
4060 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4061 {
4062 int mode = ege_select_one_action_get_active( act );
4063 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4064 prefs->setInt("/tools/tweak/mode", mode);
4066 GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
4067 GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
4068 GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
4069 GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
4070 GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
4071 GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
4072 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
4073 if (doh) gtk_action_set_sensitive (doh, TRUE);
4074 if (dos) gtk_action_set_sensitive (dos, TRUE);
4075 if (dol) gtk_action_set_sensitive (dol, TRUE);
4076 if (doo) gtk_action_set_sensitive (doo, TRUE);
4077 if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
4078 if (fid) gtk_action_set_sensitive (fid, FALSE);
4079 } else {
4080 if (doh) gtk_action_set_sensitive (doh, FALSE);
4081 if (dos) gtk_action_set_sensitive (dos, FALSE);
4082 if (dol) gtk_action_set_sensitive (dol, FALSE);
4083 if (doo) gtk_action_set_sensitive (doo, FALSE);
4084 if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
4085 if (fid) gtk_action_set_sensitive (fid, TRUE);
4086 }
4087 }
4089 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4090 {
4091 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4092 prefs->setDouble( "/tools/tweak/fidelity", adj->value * 0.01 );
4093 }
4095 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
4096 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4097 prefs->setBool("/tools/tweak/doh", gtk_toggle_action_get_active(act));
4098 }
4099 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
4100 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4101 prefs->setBool("/tools/tweak/dos", gtk_toggle_action_get_active(act));
4102 }
4103 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
4104 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4105 prefs->setBool("/tools/tweak/dol", gtk_toggle_action_get_active(act));
4106 }
4107 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
4108 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4109 prefs->setBool("/tools/tweak/doo", gtk_toggle_action_get_active(act));
4110 }
4112 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4113 {
4114 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
4115 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4117 {
4118 /* Width */
4119 gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
4120 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4121 EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
4122 _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
4123 "/tools/tweak/width", 15,
4124 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
4125 1, 100, 1.0, 10.0,
4126 labels, values, G_N_ELEMENTS(labels),
4127 sp_tweak_width_value_changed, 0.01, 0, 100 );
4128 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4129 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4130 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4131 }
4134 {
4135 /* Force */
4136 gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
4137 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4138 EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
4139 _("Force"), _("Force:"), _("The force of the tweak action"),
4140 "/tools/tweak/force", 20,
4141 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
4142 1, 100, 1.0, 10.0,
4143 labels, values, G_N_ELEMENTS(labels),
4144 sp_tweak_force_value_changed, 0.01, 0, 100 );
4145 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4146 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4147 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4148 }
4150 /* Mode */
4151 {
4152 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4154 GtkTreeIter iter;
4155 gtk_list_store_append( model, &iter );
4156 gtk_list_store_set( model, &iter,
4157 0, _("Move mode"),
4158 1, _("Move objects in any direction"),
4159 2, INKSCAPE_ICON_OBJECT_TWEAK_PUSH,
4160 -1 );
4162 gtk_list_store_append( model, &iter );
4163 gtk_list_store_set( model, &iter,
4164 0, _("Move in/out mode"),
4165 1, _("Move objects towards cursor; with Shift from cursor"),
4166 2, INKSCAPE_ICON_OBJECT_TWEAK_ATTRACT,
4167 -1 );
4169 gtk_list_store_append( model, &iter );
4170 gtk_list_store_set( model, &iter,
4171 0, _("Move jitter mode"),
4172 1, _("Move objects in random directions"),
4173 2, INKSCAPE_ICON_OBJECT_TWEAK_RANDOMIZE,
4174 -1 );
4176 gtk_list_store_append( model, &iter );
4177 gtk_list_store_set( model, &iter,
4178 0, _("Scale mode"),
4179 1, _("Shrink objects, with Shift enlarge"),
4180 2, INKSCAPE_ICON_OBJECT_TWEAK_SHRINK,
4181 -1 );
4183 gtk_list_store_append( model, &iter );
4184 gtk_list_store_set( model, &iter,
4185 0, _("Rotate mode"),
4186 1, _("Rotate objects, with Shift counterclockwise"),
4187 2, INKSCAPE_ICON_OBJECT_TWEAK_ROTATE,
4188 -1 );
4190 gtk_list_store_append( model, &iter );
4191 gtk_list_store_set( model, &iter,
4192 0, _("Duplicate/delete mode"),
4193 1, _("Duplicate objects, with Shift delete"),
4194 2, INKSCAPE_ICON_OBJECT_TWEAK_DUPLICATE,
4195 -1 );
4197 gtk_list_store_append( model, &iter );
4198 gtk_list_store_set( model, &iter,
4199 0, _("Push mode"),
4200 1, _("Push parts of paths in any direction"),
4201 2, INKSCAPE_ICON_PATH_TWEAK_PUSH,
4202 -1 );
4204 gtk_list_store_append( model, &iter );
4205 gtk_list_store_set( model, &iter,
4206 0, _("Shrink/grow mode"),
4207 1, _("Shrink (inset) parts of paths; with Shift grow (outset)"),
4208 2, INKSCAPE_ICON_PATH_TWEAK_SHRINK,
4209 -1 );
4211 gtk_list_store_append( model, &iter );
4212 gtk_list_store_set( model, &iter,
4213 0, _("Attract/repel mode"),
4214 1, _("Attract parts of paths towards cursor; with Shift from cursor"),
4215 2, INKSCAPE_ICON_PATH_TWEAK_ATTRACT,
4216 -1 );
4218 gtk_list_store_append( model, &iter );
4219 gtk_list_store_set( model, &iter,
4220 0, _("Roughen mode"),
4221 1, _("Roughen parts of paths"),
4222 2, INKSCAPE_ICON_PATH_TWEAK_ROUGHEN,
4223 -1 );
4225 gtk_list_store_append( model, &iter );
4226 gtk_list_store_set( model, &iter,
4227 0, _("Color paint mode"),
4228 1, _("Paint the tool's color upon selected objects"),
4229 2, INKSCAPE_ICON_OBJECT_TWEAK_PAINT,
4230 -1 );
4232 gtk_list_store_append( model, &iter );
4233 gtk_list_store_set( model, &iter,
4234 0, _("Color jitter mode"),
4235 1, _("Jitter the colors of selected objects"),
4236 2, INKSCAPE_ICON_OBJECT_TWEAK_JITTER_COLOR,
4237 -1 );
4239 gtk_list_store_append( model, &iter );
4240 gtk_list_store_set( model, &iter,
4241 0, _("Blur mode"),
4242 1, _("Blur selected objects more; with Shift, blur less"),
4243 2, INKSCAPE_ICON_OBJECT_TWEAK_BLUR,
4244 -1 );
4247 EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4248 g_object_set( act, "short_label", _("Mode:"), NULL );
4249 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4250 g_object_set_data( holder, "mode_action", act );
4252 ege_select_one_action_set_appearance( act, "full" );
4253 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4254 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4255 ege_select_one_action_set_icon_column( act, 2 );
4256 ege_select_one_action_set_icon_size( act, secondarySize );
4257 ege_select_one_action_set_tooltip_column( act, 1 );
4259 gint mode = prefs->getInt("/tools/tweak/mode", 0);
4260 ege_select_one_action_set_active( act, mode );
4261 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
4263 g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
4264 }
4266 guint mode = prefs->getInt("/tools/tweak/mode", 0);
4268 {
4269 EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
4270 ege_output_action_set_use_markup( act, TRUE );
4271 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4272 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4273 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4274 g_object_set_data( holder, "tweak_channels_label", act);
4275 }
4277 {
4278 InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
4279 _("Hue"),
4280 _("In color mode, act on objects' hue"),
4281 NULL,
4282 Inkscape::ICON_SIZE_DECORATION );
4283 //TRANSLATORS: "H" here stands for hue
4284 g_object_set( act, "short_label", _("H"), NULL );
4285 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4286 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
4287 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doh", true) );
4288 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4289 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4290 g_object_set_data( holder, "tweak_doh", act);
4291 }
4292 {
4293 InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
4294 _("Saturation"),
4295 _("In color mode, act on objects' saturation"),
4296 NULL,
4297 Inkscape::ICON_SIZE_DECORATION );
4298 //TRANSLATORS: "S" here stands for Saturation
4299 g_object_set( act, "short_label", _("S"), NULL );
4300 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4301 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
4302 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dos", true) );
4303 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4304 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4305 g_object_set_data( holder, "tweak_dos", act );
4306 }
4307 {
4308 InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
4309 _("Lightness"),
4310 _("In color mode, act on objects' lightness"),
4311 NULL,
4312 Inkscape::ICON_SIZE_DECORATION );
4313 //TRANSLATORS: "L" here stands for Lightness
4314 g_object_set( act, "short_label", _("L"), NULL );
4315 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4316 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
4317 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dol", true) );
4318 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4319 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4320 g_object_set_data( holder, "tweak_dol", act );
4321 }
4322 {
4323 InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
4324 _("Opacity"),
4325 _("In color mode, act on objects' opacity"),
4326 NULL,
4327 Inkscape::ICON_SIZE_DECORATION );
4328 //TRANSLATORS: "O" here stands for Opacity
4329 g_object_set( act, "short_label", _("O"), NULL );
4330 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4331 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
4332 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doo", true) );
4333 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4334 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4335 g_object_set_data( holder, "tweak_doo", act );
4336 }
4338 { /* Fidelity */
4339 gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
4340 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4341 EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
4342 _("Fidelity"), _("Fidelity:"),
4343 _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
4344 "/tools/tweak/fidelity", 50,
4345 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
4346 1, 100, 1.0, 10.0,
4347 labels, values, G_N_ELEMENTS(labels),
4348 sp_tweak_fidelity_value_changed, 0.01, 0, 100 );
4349 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4350 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4351 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
4352 gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
4353 g_object_set_data( holder, "tweak_fidelity", eact );
4354 }
4357 /* Use Pressure button */
4358 {
4359 InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
4360 _("Pressure"),
4361 _("Use the pressure of the input device to alter the force of tweak action"),
4362 INKSCAPE_ICON_DRAW_USE_PRESSURE,
4363 Inkscape::ICON_SIZE_DECORATION );
4364 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4365 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
4366 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/usepressure", true) );
4367 }
4369 }
4372 //########################
4373 //## Calligraphy ##
4374 //########################
4375 static void update_presets_list (GObject *tbl)
4376 {
4377 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4378 if (g_object_get_data(tbl, "presets_blocked"))
4379 return;
4381 EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4382 if (!sel) {
4383 // WTF!? This will cause a segfault if ever reached
4384 //ege_select_one_action_set_active(sel, 0);
4385 return;
4386 }
4388 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4390 int ege_index = 1;
4391 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++ege_index) {
4392 bool match = true;
4394 std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(*i);
4395 for (std::vector<Inkscape::Preferences::Entry>::iterator j = preset.begin(); j != preset.end(); ++j) {
4396 Glib::ustring entry_name = j->getEntryName();
4397 if (entry_name == "id" || entry_name == "name") continue;
4399 void *widget = g_object_get_data(tbl, entry_name.data());
4400 if (widget) {
4401 if (GTK_IS_ADJUSTMENT(widget)) {
4402 double v = j->getDouble();
4403 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4404 //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
4405 if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
4406 match = false;
4407 break;
4408 }
4409 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4410 bool v = j->getBool();
4411 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4412 //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
4413 if ( static_cast<bool>(gtk_toggle_action_get_active(toggle)) != v ) {
4414 match = false;
4415 break;
4416 }
4417 }
4418 }
4419 }
4421 if (match) {
4422 // newly added item is at the same index as the
4423 // save command, so we need to change twice for it to take effect
4424 ege_select_one_action_set_active(sel, 0);
4425 ege_select_one_action_set_active(sel, ege_index); // one-based index
4426 return;
4427 }
4428 }
4430 // no match found
4431 ege_select_one_action_set_active(sel, 0);
4432 }
4434 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
4435 {
4436 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4437 prefs->setDouble( "/tools/calligraphic/mass", adj->value * 0.01 );
4438 update_presets_list(tbl);
4439 }
4441 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
4442 {
4443 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4444 prefs->setDouble( "/tools/calligraphic/wiggle", adj->value * 0.01 );
4445 update_presets_list(tbl);
4446 }
4448 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
4449 {
4450 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4451 prefs->setDouble( "/tools/calligraphic/angle", adj->value );
4452 update_presets_list(tbl);
4453 }
4455 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
4456 {
4457 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4458 prefs->setDouble( "/tools/calligraphic/width", adj->value * 0.01 );
4459 update_presets_list(tbl);
4460 }
4462 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
4463 {
4464 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4465 prefs->setDouble("/tools/calligraphic/thinning", adj->value * 0.01 );
4466 update_presets_list(tbl);
4467 }
4469 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
4470 {
4471 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4472 prefs->setDouble( "/tools/calligraphic/flatness", adj->value * 0.01);
4473 update_presets_list(tbl);
4474 }
4476 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
4477 {
4478 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4479 prefs->setDouble( "/tools/calligraphic/tremor", adj->value * 0.01 );
4480 update_presets_list(tbl);
4481 }
4483 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
4484 {
4485 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4486 prefs->setDouble( "/tools/calligraphic/cap_rounding", adj->value );
4487 update_presets_list(tbl);
4488 }
4490 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject* tbl )
4491 {
4492 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4493 prefs->setBool("/tools/calligraphic/usepressure", gtk_toggle_action_get_active( act ));
4494 update_presets_list(tbl);
4495 }
4497 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject* tbl )
4498 {
4499 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4500 prefs->setBool("/tools/calligraphic/tracebackground", gtk_toggle_action_get_active( act ));
4501 update_presets_list(tbl);
4502 }
4504 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject* tbl )
4505 {
4506 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4507 GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
4508 prefs->setBool("/tools/calligraphic/usetilt", gtk_toggle_action_get_active( act ));
4509 update_presets_list(tbl);
4510 if (calligraphy_angle )
4511 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
4512 }
4515 static gchar const *const widget_names[] = {
4516 "width",
4517 "mass",
4518 "wiggle",
4519 "angle",
4520 "thinning",
4521 "tremor",
4522 "flatness",
4523 "cap_rounding",
4524 "usepressure",
4525 "tracebackground",
4526 "usetilt"
4527 };
4530 static void sp_dcc_build_presets_list(GObject *tbl)
4531 {
4532 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4534 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4535 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4536 gtk_list_store_clear (model);
4538 {
4539 GtkTreeIter iter;
4540 gtk_list_store_append( model, &iter );
4541 gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4542 }
4544 // iterate over all presets to populate the list
4545 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4546 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4547 int ii=1;
4549 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i) {
4550 GtkTreeIter iter;
4551 Glib::ustring preset_name = prefs->getString(*i + "/name");
4552 gtk_list_store_append( model, &iter );
4553 gtk_list_store_set( model, &iter, 0, preset_name.data(), 1, ii++, -1 );
4554 }
4556 {
4557 GtkTreeIter iter;
4558 gtk_list_store_append( model, &iter );
4559 gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4560 g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4561 }
4563 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4565 update_presets_list (tbl);
4566 }
4568 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4569 {
4570 using Inkscape::UI::Dialog::CalligraphicProfileRename;
4571 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4572 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4573 if (! desktop) return;
4575 if (g_object_get_data(tbl, "presets_blocked"))
4576 return;
4578 CalligraphicProfileRename::show(desktop);
4579 if ( !CalligraphicProfileRename::applied()) {
4580 // dialog cancelled
4581 update_presets_list (tbl);
4582 return;
4583 }
4584 Glib::ustring profile_name = CalligraphicProfileRename::getProfileName();
4586 if (profile_name.empty()) {
4587 // empty name entered
4588 update_presets_list (tbl);
4589 return;
4590 }
4592 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4594 // If there's a preset with the given name, find it and set save_path appropriately
4595 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4596 int total_presets = presets.size();
4597 int new_index = -1;
4598 Glib::ustring save_path; // profile pref path without a trailing slash
4600 int temp_index = 0;
4601 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++temp_index) {
4602 Glib::ustring name = prefs->getString(*i + "/name");
4603 if (!name.empty() && profile_name == name) {
4604 new_index = temp_index;
4605 save_path = *i;
4606 break;
4607 }
4608 }
4610 if (new_index == -1) {
4611 // no preset with this name, create
4612 new_index = total_presets + 1;
4613 gchar *profile_id = g_strdup_printf("/dcc%d", new_index);
4614 save_path = Glib::ustring("/tools/calligraphic/preset") + profile_id;
4615 g_free(profile_id);
4616 }
4618 for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4619 gchar const *const widget_name = widget_names[i];
4620 void *widget = g_object_get_data(tbl, widget_name);
4621 if (widget) {
4622 if (GTK_IS_ADJUSTMENT(widget)) {
4623 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4624 prefs->setDouble(save_path + "/" + widget_name, gtk_adjustment_get_value(adj));
4625 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4626 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4627 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4628 prefs->setBool(save_path + "/" + widget_name, gtk_toggle_action_get_active(toggle));
4629 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4630 } else {
4631 g_warning("Unknown widget type for preset: %s\n", widget_name);
4632 }
4633 } else {
4634 g_warning("Bad key when writing preset: %s\n", widget_name);
4635 }
4636 }
4637 prefs->setString(save_path + "/name", profile_name);
4639 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4640 sp_dcc_build_presets_list (tbl);
4641 }
4644 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4646 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4648 gint preset_index = ege_select_one_action_get_active( act );
4649 // This is necessary because EgeSelectOneAction spams us with GObject "changed" signal calls
4650 // even when the preset is not changed. It would be good to replace it with something more
4651 // modern. Index 0 means "No preset", so we don't do anything.
4652 if (preset_index == 0) return;
4654 gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4656 if (preset_index == save_presets_index) {
4657 // this is the Save command
4658 sp_dcc_save_profile(NULL, tbl);
4659 return;
4660 }
4662 if (g_object_get_data(tbl, "presets_blocked"))
4663 return;
4665 // preset_index is one-based so we subtract 1
4666 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4667 Glib::ustring preset_path = presets.at(preset_index - 1);
4669 if (!preset_path.empty()) {
4670 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
4672 std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(preset_path);
4674 // Shouldn't this be std::map?
4675 for (std::vector<Inkscape::Preferences::Entry>::iterator i = preset.begin(); i != preset.end(); ++i) {
4676 Glib::ustring entry_name = i->getEntryName();
4677 if (entry_name == "id" || entry_name == "name") continue;
4678 void *widget = g_object_get_data(tbl, entry_name.data());
4679 if (widget) {
4680 if (GTK_IS_ADJUSTMENT(widget)) {
4681 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4682 gtk_adjustment_set_value(adj, i->getDouble());
4683 //std::cout << "set adj " << attr_name << " to " << v << "\n";
4684 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4685 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4686 gtk_toggle_action_set_active(toggle, i->getBool());
4687 //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4688 } else {
4689 g_warning("Unknown widget type for preset: %s\n", entry_name.data());
4690 }
4691 } else {
4692 g_warning("Bad key found in a preset record: %s\n", entry_name.data());
4693 }
4694 }
4695 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4696 }
4697 }
4700 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4701 {
4702 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4703 {
4704 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4706 EgeAdjustmentAction* calligraphy_angle = 0;
4708 {
4709 /* Width */
4710 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4711 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4712 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4713 _("Pen Width"), _("Width:"),
4714 _("The width of the calligraphic pen (relative to the visible canvas area)"),
4715 "/tools/calligraphic/width", 15,
4716 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4717 1, 100, 1.0, 10.0,
4718 labels, values, G_N_ELEMENTS(labels),
4719 sp_ddc_width_value_changed, 0.01, 0, 100 );
4720 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4721 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4722 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4723 }
4725 {
4726 /* Thinning */
4727 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4728 gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4729 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4730 _("Stroke Thinning"), _("Thinning:"),
4731 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4732 "/tools/calligraphic/thinning", 10,
4733 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4734 -100, 100, 1, 10.0,
4735 labels, values, G_N_ELEMENTS(labels),
4736 sp_ddc_velthin_value_changed, 0.01, 0, 100);
4737 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4738 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4739 }
4741 {
4742 /* Angle */
4743 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4744 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4745 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4746 _("Pen Angle"), _("Angle:"),
4747 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4748 "/tools/calligraphic/angle", 30,
4749 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4750 -90.0, 90.0, 1.0, 10.0,
4751 labels, values, G_N_ELEMENTS(labels),
4752 sp_ddc_angle_value_changed, 1, 0 );
4753 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4754 g_object_set_data( holder, "angle_action", eact );
4755 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4756 calligraphy_angle = eact;
4757 }
4759 {
4760 /* Fixation */
4761 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4762 gdouble values[] = {0, 20, 40, 60, 90, 100};
4763 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4764 _("Fixation"), _("Fixation:"),
4765 _("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"),
4766 "/tools/calligraphic/flatness", 90,
4767 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4768 0.0, 100, 1.0, 10.0,
4769 labels, values, G_N_ELEMENTS(labels),
4770 sp_ddc_flatness_value_changed, 0.01, 0, 100 );
4771 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4772 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4773 }
4775 {
4776 /* Cap Rounding */
4777 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4778 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4779 // TRANSLATORS: "cap" means "end" (both start and finish) here
4780 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4781 _("Cap rounding"), _("Caps:"),
4782 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4783 "/tools/calligraphic/cap_rounding", 0.0,
4784 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4785 0.0, 5.0, 0.01, 0.1,
4786 labels, values, G_N_ELEMENTS(labels),
4787 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4788 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4789 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4790 }
4792 {
4793 /* Tremor */
4794 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4795 gdouble values[] = {0, 10, 20, 40, 60, 100};
4796 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4797 _("Stroke Tremor"), _("Tremor:"),
4798 _("Increase to make strokes rugged and trembling"),
4799 "/tools/calligraphic/tremor", 0.0,
4800 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4801 0.0, 100, 1, 10.0,
4802 labels, values, G_N_ELEMENTS(labels),
4803 sp_ddc_tremor_value_changed, 0.01, 0, 100 );
4805 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4806 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4807 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4808 }
4810 {
4811 /* Wiggle */
4812 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4813 gdouble values[] = {0, 20, 40, 60, 100};
4814 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4815 _("Pen Wiggle"), _("Wiggle:"),
4816 _("Increase to make the pen waver and wiggle"),
4817 "/tools/calligraphic/wiggle", 0.0,
4818 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4819 0.0, 100, 1, 10.0,
4820 labels, values, G_N_ELEMENTS(labels),
4821 sp_ddc_wiggle_value_changed, 0.01, 0, 100 );
4822 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4823 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4824 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4825 }
4827 {
4828 /* Mass */
4829 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4830 gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4831 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4832 _("Pen Mass"), _("Mass:"),
4833 _("Increase to make the pen drag behind, as if slowed by inertia"),
4834 "/tools/calligraphic/mass", 2.0,
4835 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4836 0.0, 100, 1, 10.0,
4837 labels, values, G_N_ELEMENTS(labels),
4838 sp_ddc_mass_value_changed, 0.01, 0, 100 );
4839 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4840 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4841 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4842 }
4845 /* Trace Background button */
4846 {
4847 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4848 _("Trace Background"),
4849 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4850 INKSCAPE_ICON_DRAW_TRACE_BACKGROUND,
4851 Inkscape::ICON_SIZE_DECORATION );
4852 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4853 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4854 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/tracebackground", false) );
4855 g_object_set_data( holder, "tracebackground", act );
4856 }
4858 /* Use Pressure button */
4859 {
4860 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4861 _("Pressure"),
4862 _("Use the pressure of the input device to alter the width of the pen"),
4863 INKSCAPE_ICON_DRAW_USE_PRESSURE,
4864 Inkscape::ICON_SIZE_DECORATION );
4865 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4866 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4867 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usepressure", true) );
4868 g_object_set_data( holder, "usepressure", act );
4869 }
4871 /* Use Tilt button */
4872 {
4873 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4874 _("Tilt"),
4875 _("Use the tilt of the input device to alter the angle of the pen's nib"),
4876 INKSCAPE_ICON_DRAW_USE_TILT,
4877 Inkscape::ICON_SIZE_DECORATION );
4878 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4879 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4880 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs->getBool("/tools/calligraphic/usetilt", true) );
4881 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usetilt", true) );
4882 g_object_set_data( holder, "usetilt", act );
4883 }
4885 /*calligraphic profile */
4886 {
4887 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
4888 EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
4889 ege_select_one_action_set_appearance (act1, "compact");
4890 g_object_set_data (holder, "profile_selector", act1 );
4892 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
4894 sp_dcc_build_presets_list (holder);
4896 g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
4897 gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
4898 }
4899 }
4900 }
4903 //########################
4904 //## Circle / Arc ##
4905 //########################
4907 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4908 {
4909 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4910 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4912 if (v1 == 0 && v2 == 0) {
4913 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4914 gtk_action_set_sensitive( ocb, FALSE );
4915 gtk_action_set_sensitive( make_whole, FALSE );
4916 }
4917 } else {
4918 gtk_action_set_sensitive( ocb, TRUE );
4919 gtk_action_set_sensitive( make_whole, TRUE );
4920 }
4921 }
4923 static void
4924 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4925 {
4926 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4928 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4929 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4930 prefs->setDouble(Glib::ustring("/tools/shapes/arc") + value_name, (adj->value * M_PI)/ 180);
4931 }
4933 // quit if run by the attr_changed listener
4934 if (g_object_get_data( tbl, "freeze" )) {
4935 return;
4936 }
4938 // in turn, prevent listener from responding
4939 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4941 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4943 bool modmade = false;
4944 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4945 items != NULL;
4946 items = items->next)
4947 {
4948 SPItem *item = SP_ITEM(items->data);
4950 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4952 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4953 SPArc *arc = SP_ARC(item);
4955 if (!strcmp(value_name, "start"))
4956 ge->start = (adj->value * M_PI)/ 180;
4957 else
4958 ge->end = (adj->value * M_PI)/ 180;
4960 sp_genericellipse_normalize(ge);
4961 ((SPObject *)arc)->updateRepr();
4962 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4964 modmade = true;
4965 }
4966 }
4968 g_free(namespaced_name);
4970 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4972 sp_arctb_sensitivize( tbl, adj->value, other->value );
4974 if (modmade) {
4975 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4976 _("Arc: Change start/end"));
4977 }
4979 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4980 }
4983 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
4984 {
4985 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
4986 }
4988 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4989 {
4990 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
4991 }
4994 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4995 {
4996 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4997 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4998 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4999 prefs->setBool("/tools/shapes/arc/open", ege_select_one_action_get_active(act) != 0);
5000 }
5002 // quit if run by the attr_changed listener
5003 if (g_object_get_data( tbl, "freeze" )) {
5004 return;
5005 }
5007 // in turn, prevent listener from responding
5008 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5010 bool modmade = false;
5012 if ( ege_select_one_action_get_active(act) != 0 ) {
5013 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5014 items != NULL;
5015 items = items->next)
5016 {
5017 if (SP_IS_ARC((SPItem *) items->data)) {
5018 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5019 repr->setAttribute("sodipodi:open", "true");
5020 SP_OBJECT((SPItem *) items->data)->updateRepr();
5021 modmade = true;
5022 }
5023 }
5024 } else {
5025 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5026 items != NULL;
5027 items = items->next)
5028 {
5029 if (SP_IS_ARC((SPItem *) items->data)) {
5030 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5031 repr->setAttribute("sodipodi:open", NULL);
5032 SP_OBJECT((SPItem *) items->data)->updateRepr();
5033 modmade = true;
5034 }
5035 }
5036 }
5038 if (modmade) {
5039 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
5040 _("Arc: Change open/closed"));
5041 }
5043 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5044 }
5046 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
5047 {
5048 GtkAdjustment *adj;
5049 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
5050 gtk_adjustment_set_value(adj, 0.0);
5051 gtk_adjustment_value_changed(adj);
5053 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
5054 gtk_adjustment_set_value(adj, 0.0);
5055 gtk_adjustment_value_changed(adj);
5057 spinbutton_defocus( GTK_OBJECT(obj) );
5058 }
5060 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
5061 gchar const */*old_value*/, gchar const */*new_value*/,
5062 bool /*is_interactive*/, gpointer data)
5063 {
5064 GObject *tbl = G_OBJECT(data);
5066 // quit if run by the _changed callbacks
5067 if (g_object_get_data( tbl, "freeze" )) {
5068 return;
5069 }
5071 // in turn, prevent callbacks from responding
5072 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5074 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
5075 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
5077 GtkAdjustment *adj1,*adj2;
5078 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
5079 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
5080 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
5081 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
5083 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
5085 char const *openstr = NULL;
5086 openstr = repr->attribute("sodipodi:open");
5087 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
5089 if (openstr) {
5090 ege_select_one_action_set_active( ocb, 1 );
5091 } else {
5092 ege_select_one_action_set_active( ocb, 0 );
5093 }
5095 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5096 }
5098 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
5099 NULL, /* child_added */
5100 NULL, /* child_removed */
5101 arc_tb_event_attr_changed,
5102 NULL, /* content_changed */
5103 NULL /* order_changed */
5104 };
5107 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
5108 {
5109 int n_selected = 0;
5110 Inkscape::XML::Node *repr = NULL;
5112 purge_repr_listener( tbl, tbl );
5114 for (GSList const *items = selection->itemList();
5115 items != NULL;
5116 items = items->next)
5117 {
5118 if (SP_IS_ARC((SPItem *) items->data)) {
5119 n_selected++;
5120 repr = SP_OBJECT_REPR((SPItem *) items->data);
5121 }
5122 }
5124 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
5126 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
5127 if (n_selected == 0) {
5128 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
5129 } else if (n_selected == 1) {
5130 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
5131 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5133 if (repr) {
5134 g_object_set_data( tbl, "repr", repr );
5135 Inkscape::GC::anchor(repr);
5136 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
5137 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
5138 }
5139 } else {
5140 // FIXME: implement averaging of all parameters for multiple selected
5141 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
5142 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5143 sp_arctb_sensitivize( tbl, 1, 0 );
5144 }
5145 }
5148 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5149 {
5150 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5152 EgeAdjustmentAction* eact = 0;
5153 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
5156 {
5157 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
5158 ege_output_action_set_use_markup( act, TRUE );
5159 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5160 g_object_set_data( holder, "mode_action", act );
5161 }
5163 /* Start */
5164 {
5165 eact = create_adjustment_action( "ArcStartAction",
5166 _("Start"), _("Start:"),
5167 _("The angle (in degrees) from the horizontal to the arc's start point"),
5168 "/tools/shapes/arc/start", 0.0,
5169 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
5170 -360.0, 360.0, 1.0, 10.0,
5171 0, 0, 0,
5172 sp_arctb_start_value_changed);
5173 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5174 }
5176 /* End */
5177 {
5178 eact = create_adjustment_action( "ArcEndAction",
5179 _("End"), _("End:"),
5180 _("The angle (in degrees) from the horizontal to the arc's end point"),
5181 "/tools/shapes/arc/end", 0.0,
5182 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
5183 -360.0, 360.0, 1.0, 10.0,
5184 0, 0, 0,
5185 sp_arctb_end_value_changed);
5186 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5187 }
5189 /* Segments / Pie checkbox */
5190 {
5191 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5193 GtkTreeIter iter;
5194 gtk_list_store_append( model, &iter );
5195 gtk_list_store_set( model, &iter,
5196 0, _("Closed arc"),
5197 1, _("Switch to segment (closed shape with two radii)"),
5198 2, INKSCAPE_ICON_DRAW_ELLIPSE_SEGMENT,
5199 -1 );
5201 gtk_list_store_append( model, &iter );
5202 gtk_list_store_set( model, &iter,
5203 0, _("Open Arc"),
5204 1, _("Switch to arc (unclosed shape)"),
5205 2, INKSCAPE_ICON_DRAW_ELLIPSE_ARC,
5206 -1 );
5208 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5209 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5210 g_object_set_data( holder, "open_action", act );
5212 ege_select_one_action_set_appearance( act, "full" );
5213 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5214 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5215 ege_select_one_action_set_icon_column( act, 2 );
5216 ege_select_one_action_set_icon_size( act, secondarySize );
5217 ege_select_one_action_set_tooltip_column( act, 1 );
5219 bool isClosed = !prefs->getBool("/tools/shapes/arc/open", false);
5220 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
5221 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
5222 }
5224 /* Make Whole */
5225 {
5226 InkAction* inky = ink_action_new( "ArcResetAction",
5227 _("Make whole"),
5228 _("Make the shape a whole ellipse, not arc or segment"),
5229 INKSCAPE_ICON_DRAW_ELLIPSE_WHOLE,
5230 secondarySize );
5231 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
5232 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5233 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
5234 g_object_set_data( holder, "make_whole", inky );
5235 }
5237 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
5238 // sensitivize make whole and open checkbox
5239 {
5240 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
5241 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
5242 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
5243 }
5246 sigc::connection *connection = new sigc::connection(
5247 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
5248 );
5249 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
5250 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
5251 }
5256 // toggle button callbacks and updaters
5258 //########################
5259 //## Dropper ##
5260 //########################
5262 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
5263 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5264 prefs->setInt( "/tools/dropper/pick", gtk_toggle_action_get_active( act ) );
5265 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
5266 if ( set_action ) {
5267 if ( gtk_toggle_action_get_active( act ) ) {
5268 gtk_action_set_sensitive( set_action, TRUE );
5269 } else {
5270 gtk_action_set_sensitive( set_action, FALSE );
5271 }
5272 }
5274 spinbutton_defocus(GTK_OBJECT(tbl));
5275 }
5277 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
5278 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5279 prefs->setBool( "/tools/dropper/setalpha", gtk_toggle_action_get_active( act ) );
5280 spinbutton_defocus(GTK_OBJECT(tbl));
5281 }
5284 /**
5285 * Dropper auxiliary toolbar construction and setup.
5286 *
5287 * TODO: Would like to add swatch of current color.
5288 * TODO: Add queue of last 5 or so colors selected with new swatches so that
5289 * can drag and drop places. Will provide a nice mixing palette.
5290 */
5291 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
5292 {
5293 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5294 gint pickAlpha = prefs->getInt( "/tools/dropper/pick", 1 );
5296 {
5297 EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
5298 ege_output_action_set_use_markup( act, TRUE );
5299 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5300 }
5302 {
5303 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
5304 _("Pick opacity"),
5305 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
5306 NULL,
5307 Inkscape::ICON_SIZE_DECORATION );
5308 g_object_set( act, "short_label", _("Pick"), NULL );
5309 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5310 g_object_set_data( holder, "pick_action", act );
5311 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
5312 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
5313 }
5315 {
5316 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
5317 _("Assign opacity"),
5318 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
5319 NULL,
5320 Inkscape::ICON_SIZE_DECORATION );
5321 g_object_set( act, "short_label", _("Assign"), NULL );
5322 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5323 g_object_set_data( holder, "set_action", act );
5324 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/dropper/setalpha", true) );
5325 // make sure it's disabled if we're not picking alpha
5326 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
5327 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
5328 }
5329 }
5332 //########################
5333 //## LPETool ##
5334 //########################
5336 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
5338 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
5339 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
5340 {
5341 using namespace Inkscape::LivePathEffect;
5343 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
5344 SPEventContext *ec = desktop->event_context;
5345 if (!SP_IS_LPETOOL_CONTEXT(ec)) {
5346 return;
5347 }
5349 // only take action if run by the attr_changed listener
5350 if (!g_object_get_data(tbl, "freeze")) {
5351 // in turn, prevent listener from responding
5352 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5354 gint mode = ege_select_one_action_get_active(act);
5355 EffectType type = lpesubtools[mode].type;
5357 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5358 bool success = lpetool_try_construction(lc, type);
5359 if (success) {
5360 // since the construction was already performed, we set the state back to inactive
5361 ege_select_one_action_set_active(act, 0);
5362 mode = 0;
5363 } else {
5364 // switch to the chosen subtool
5365 SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
5366 }
5368 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5369 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5370 prefs->setInt( "/tools/lpetool/mode", mode );
5371 }
5373 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
5374 }
5375 }
5377 void sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject */*tbl*/)
5378 {
5379 SPEventContext *ec = selection->desktop()->event_context;
5380 if (!SP_IS_LPETOOL_CONTEXT(ec))
5381 return;
5383 lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
5384 }
5386 void
5387 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
5388 {
5389 using namespace Inkscape::LivePathEffect;
5390 SPEventContext *ec = selection->desktop()->event_context;
5391 if (!SP_IS_LPETOOL_CONTEXT(ec))
5392 return;
5393 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
5395 lpetool_delete_measuring_items(lc);
5396 lpetool_create_measuring_items(lc, selection);
5398 // activate line segment combo box if a single item with LPELineSegment is selected
5399 GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
5400 SPItem *item = selection->singleItem();
5401 if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
5402 SPLPEItem *lpeitem = SP_LPE_ITEM(item);
5403 Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
5404 if (lpe && lpe->effectType() == LINE_SEGMENT) {
5405 LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
5406 g_object_set_data(tbl, "currentlpe", lpe);
5407 g_object_set_data(tbl, "currentlpeitem", lpeitem);
5408 gtk_action_set_sensitive(w, TRUE);
5409 ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
5410 } else {
5411 g_object_set_data(tbl, "currentlpe", NULL);
5412 g_object_set_data(tbl, "currentlpeitem", NULL);
5413 gtk_action_set_sensitive(w, FALSE);
5414 }
5415 } else {
5416 g_object_set_data(tbl, "currentlpe", NULL);
5417 g_object_set_data(tbl, "currentlpeitem", NULL);
5418 gtk_action_set_sensitive(w, FALSE);
5419 }
5420 }
5422 static void
5423 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
5424 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5425 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5427 bool show = gtk_toggle_action_get_active( act );
5428 prefs->setBool("/tools/lpetool/show_bbox", show);
5430 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5431 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5432 lpetool_context_reset_limiting_bbox(lc);
5433 }
5434 }
5436 static void
5437 lpetool_toggle_show_measuring_info (GtkToggleAction *act, GObject *tbl) {
5438 SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
5439 if (!tools_isactive(desktop, TOOLS_LPETOOL))
5440 return;
5442 GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
5443 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5444 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5445 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5446 bool show = gtk_toggle_action_get_active( act );
5447 prefs->setBool("/tools/lpetool/show_measuring_info", show);
5448 lpetool_show_measuring_info(lc, show);
5449 gtk_action_set_sensitive(GTK_ACTION(unitact), show);
5450 }
5451 }
5453 static void lpetool_unit_changed(GtkAction* /*act*/, GObject* tbl) {
5454 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
5455 SPUnit const *unit = tracker->getActiveUnit();
5456 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5457 prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5459 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5460 if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
5461 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5462 lpetool_delete_measuring_items(lc);
5463 lpetool_create_measuring_items(lc);
5464 }
5465 }
5467 static void
5468 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
5469 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5470 Inkscape::Selection *selection = desktop->selection;
5472 Geom::OptRect bbox = selection->bounds();
5474 if (bbox) {
5475 Geom::Point A(bbox->min());
5476 Geom::Point B(bbox->max());
5478 A *= desktop->doc2dt();
5479 B *= desktop->doc2dt();
5481 // TODO: should we provide a way to store points in prefs?
5482 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5483 prefs->setDouble("/tools/lpetool/bbox_upperleftx", A[Geom::X]);
5484 prefs->setDouble("/tools/lpetool/bbox_upperlefty", A[Geom::Y]);
5485 prefs->setDouble("/tools/lpetool/bbox_lowerrightx", B[Geom::X]);
5486 prefs->setDouble("/tools/lpetool/bbox_lowerrighty", B[Geom::Y]);
5488 lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
5489 }
5491 gtk_toggle_action_set_active(act, false);
5492 }
5494 static void
5495 sp_line_segment_build_list(GObject *tbl)
5496 {
5497 g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
5499 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
5500 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
5501 gtk_list_store_clear (model);
5503 // TODO: we add the entries of rht combo box manually; later this should be done automatically
5504 {
5505 GtkTreeIter iter;
5506 gtk_list_store_append( model, &iter );
5507 gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
5508 gtk_list_store_append( model, &iter );
5509 gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
5510 gtk_list_store_append( model, &iter );
5511 gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
5512 gtk_list_store_append( model, &iter );
5513 gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
5514 }
5516 g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5517 }
5519 static void
5520 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
5521 using namespace Inkscape::LivePathEffect;
5523 // quit if run by the attr_changed listener
5524 if (g_object_get_data(tbl, "freeze")) {
5525 return;
5526 }
5528 // in turn, prevent listener from responding
5529 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5531 LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
5532 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5533 if (lpeitem) {
5534 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5535 lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
5536 sp_lpe_item_update_patheffect(lpeitem, true, true);
5537 }
5539 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5540 }
5542 static void
5543 lpetool_open_lpe_dialog (GtkToggleAction *act, gpointer data) {
5544 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5546 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5547 sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5548 }
5549 gtk_toggle_action_set_active(act, false);
5550 }
5552 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5553 {
5554 UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5555 tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5556 g_object_set_data(holder, "tracker", tracker);
5557 SPUnit const *unit = tracker->getActiveUnit();
5559 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5560 prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5562 /** Automatically create a list of LPEs that get added to the toolbar **/
5563 {
5564 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5566 GtkTreeIter iter;
5568 // the first toggle button represents the state that no subtool is active (remove this when
5569 // this can be modeled by EgeSelectOneAction or some other action)
5570 gtk_list_store_append( model, &iter );
5571 gtk_list_store_set( model, &iter,
5572 0, _("All inactive"),
5573 1, _("No geometric tool is active"),
5574 2, _("draw-geometry-inactive"),
5575 -1 );
5577 Inkscape::LivePathEffect::EffectType type;
5578 for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5579 type = lpesubtools[i].type;
5580 gtk_list_store_append( model, &iter );
5581 gtk_list_store_set( model, &iter,
5582 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5583 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5584 2, lpesubtools[i].icon_name,
5585 -1 );
5586 }
5588 EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5589 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5590 g_object_set_data( holder, "lpetool_mode_action", act );
5592 ege_select_one_action_set_appearance( act, "full" );
5593 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5594 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5595 ege_select_one_action_set_icon_column( act, 2 );
5596 ege_select_one_action_set_tooltip_column( act, 1 );
5598 gint lpeToolMode = prefs->getInt("/tools/lpetool/mode", 0);
5599 ege_select_one_action_set_active( act, lpeToolMode );
5600 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5601 }
5603 /* Show limiting bounding box */
5604 {
5605 InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5606 _("Show limiting bounding box"),
5607 _("Show bounding box (used to cut infinite lines)"),
5608 "show-bounding-box",
5609 Inkscape::ICON_SIZE_DECORATION );
5610 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5611 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5612 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_bbox", true ) );
5613 }
5615 /* Set limiting bounding box to bbox of current selection */
5616 {
5617 InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5618 _("Get limiting bounding box from selection"),
5619 _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5620 "draw-geometry-set-bounding-box",
5621 Inkscape::ICON_SIZE_DECORATION );
5622 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5623 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5624 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5625 }
5628 /* Combo box to choose line segment type */
5629 {
5630 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5631 EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5632 ege_select_one_action_set_appearance (act, "compact");
5633 g_object_set_data (holder, "lpetool_line_segment_action", act );
5635 g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5637 sp_line_segment_build_list (holder);
5639 g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5640 gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5641 gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5642 }
5644 /* Display measuring info for selected items */
5645 {
5646 InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5647 _("Display measuring info"),
5648 _("Display measuring info for selected items"),
5649 "draw-geometry-show-measuring-info",
5650 Inkscape::ICON_SIZE_DECORATION );
5651 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5652 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
5653 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_measuring_info", true ) );
5654 }
5656 // add the units menu
5657 {
5658 GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5659 gtk_action_group_add_action( mainActions, act );
5660 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5661 g_object_set_data(holder, "lpetool_units_action", act);
5662 gtk_action_set_sensitive(act, prefs->getBool("/tools/lpetool/show_measuring_info", true));
5663 }
5665 /* Open LPE dialog (to adapt parameters numerically) */
5666 {
5667 InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
5668 _("Open LPE dialog"),
5669 _("Open LPE dialog (to adapt parameters numerically)"),
5670 "dialog-geometry",
5671 Inkscape::ICON_SIZE_DECORATION );
5672 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5673 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
5674 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5675 }
5677 //watch selection
5678 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5680 sigc::connection *c_selection_modified =
5681 new sigc::connection (sp_desktop_selection (desktop)->connectModified
5682 (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
5683 pool->add_connection ("selection-modified", c_selection_modified);
5685 sigc::connection *c_selection_changed =
5686 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5687 (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5688 pool->add_connection ("selection-changed", c_selection_changed);
5689 }
5691 //########################
5692 //## Eraser ##
5693 //########################
5695 static void sp_erc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
5696 {
5697 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5698 prefs->setDouble( "/tools/eraser/width", adj->value * 0.01 );
5699 update_presets_list(tbl);
5700 }
5702 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5703 {
5704 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5705 bool eraserMode = ege_select_one_action_get_active( act ) != 0;
5706 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5707 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5708 prefs->setBool( "/tools/eraser/mode", eraserMode );
5709 }
5711 // only take action if run by the attr_changed listener
5712 if (!g_object_get_data( tbl, "freeze" )) {
5713 // in turn, prevent listener from responding
5714 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5716 if ( eraserMode != 0 ) {
5717 } else {
5718 }
5719 // TODO finish implementation
5721 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5722 }
5723 }
5725 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5726 {
5727 {
5728 /* Width */
5729 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5730 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5731 EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5732 _("Pen Width"), _("Width:"),
5733 _("The width of the eraser pen (relative to the visible canvas area)"),
5734 "/tools/eraser/width", 15,
5735 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5736 1, 100, 1.0, 10.0,
5737 labels, values, G_N_ELEMENTS(labels),
5738 sp_erc_width_value_changed, 0.01, 0, 100 );
5739 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5740 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5741 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5742 }
5744 {
5745 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5747 GtkTreeIter iter;
5748 gtk_list_store_append( model, &iter );
5749 gtk_list_store_set( model, &iter,
5750 0, _("Delete"),
5751 1, _("Delete objects touched by the eraser"),
5752 2, INKSCAPE_ICON_DRAW_ERASER_DELETE_OBJECTS,
5753 -1 );
5755 gtk_list_store_append( model, &iter );
5756 gtk_list_store_set( model, &iter,
5757 0, _("Cut"),
5758 1, _("Cut out from objects"),
5759 2, INKSCAPE_ICON_PATH_DIFFERENCE,
5760 -1 );
5762 EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5763 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5764 g_object_set_data( holder, "eraser_mode_action", act );
5766 ege_select_one_action_set_appearance( act, "full" );
5767 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5768 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5769 ege_select_one_action_set_icon_column( act, 2 );
5770 ege_select_one_action_set_tooltip_column( act, 1 );
5772 /// @todo Convert to boolean?
5773 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5774 gint eraserMode = prefs->getBool("/tools/eraser/mode") ? 1 : 0;
5775 ege_select_one_action_set_active( act, eraserMode );
5776 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
5777 }
5779 }
5781 //########################
5782 //## Text Toolbox ##
5783 //########################
5784 /*
5785 static void
5786 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
5787 {
5788 //Call back for letter sizing spinbutton
5789 }
5791 static void
5792 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
5793 {
5794 //Call back for line height spinbutton
5795 }
5797 static void
5798 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5799 {
5800 //Call back for horizontal kerning spinbutton
5801 }
5803 static void
5804 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5805 {
5806 //Call back for vertical kerning spinbutton
5807 }
5809 static void
5810 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
5811 {
5812 //Call back for letter rotation spinbutton
5813 }*/
5815 namespace {
5817 void
5818 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
5819 {
5820 // quit if run by the _changed callbacks
5821 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5822 return;
5823 }
5825 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5827 SPStyle *query =
5828 sp_style_new (SP_ACTIVE_DOCUMENT);
5830 int result_family =
5831 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5833 int result_style =
5834 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5836 int result_numbers =
5837 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5839 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5841 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5842 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
5843 // there are no texts in selection, read from prefs
5845 sp_style_read_from_prefs(query, "/tools/text");
5847 if (g_object_get_data(tbl, "text_style_from_prefs")) {
5848 // do not reset the toolbar style from prefs if we already did it last time
5849 sp_style_unref(query);
5850 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5851 return;
5852 }
5853 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
5854 } else {
5855 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
5856 }
5858 if (query->text)
5859 {
5860 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
5861 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5862 gtk_entry_set_text (GTK_ENTRY (entry), "");
5864 } else if (query->text->font_specification.value || query->text->font_family.value) {
5866 Gtk::ComboBoxEntry *combo = (Gtk::ComboBoxEntry *) (g_object_get_data (G_OBJECT (tbl), "family-entry-combo"));
5867 GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5869 // Get the font that corresponds
5870 Glib::ustring familyName;
5872 font_instance * font = font_factory::Default()->FaceFromStyle(query);
5873 if (font) {
5874 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
5875 font->Unref();
5876 font = NULL;
5877 }
5879 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
5881 Gtk::TreeIter iter;
5882 try {
5883 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
5884 Glib::RefPtr<Gtk::TreeModel> model = combo->get_model();
5885 iter = model->get_iter(path);
5886 } catch (...) {
5887 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
5888 sp_style_unref(query);
5889 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5890 return;
5891 }
5893 combo->set_active (iter);
5894 }
5896 //Size
5897 {
5898 GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
5899 gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
5900 gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
5901 g_free(str);
5902 }
5904 //Anchor
5905 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
5906 {
5907 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
5908 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5909 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5910 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5911 }
5912 else
5913 {
5914 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
5915 {
5916 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
5917 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5918 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5919 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5920 }
5921 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
5922 {
5923 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
5924 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5925 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5926 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5927 }
5928 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
5929 {
5930 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
5931 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5932 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5933 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5934 }
5935 }
5937 //Style
5938 {
5939 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
5941 gboolean active = gtk_toggle_button_get_active (button);
5942 gboolean check = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
5944 if (active != check)
5945 {
5946 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5947 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5948 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5949 }
5950 }
5952 {
5953 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
5955 gboolean active = gtk_toggle_button_get_active (button);
5956 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
5958 if (active != check)
5959 {
5960 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5961 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5962 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5963 }
5964 }
5966 //Orientation
5967 //locking both buttons, changing one affect all group (both)
5968 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
5969 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5971 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
5972 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
5974 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
5975 {
5976 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5977 }
5978 else
5979 {
5980 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
5981 }
5982 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5983 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
5984 }
5986 sp_style_unref(query);
5988 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5989 }
5991 void
5992 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
5993 {
5994 sp_text_toolbox_selection_changed (selection, tbl);
5995 }
5997 void
5998 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
5999 {
6000 sp_text_toolbox_selection_changed (NULL, tbl);
6001 }
6003 void
6004 sp_text_toolbox_family_changed (GtkComboBoxEntry *,
6005 GObject *tbl)
6006 {
6007 // quit if run by the _changed callbacks
6008 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6009 return;
6010 }
6012 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6014 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6015 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6016 const gchar* family = gtk_entry_get_text (GTK_ENTRY (entry));
6018 //g_print ("family changed to: %s\n", family);
6020 SPStyle *query =
6021 sp_style_new (SP_ACTIVE_DOCUMENT);
6023 int result_fontspec =
6024 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6026 SPCSSAttr *css = sp_repr_css_attr_new ();
6028 // First try to get the font spec from the stored value
6029 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
6031 if (fontSpec.empty()) {
6032 // Construct a new font specification if it does not yet exist
6033 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6034 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6035 fontFromStyle->Unref();
6036 }
6038 if (!fontSpec.empty()) {
6039 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
6040 if (!newFontSpec.empty() && fontSpec != newFontSpec) {
6041 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
6042 if (font) {
6043 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6045 // Set all the these just in case they were altered when finding the best
6046 // match for the new family and old style...
6048 gchar c[256];
6050 font->Family(c, 256);
6051 sp_repr_css_set_property (css, "font-family", c);
6053 font->Attribute( "weight", c, 256);
6054 sp_repr_css_set_property (css, "font-weight", c);
6056 font->Attribute("style", c, 256);
6057 sp_repr_css_set_property (css, "font-style", c);
6059 font->Attribute("stretch", c, 256);
6060 sp_repr_css_set_property (css, "font-stretch", c);
6062 font->Attribute("variant", c, 256);
6063 sp_repr_css_set_property (css, "font-variant", c);
6065 font->Unref();
6066 }
6067 }
6068 }
6070 // If querying returned nothing, set the default style of the tool (for new texts)
6071 if (result_fontspec == QUERY_STYLE_NOTHING)
6072 {
6073 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6074 prefs->setStyle("/tools/text/style", css);
6075 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
6076 }
6077 else
6078 {
6079 sp_desktop_set_style (desktop, css, true, true);
6080 }
6082 sp_style_unref(query);
6084 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6085 _("Text: Change font family"));
6086 sp_repr_css_attr_unref (css);
6088 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6090 // unfreeze
6091 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6093 // focus to canvas
6094 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6095 }
6098 void
6099 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
6100 gpointer data)
6101 {
6102 if (g_object_get_data (G_OBJECT (button), "block")) return;
6103 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
6104 int prop = GPOINTER_TO_INT(data);
6106 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6107 SPCSSAttr *css = sp_repr_css_attr_new ();
6109 switch (prop)
6110 {
6111 case 0:
6112 {
6113 sp_repr_css_set_property (css, "text-anchor", "start");
6114 sp_repr_css_set_property (css, "text-align", "start");
6115 break;
6116 }
6117 case 1:
6118 {
6119 sp_repr_css_set_property (css, "text-anchor", "middle");
6120 sp_repr_css_set_property (css, "text-align", "center");
6121 break;
6122 }
6124 case 2:
6125 {
6126 sp_repr_css_set_property (css, "text-anchor", "end");
6127 sp_repr_css_set_property (css, "text-align", "end");
6128 break;
6129 }
6131 case 3:
6132 {
6133 sp_repr_css_set_property (css, "text-anchor", "start");
6134 sp_repr_css_set_property (css, "text-align", "justify");
6135 break;
6136 }
6137 }
6139 SPStyle *query =
6140 sp_style_new (SP_ACTIVE_DOCUMENT);
6141 int result_numbers =
6142 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6144 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6145 if (result_numbers == QUERY_STYLE_NOTHING)
6146 {
6147 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6148 prefs->setStyle("/tools/text/style", css);
6149 }
6151 sp_style_unref(query);
6153 sp_desktop_set_style (desktop, css, true, true);
6154 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6155 _("Text: Change alignment"));
6156 sp_repr_css_attr_unref (css);
6158 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6159 }
6161 void
6162 sp_text_toolbox_style_toggled (GtkToggleButton *button,
6163 gpointer data)
6164 {
6165 if (g_object_get_data (G_OBJECT (button), "block")) return;
6167 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6168 SPCSSAttr *css = sp_repr_css_attr_new ();
6169 int prop = GPOINTER_TO_INT(data);
6170 bool active = gtk_toggle_button_get_active (button);
6172 SPStyle *query =
6173 sp_style_new (SP_ACTIVE_DOCUMENT);
6175 int result_fontspec =
6176 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6178 //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6179 //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6180 //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6182 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
6183 Glib::ustring newFontSpec = "";
6185 if (fontSpec.empty()) {
6186 // Construct a new font specification if it does not yet exist
6187 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6188 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6189 fontFromStyle->Unref();
6190 }
6192 switch (prop)
6193 {
6194 case 0:
6195 {
6196 if (!fontSpec.empty()) {
6197 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
6198 }
6199 if (fontSpec != newFontSpec) {
6200 // Don't even set the bold if the font didn't exist on the system
6201 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
6202 }
6203 break;
6204 }
6206 case 1:
6207 {
6208 if (!fontSpec.empty()) {
6209 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
6210 }
6211 if (fontSpec != newFontSpec) {
6212 // Don't even set the italic if the font didn't exist on the system
6213 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
6214 }
6215 break;
6216 }
6217 }
6219 if (!newFontSpec.empty()) {
6220 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6221 }
6223 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6224 if (result_fontspec == QUERY_STYLE_NOTHING)
6225 {
6226 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6227 prefs->setStyle("/tools/text/style", css);
6228 }
6230 sp_style_unref(query);
6232 sp_desktop_set_style (desktop, css, true, true);
6233 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6234 _("Text: Change font style"));
6235 sp_repr_css_attr_unref (css);
6237 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6238 }
6240 void
6241 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
6242 gpointer data)
6243 {
6244 if (g_object_get_data (G_OBJECT (button), "block")) {
6245 return;
6246 }
6248 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6249 SPCSSAttr *css = sp_repr_css_attr_new ();
6250 int prop = GPOINTER_TO_INT(data);
6252 switch (prop)
6253 {
6254 case 0:
6255 {
6256 sp_repr_css_set_property (css, "writing-mode", "lr");
6257 break;
6258 }
6260 case 1:
6261 {
6262 sp_repr_css_set_property (css, "writing-mode", "tb");
6263 break;
6264 }
6265 }
6267 SPStyle *query =
6268 sp_style_new (SP_ACTIVE_DOCUMENT);
6269 int result_numbers =
6270 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6272 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6273 if (result_numbers == QUERY_STYLE_NOTHING)
6274 {
6275 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6276 prefs->setStyle("/tools/text/style", css);
6277 }
6279 sp_desktop_set_style (desktop, css, true, true);
6280 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6281 _("Text: Change orientation"));
6282 sp_repr_css_attr_unref (css);
6284 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6285 }
6287 gboolean
6288 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6289 {
6290 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6291 if (!desktop) return FALSE;
6293 switch (get_group0_keyval (event)) {
6294 case GDK_KP_Enter: // chosen
6295 case GDK_Return:
6296 // unfreeze and update, which will defocus
6297 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6298 sp_text_toolbox_family_changed (NULL, tbl);
6299 return TRUE; // I consumed the event
6300 break;
6301 case GDK_Escape:
6302 // defocus
6303 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6304 return TRUE; // I consumed the event
6305 break;
6306 }
6307 return FALSE;
6308 }
6310 gboolean
6311 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
6312 {
6313 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6314 if (!desktop) return FALSE;
6316 switch (get_group0_keyval (event)) {
6317 case GDK_KP_Enter:
6318 case GDK_Return:
6319 case GDK_Escape: // defocus
6320 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6321 return TRUE; // I consumed the event
6322 break;
6323 case GDK_w:
6324 case GDK_W:
6325 if (event->state & GDK_CONTROL_MASK) {
6326 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6327 return TRUE; // I consumed the event
6328 }
6329 break;
6330 }
6331 return FALSE;
6332 }
6335 void
6336 sp_text_toolbox_size_changed (GtkComboBox *cbox,
6337 GObject *tbl)
6338 {
6339 // quit if run by the _changed callbacks
6340 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6341 return;
6342 }
6344 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6346 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6348 // If this is not from selecting a size in the list (in which case get_active will give the
6349 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
6350 // process this event. This fixes GTK's stupid insistence on sending an activate change every
6351 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
6352 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed")) {
6353 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6354 return;
6355 }
6357 gdouble value = -1;
6358 {
6359 gchar *endptr;
6360 gchar *const text = gtk_combo_box_get_active_text(cbox);
6361 if (text) {
6362 value = g_strtod(text, &endptr);
6363 if (endptr == text) { // Conversion failed, non-numeric input.
6364 value = -1;
6365 }
6366 g_free(text);
6367 }
6368 }
6369 if (value <= 0) {
6370 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6371 return; // could not parse value
6372 }
6374 SPCSSAttr *css = sp_repr_css_attr_new ();
6375 Inkscape::CSSOStringStream osfs;
6376 osfs << value;
6377 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
6379 SPStyle *query =
6380 sp_style_new (SP_ACTIVE_DOCUMENT);
6381 int result_numbers =
6382 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6384 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6385 if (result_numbers == QUERY_STYLE_NOTHING)
6386 {
6387 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6388 prefs->setStyle("/tools/text/style", css);
6389 }
6391 sp_style_unref(query);
6393 sp_desktop_set_style (desktop, css, true, true);
6394 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
6395 _("Text: Change font size"));
6396 sp_repr_css_attr_unref (css);
6398 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6400 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6401 }
6403 gboolean
6404 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
6405 {
6406 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6407 if (!desktop) return FALSE;
6409 if (!g_object_get_data (tbl, "esc-pressed")) {
6410 g_object_set_data (tbl, "enter-pressed", gpointer(1));
6411 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6412 sp_text_toolbox_size_changed (cbox, tbl);
6413 g_object_set_data (tbl, "enter-pressed", gpointer(0));
6414 }
6415 return FALSE; // I consumed the event
6416 }
6419 gboolean
6420 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6421 {
6422 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6423 if (!desktop) return FALSE;
6425 switch (get_group0_keyval (event)) {
6426 case GDK_Escape: // defocus
6427 g_object_set_data (tbl, "esc-pressed", gpointer(1));
6428 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6429 g_object_set_data (tbl, "esc-pressed", gpointer(0));
6430 return TRUE; // I consumed the event
6431 break;
6432 case GDK_Return: // defocus
6433 case GDK_KP_Enter:
6434 g_object_set_data (tbl, "enter-pressed", gpointer(1));
6435 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6436 sp_text_toolbox_size_changed (cbox, tbl);
6437 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6438 g_object_set_data (tbl, "enter-pressed", gpointer(0));
6439 return TRUE; // I consumed the event
6440 break;
6441 }
6442 return FALSE;
6443 }
6445 // While editing font name in the entry, disable family_changed by freezing, otherwise completion
6446 // does not work!
6447 gboolean
6448 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
6449 GdkEventFocus */*event*/,
6450 GObject *tbl)
6451 {
6452 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6453 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1); // select all
6454 return FALSE;
6455 }
6457 gboolean
6458 sp_text_toolbox_entry_focus_out (GtkWidget *entry,
6459 GdkEventFocus */*event*/,
6460 GObject *tbl)
6461 {
6462 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6463 gtk_entry_select_region (GTK_ENTRY (entry), 0, 0); // deselect
6464 return FALSE;
6465 }
6467 void
6468 cell_data_func (GtkCellLayout */*cell_layout*/,
6469 GtkCellRenderer *cell,
6470 GtkTreeModel *tree_model,
6471 GtkTreeIter *iter,
6472 gpointer /*data*/)
6473 {
6474 gchar *family;
6475 gtk_tree_model_get(tree_model, iter, 0, &family, -1);
6476 gchar *const family_escaped = g_markup_escape_text(family, -1);
6478 static char const *const sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
6479 gchar *const sample_escaped = g_markup_escape_text(sample, -1);
6481 std::stringstream markup;
6482 markup << family_escaped << " <span foreground='darkgray' font_family='"
6483 << family_escaped << "'>" << sample_escaped << "</span>";
6484 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6486 g_free(family);
6487 g_free(family_escaped);
6488 g_free(sample_escaped);
6489 }
6491 gboolean text_toolbox_completion_match_selected (GtkEntryCompletion *widget,
6492 GtkTreeModel *model,
6493 GtkTreeIter *iter,
6494 GObject *tbl)
6495 {
6496 // We intercept this signal so as to fire family_changed at once (without it, you'd have to
6497 // press Enter again after choosing a completion)
6498 gchar *family;
6499 gtk_tree_model_get(model, iter, 0, &family, -1);
6501 GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6502 gtk_entry_set_text (GTK_ENTRY (entry), family);
6504 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6505 sp_text_toolbox_family_changed (NULL, tbl);
6506 return TRUE;
6507 }
6510 static void
6511 cbe_add_completion (GtkComboBoxEntry *cbe, GObject *tbl){
6512 GtkEntry *entry;
6513 GtkEntryCompletion *completion;
6514 GtkTreeModel *model;
6516 entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(cbe)));
6517 completion = gtk_entry_completion_new();
6518 model = gtk_combo_box_get_model(GTK_COMBO_BOX(cbe));
6519 gtk_entry_completion_set_model(completion, model);
6520 gtk_entry_completion_set_text_column(completion, 0);
6521 gtk_entry_completion_set_inline_completion(completion, FALSE);
6522 gtk_entry_completion_set_inline_selection(completion, FALSE);
6523 gtk_entry_completion_set_popup_completion(completion, TRUE);
6524 gtk_entry_set_completion(entry, completion);
6526 g_signal_connect (G_OBJECT (completion), "match-selected", G_CALLBACK (text_toolbox_completion_match_selected), tbl);
6528 g_object_unref(completion);
6529 }
6531 void sp_text_toolbox_family_popnotify (GtkComboBox *widget,
6532 void *property,
6533 GObject *tbl)
6534 {
6535 // while the drop-down is open, we disable font family changing, reenabling it only when it closes
6537 gboolean shown;
6538 g_object_get (G_OBJECT(widget), "popup-shown", &shown, NULL);
6539 if (shown) {
6540 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6541 //g_print("POP: notify: SHOWN\n");
6542 } else {
6543 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6545 // stupid GTK doesn't let us attach to events in the drop-down window, so we peek here to
6546 // find out if the drop down was closed by Enter and if so, manually update (only
6547 // necessary on Windows, on Linux it updates itself - what a mess, but we'll manage)
6548 GdkEvent *ev = gtk_get_current_event();
6549 if (ev) {
6550 //g_print ("ev type: %d\n", ev->type);
6551 if (ev->type == GDK_KEY_PRESS) {
6552 switch (get_group0_keyval ((GdkEventKey *) ev)) {
6553 case GDK_KP_Enter: // chosen
6554 case GDK_Return:
6555 {
6556 // make sure the chosen one is inserted into the entry
6557 GtkComboBox *combo = GTK_COMBO_BOX (((Gtk::ComboBox *) (g_object_get_data (tbl, "family-entry-combo")))->gobj());
6558 GtkTreeModel *model = gtk_combo_box_get_model(combo);
6559 GtkTreeIter iter;
6560 gboolean has_active = gtk_combo_box_get_active_iter (combo, &iter);
6561 if (has_active) {
6562 gchar *family;
6563 gtk_tree_model_get(model, &iter, 0, &family, -1);
6564 GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6565 gtk_entry_set_text (GTK_ENTRY (entry), family);
6566 }
6568 // update
6569 sp_text_toolbox_family_changed (NULL, tbl);
6570 break;
6571 }
6572 }
6573 }
6574 }
6576 // regardless of whether we updated, defocus the widget
6577 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6578 if (desktop)
6579 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6580 //g_print("POP: notify: HIDDEN\n");
6581 }
6582 }
6584 GtkWidget*
6585 sp_text_toolbox_new (SPDesktop *desktop)
6586 {
6587 GtkToolbar *tbl = GTK_TOOLBAR(gtk_toolbar_new());
6588 GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("/toolbox/secondary", 1));
6590 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
6591 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
6593 GtkTooltips *tt = gtk_tooltips_new();
6595 ////////////Family
6596 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
6597 Gtk::ComboBoxEntry *font_sel = Gtk::manage(new Gtk::ComboBoxEntry(store));
6598 g_signal_connect (G_OBJECT (font_sel->gobj()), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
6600 cbe_add_completion(font_sel->gobj(), G_OBJECT(tbl));
6602 gtk_toolbar_append_widget( tbl, (GtkWidget*) font_sel->gobj(), "", "");
6603 g_object_set_data (G_OBJECT (tbl), "family-entry-combo", font_sel);
6605 // expand the field a bit so as to view more of the previews in the drop-down
6606 GtkRequisition req;
6607 gtk_widget_size_request (GTK_WIDGET (font_sel->gobj()), &req);
6608 gtk_widget_set_size_request (GTK_WIDGET (font_sel->gobj()), req.width + 40, -1);
6610 GtkWidget* entry = (GtkWidget*) font_sel->get_entry()->gobj();
6611 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6613 g_signal_connect (G_OBJECT (font_sel->gobj()), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6614 g_signal_connect (G_OBJECT (font_sel->gobj()), "notify::popup-shown",
6615 G_CALLBACK (sp_text_toolbox_family_popnotify), tbl);
6616 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
6617 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
6618 g_signal_connect (G_OBJECT (entry), "focus-out-event", G_CALLBACK (sp_text_toolbox_entry_focus_out), tbl);
6620 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
6621 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
6623 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
6624 gtk_cell_layout_clear( GTK_CELL_LAYOUT(font_sel->gobj()) );
6625 gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(font_sel->gobj()) , cell , TRUE );
6626 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT(font_sel->gobj()), cell, GtkCellLayoutDataFunc (cell_data_func), NULL, NULL);
6628 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
6629 GtkWidget *box = gtk_event_box_new ();
6630 gtk_container_add (GTK_CONTAINER (box), image);
6631 gtk_toolbar_append_widget( tbl, box, "", "");
6632 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
6633 GtkTooltips *tooltips = gtk_tooltips_new ();
6634 gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
6635 gtk_widget_hide (GTK_WIDGET (box));
6636 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
6638 ////////////Size
6639 gchar const *const sizes[] = {
6640 "4", "6", "8", "9", "10", "11", "12", "13", "14",
6641 "16", "18", "20", "22", "24", "28",
6642 "32", "36", "40", "48", "56", "64", "72", "144"
6643 };
6645 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
6646 for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
6647 gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
6648 }
6649 gtk_widget_set_size_request (cbox, 80, -1);
6650 gtk_toolbar_append_widget( tbl, cbox, "", "");
6651 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
6652 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
6653 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
6654 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
6656 ////////////Text anchor
6657 GtkWidget *group = gtk_radio_button_new (NULL);
6658 GtkWidget *row = gtk_hbox_new (FALSE, 4);
6659 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
6661 // left
6662 GtkWidget *rbutton = group;
6663 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6664 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
6665 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6667 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6668 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
6669 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
6670 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
6672 // center
6673 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6674 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6675 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
6676 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6678 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6679 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
6680 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
6681 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
6683 // right
6684 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6685 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6686 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
6687 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6689 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6690 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
6691 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
6692 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
6694 // fill
6695 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6696 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6697 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
6698 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6700 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6701 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
6702 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
6703 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
6705 gtk_toolbar_append_widget( tbl, row, "", "");
6707 //spacer
6708 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6710 ////////////Text style
6711 row = gtk_hbox_new (FALSE, 4);
6713 // bold
6714 rbutton = gtk_toggle_button_new ();
6715 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6716 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
6717 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6718 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
6720 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6721 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
6722 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
6724 // italic
6725 rbutton = gtk_toggle_button_new ();
6726 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6727 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
6728 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6729 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
6731 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6732 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
6733 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
6735 gtk_toolbar_append_widget( tbl, row, "", "");
6737 //spacer
6738 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6740 // Text orientation
6741 group = gtk_radio_button_new (NULL);
6742 row = gtk_hbox_new (FALSE, 4);
6743 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
6745 // horizontal
6746 rbutton = group;
6747 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6748 gtk_container_add (GTK_CONTAINER (rbutton),
6749 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_HORIZONTAL));
6750 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6751 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
6753 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6754 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
6755 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
6757 // vertical
6758 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6759 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6760 gtk_container_add (GTK_CONTAINER (rbutton),
6761 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_VERTICAL));
6762 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6763 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
6765 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6766 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
6767 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
6768 gtk_toolbar_append_widget( tbl, row, "", "" );
6771 //watch selection
6772 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
6774 sigc::connection *c_selection_changed =
6775 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
6776 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
6777 pool->add_connection ("selection-changed", c_selection_changed);
6779 sigc::connection *c_selection_modified =
6780 new sigc::connection (sp_desktop_selection (desktop)->connectModified
6781 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
6782 pool->add_connection ("selection-modified", c_selection_modified);
6784 sigc::connection *c_subselection_changed =
6785 new sigc::connection (desktop->connectToolSubselectionChanged
6786 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
6787 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
6789 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
6792 gtk_widget_show_all( GTK_WIDGET(tbl) );
6794 return GTK_WIDGET(tbl);
6795 } // end of sp_text_toolbox_new()
6797 }//<unnamed> namespace
6800 //#########################
6801 //## Connector ##
6802 //#########################
6804 static void sp_connector_path_set_avoid(void)
6805 {
6806 cc_selection_set_avoid(true);
6807 }
6810 static void sp_connector_path_set_ignore(void)
6811 {
6812 cc_selection_set_avoid(false);
6813 }
6817 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
6818 {
6819 // quit if run by the _changed callbacks
6820 if (g_object_get_data( tbl, "freeze" )) {
6821 return;
6822 }
6824 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
6825 SPDocument *doc = sp_desktop_document(desktop);
6827 if (!sp_document_get_undo_sensitive(doc))
6828 {
6829 return;
6830 }
6832 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6834 if ( repr->attribute("inkscape:connector-spacing") ) {
6835 gdouble priorValue = gtk_adjustment_get_value(adj);
6836 sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
6837 if ( priorValue == gtk_adjustment_get_value(adj) ) {
6838 return;
6839 }
6840 } else if ( adj->value == defaultConnSpacing ) {
6841 return;
6842 }
6844 // in turn, prevent callbacks from responding
6845 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6847 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
6848 SP_OBJECT(desktop->namedview)->updateRepr();
6850 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
6851 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
6852 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
6853 Geom::Matrix m = Geom::identity();
6854 avoid_item_move(&m, item);
6855 }
6857 if (items) {
6858 g_slist_free(items);
6859 }
6861 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
6862 _("Change connector spacing"));
6864 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6866 spinbutton_defocus(GTK_OBJECT(tbl));
6867 }
6869 static void sp_connector_graph_layout(void)
6870 {
6871 if (!SP_ACTIVE_DESKTOP) return;
6872 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6874 // hack for clones, see comment in align-and-distribute.cpp
6875 int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
6876 prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
6878 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
6880 prefs->setInt("/options/clonecompensation/value", saved_compensation);
6882 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
6883 }
6885 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6886 {
6887 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6888 prefs->setBool("/tools/connector/directedlayout",
6889 gtk_toggle_action_get_active( act ));
6890 }
6892 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6893 {
6894 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6895 prefs->setBool("/tools/connector/avoidoverlaplayout",
6896 gtk_toggle_action_get_active( act ));
6897 }
6900 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
6901 {
6902 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6903 prefs->setDouble("/tools/connector/length", adj->value);
6904 spinbutton_defocus(GTK_OBJECT(tbl));
6905 }
6907 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
6908 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
6909 bool /*is_interactive*/, gpointer data)
6910 {
6911 GtkWidget *tbl = GTK_WIDGET(data);
6913 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6914 return;
6915 }
6916 if (strcmp(name, "inkscape:connector-spacing") != 0) {
6917 return;
6918 }
6920 GtkAdjustment *adj = (GtkAdjustment*)
6921 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
6922 gdouble spacing = defaultConnSpacing;
6923 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
6925 gtk_adjustment_set_value(adj, spacing);
6926 }
6929 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
6930 NULL, /* child_added */
6931 NULL, /* child_removed */
6932 connector_tb_event_attr_changed,
6933 NULL, /* content_changed */
6934 NULL /* order_changed */
6935 };
6938 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
6939 {
6940 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6941 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
6943 {
6944 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
6945 _("Avoid"),
6946 _("Make connectors avoid selected objects"),
6947 INKSCAPE_ICON_CONNECTOR_AVOID,
6948 secondarySize );
6949 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
6950 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6951 }
6953 {
6954 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
6955 _("Ignore"),
6956 _("Make connectors ignore selected objects"),
6957 INKSCAPE_ICON_CONNECTOR_IGNORE,
6958 secondarySize );
6959 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
6960 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6961 }
6963 EgeAdjustmentAction* eact = 0;
6965 // Spacing spinbox
6966 eact = create_adjustment_action( "ConnectorSpacingAction",
6967 _("Connector Spacing"), _("Spacing:"),
6968 _("The amount of space left around objects by auto-routing connectors"),
6969 "/tools/connector/spacing", defaultConnSpacing,
6970 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
6971 0, 100, 1.0, 10.0,
6972 0, 0, 0,
6973 connector_spacing_changed, 1, 0 );
6974 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6976 // Graph (connector network) layout
6977 {
6978 InkAction* inky = ink_action_new( "ConnectorGraphAction",
6979 _("Graph"),
6980 _("Nicely arrange selected connector network"),
6981 INKSCAPE_ICON_DISTRIBUTE_GRAPH,
6982 secondarySize );
6983 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
6984 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6985 }
6987 // Default connector length spinbox
6988 eact = create_adjustment_action( "ConnectorLengthAction",
6989 _("Connector Length"), _("Length:"),
6990 _("Ideal length for connectors when layout is applied"),
6991 "/tools/connector/length", 100,
6992 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
6993 10, 1000, 10.0, 100.0,
6994 0, 0, 0,
6995 connector_length_changed, 1, 0 );
6996 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6999 // Directed edges toggle button
7000 {
7001 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
7002 _("Downwards"),
7003 _("Make connectors with end-markers (arrows) point downwards"),
7004 INKSCAPE_ICON_DISTRIBUTE_GRAPH_DIRECTED,
7005 Inkscape::ICON_SIZE_DECORATION );
7006 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7008 bool tbuttonstate = prefs->getBool("/tools/connector/directedlayout");
7009 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7011 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
7012 }
7014 // Avoid overlaps toggle button
7015 {
7016 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
7017 _("Remove overlaps"),
7018 _("Do not allow overlapping shapes"),
7019 INKSCAPE_ICON_DISTRIBUTE_REMOVE_OVERLAPS,
7020 Inkscape::ICON_SIZE_DECORATION );
7021 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7023 bool tbuttonstate = prefs->getBool("/tools/connector/avoidoverlaplayout");
7024 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), (tbuttonstate ? TRUE : FALSE ));
7026 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
7027 }
7029 // Code to watch for changes to the connector-spacing attribute in
7030 // the XML.
7031 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7032 g_assert(repr != NULL);
7034 purge_repr_listener( holder, holder );
7036 if (repr) {
7037 g_object_set_data( holder, "repr", repr );
7038 Inkscape::GC::anchor(repr);
7039 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
7040 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
7041 }
7042 } // end of sp_connector_toolbox_prep()
7045 //#########################
7046 //## Paintbucket ##
7047 //#########################
7049 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
7050 {
7051 gint channels = ege_select_one_action_get_active( act );
7052 flood_channels_set_channels( channels );
7053 }
7055 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
7056 {
7057 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7058 prefs->setInt("/tools/paintbucket/threshold", (gint)adj->value);
7059 }
7061 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
7062 {
7063 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7064 prefs->setBool("/tools/paintbucket/autogap", ege_select_one_action_get_active( act ));
7065 }
7067 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
7068 {
7069 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
7070 SPUnit const *unit = tracker->getActiveUnit();
7071 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7073 prefs->setDouble("/tools/paintbucket/offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
7074 prefs->setString("/tools/paintbucket/offsetunits", sp_unit_get_abbreviation(unit));
7075 }
7077 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
7078 {
7079 // FIXME: make defaults settable via Inkscape Options
7080 struct KeyValue {
7081 char const *key;
7082 double value;
7083 } const key_values[] = {
7084 {"threshold", 15},
7085 {"offset", 0.0}
7086 };
7088 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
7089 KeyValue const &kv = key_values[i];
7090 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
7091 if ( adj ) {
7092 gtk_adjustment_set_value(adj, kv.value);
7093 }
7094 }
7096 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
7097 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
7098 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
7099 ege_select_one_action_set_active( autogap_action, 0 );
7100 }
7102 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
7103 {
7104 EgeAdjustmentAction* eact = 0;
7105 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7107 {
7108 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7110 GList* items = 0;
7111 gint count = 0;
7112 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
7113 {
7114 GtkTreeIter iter;
7115 gtk_list_store_append( model, &iter );
7116 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7117 count++;
7118 }
7119 g_list_free( items );
7120 items = 0;
7121 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
7122 g_object_set( act1, "short_label", _("Fill by:"), NULL );
7123 ege_select_one_action_set_appearance( act1, "compact" );
7124 ege_select_one_action_set_active( act1, prefs->getInt("/tools/paintbucket/channels", 0) );
7125 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
7126 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
7127 g_object_set_data( holder, "channels_action", act1 );
7128 }
7130 // Spacing spinbox
7131 {
7132 eact = create_adjustment_action(
7133 "ThresholdAction",
7134 _("Fill Threshold"), _("Threshold:"),
7135 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
7136 "/tools/paintbucket/threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
7137 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
7138 0, 0, 0,
7139 paintbucket_threshold_changed, 1, 0 );
7141 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
7142 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7143 }
7145 // Create the units menu.
7146 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
7147 Glib::ustring stored_unit = prefs->getString("/tools/paintbucket/offsetunits");
7148 if (!stored_unit.empty())
7149 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit.data()));
7150 g_object_set_data( holder, "tracker", tracker );
7151 {
7152 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
7153 gtk_action_group_add_action( mainActions, act );
7154 }
7156 // Offset spinbox
7157 {
7158 eact = create_adjustment_action(
7159 "OffsetAction",
7160 _("Grow/shrink by"), _("Grow/shrink by:"),
7161 _("The amount to grow (positive) or shrink (negative) the created fill path"),
7162 "/tools/paintbucket/offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
7163 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
7164 0, 0, 0,
7165 paintbucket_offset_changed, 1, 2);
7166 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
7168 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7169 }
7171 /* Auto Gap */
7172 {
7173 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7175 GList* items = 0;
7176 gint count = 0;
7177 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
7178 {
7179 GtkTreeIter iter;
7180 gtk_list_store_append( model, &iter );
7181 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7182 count++;
7183 }
7184 g_list_free( items );
7185 items = 0;
7186 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
7187 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
7188 ege_select_one_action_set_appearance( act2, "compact" );
7189 ege_select_one_action_set_active( act2, prefs->getBool("/tools/paintbucket/autogap") );
7190 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
7191 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
7192 g_object_set_data( holder, "autogap_action", act2 );
7193 }
7195 /* Reset */
7196 {
7197 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
7198 _("Defaults"),
7199 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
7200 GTK_STOCK_CLEAR );
7201 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
7202 gtk_action_group_add_action( mainActions, act );
7203 gtk_action_set_sensitive( act, TRUE );
7204 }
7206 }
7208 /*
7209 Local Variables:
7210 mode:c++
7211 c-file-style:"stroustrup"
7212 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
7213 indent-tabs-mode:nil
7214 fill-column:99
7215 End:
7216 */
7217 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :