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);
1890 if (dt == NULL || nv == NULL) {
1891 g_warning("No desktop or namedview specified (in toggle_snap_callback)!");
1892 return;
1893 }
1895 Inkscape::XML::Node *repr = SP_OBJECT_REPR(nv);
1897 if (repr == NULL) {
1898 g_warning("This namedview doesn't have a xml representation attached!");
1899 return;
1900 }
1902 bool v = false;
1903 SPAttributeEnum attr = (SPAttributeEnum) GPOINTER_TO_INT(g_object_get_data(G_OBJECT(act), "SP_ATTR_INKSCAPE"));
1905 switch (attr) {
1906 case SP_ATTR_INKSCAPE_SNAP_GLOBAL:
1907 dt->toggleSnapGlobal();
1908 break;
1909 case SP_ATTR_INKSCAPE_SNAP_BBOX:
1910 v = nv->snap_manager.snapprefs.getSnapModeBBox();
1911 sp_repr_set_boolean(repr, "inkscape:snap-bbox", !v);
1912 break;
1913 case SP_ATTR_INKSCAPE_BBOX_PATHS:
1914 v = nv->snap_manager.snapprefs.getSnapToBBoxPath();
1915 sp_repr_set_boolean(repr, "inkscape:bbox-paths", !v);
1916 break;
1917 case SP_ATTR_INKSCAPE_BBOX_NODES:
1918 v = nv->snap_manager.snapprefs.getSnapToBBoxNode();
1919 sp_repr_set_boolean(repr, "inkscape:bbox-nodes", !v);
1920 break;
1921 case SP_ATTR_INKSCAPE_SNAP_NODES:
1922 v = nv->snap_manager.snapprefs.getSnapModeNode();
1923 sp_repr_set_boolean(repr, "inkscape:snap-nodes", !v);
1924 break;
1925 case SP_ATTR_INKSCAPE_OBJECT_PATHS:
1926 v = nv->snap_manager.snapprefs.getSnapToItemPath();
1927 sp_repr_set_boolean(repr, "inkscape:object-paths", !v);
1928 break;
1929 case SP_ATTR_INKSCAPE_OBJECT_NODES:
1930 v = nv->snap_manager.snapprefs.getSnapToItemNode();
1931 sp_repr_set_boolean(repr, "inkscape:object-nodes", !v);
1932 break;
1933 case SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES:
1934 v = nv->snap_manager.snapprefs.getSnapSmoothNodes();
1935 sp_repr_set_boolean(repr, "inkscape:snap-smooth-nodes", !v);
1936 break;
1937 case SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS:
1938 v = nv->snap_manager.snapprefs.getSnapIntersectionCS();
1939 sp_repr_set_boolean(repr, "inkscape:snap-intersection-paths", !v);
1940 break;
1941 case SP_ATTR_INKSCAPE_SNAP_CENTER:
1942 v = nv->snap_manager.snapprefs.getIncludeItemCenter();
1943 sp_repr_set_boolean(repr, "inkscape:snap-center", !v);
1944 break;
1945 case SP_ATTR_INKSCAPE_SNAP_GRIDS:
1946 v = nv->snap_manager.snapprefs.getSnapToGrids();
1947 sp_repr_set_boolean(repr, "inkscape:snap-grids", !v);
1948 break;
1949 case SP_ATTR_INKSCAPE_SNAP_TO_GUIDES:
1950 v = nv->snap_manager.snapprefs.getSnapToGuides();
1951 sp_repr_set_boolean(repr, "inkscape:snap-to-guides", !v);
1952 break;
1953 case SP_ATTR_INKSCAPE_SNAP_PAGE:
1954 v = nv->snap_manager.snapprefs.getSnapToPageBorder();
1955 sp_repr_set_boolean(repr, "inkscape:snap-page", !v);
1956 break;
1957 /*case SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE:
1958 v = nv->snap_manager.snapprefs.getSnapIntersectionGG();
1959 sp_repr_set_boolean(repr, "inkscape:snap-intersection-grid-guide", !v);
1960 break;*/
1961 case SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS:
1962 v = nv->snap_manager.snapprefs.getSnapLineMidpoints();
1963 sp_repr_set_boolean(repr, "inkscape:snap-midpoints", !v);
1964 break;
1965 case SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS:
1966 v = nv->snap_manager.snapprefs.getSnapObjectMidpoints();
1967 sp_repr_set_boolean(repr, "inkscape:snap-object-midpoints", !v);
1968 break;
1969 case SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS:
1970 v = nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints();
1971 sp_repr_set_boolean(repr, "inkscape:snap-bbox-edge-midpoints", !v);
1972 break;
1973 case SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS:
1974 v = nv->snap_manager.snapprefs.getSnapBBoxMidpoints();
1975 sp_repr_set_boolean(repr, "inkscape:snap-bbox-midpoints", !v);
1976 break;
1977 default:
1978 g_warning("toggle_snap_callback has been called with an ID for which no action has been defined");
1979 break;
1980 }
1982 // The snapping preferences are stored in the document, and therefore toggling makes the document dirty
1983 SPDocument *doc = SP_OBJECT_DOCUMENT(nv);
1984 doc->setModifiedSinceSave();
1985 }
1987 void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1988 {
1989 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1990 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
1992 gchar const * descr =
1993 "<ui>"
1994 " <toolbar name='SnapToolbar'>"
1995 " <toolitem action='ToggleSnapGlobal' />"
1996 " <separator />"
1997 " <toolitem action='ToggleSnapFromBBoxCorner' />"
1998 " <toolitem action='ToggleSnapToBBoxPath' />"
1999 " <toolitem action='ToggleSnapToBBoxNode' />"
2000 " <toolitem action='ToggleSnapToFromBBoxEdgeMidpoints' />"
2001 " <toolitem action='ToggleSnapToFromBBoxCenters' />"
2002 " <separator />"
2003 " <toolitem action='ToggleSnapFromNode' />"
2004 " <toolitem action='ToggleSnapToItemPath' />"
2005 " <toolitem action='ToggleSnapToPathIntersections' />"
2006 " <toolitem action='ToggleSnapToItemNode' />"
2007 " <toolitem action='ToggleSnapToSmoothNodes' />"
2008 " <toolitem action='ToggleSnapToFromLineMidpoints' />"
2009 " <toolitem action='ToggleSnapToFromObjectCenters' />"
2010 " <toolitem action='ToggleSnapToFromRotationCenter' />"
2011 " <separator />"
2012 " <toolitem action='ToggleSnapToPageBorder' />"
2013 " <toolitem action='ToggleSnapToGrids' />"
2014 " <toolitem action='ToggleSnapToGuides' />"
2015 //" <toolitem action='ToggleSnapToGridGuideIntersections' />"
2016 " </toolbar>"
2017 "</ui>";
2019 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
2021 {
2022 InkToggleAction* act = ink_toggle_action_new("ToggleSnapGlobal",
2023 _("Snap"), _("Enable snapping"), INKSCAPE_ICON_SNAP, secondarySize,
2024 SP_ATTR_INKSCAPE_SNAP_GLOBAL);
2026 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2027 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2028 }
2030 {
2031 InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromBBoxCorner",
2032 _("Bounding box"), _("Snap bounding box corners"), INKSCAPE_ICON_SNAP_BOUNDING_BOX,
2033 secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX);
2035 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2036 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2037 }
2039 {
2040 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxPath",
2041 _("Bounding box edges"), _("Snap to edges of a bounding box"),
2042 INKSCAPE_ICON_SNAP_BOUNDING_BOX_EDGES, secondarySize, SP_ATTR_INKSCAPE_BBOX_PATHS);
2044 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2045 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2046 }
2048 {
2049 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxNode",
2050 _("Bounding box corners"), _("Snap to bounding box corners"),
2051 INKSCAPE_ICON_SNAP_BOUNDING_BOX_CORNERS, secondarySize, SP_ATTR_INKSCAPE_BBOX_NODES);
2053 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2054 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2055 }
2057 {
2058 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxEdgeMidpoints",
2059 _("BBox Edge Midpoints"), _("Snap from and to midpoints of bounding box edges"),
2060 INKSCAPE_ICON_SNAP_BOUNDING_BOX_MIDPOINTS, secondarySize,
2061 SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS);
2063 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2064 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2065 }
2067 {
2068 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxCenters",
2069 _("BBox Centers"), _("Snapping from and to centers of bounding boxes"),
2070 INKSCAPE_ICON_SNAP_BOUNDING_BOX_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS);
2072 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2073 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2074 }
2076 {
2077 InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromNode",
2078 _("Nodes"), _("Snap nodes or handles"), INKSCAPE_ICON_SNAP_NODES, secondarySize, SP_ATTR_INKSCAPE_SNAP_NODES);
2080 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2081 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2082 }
2084 {
2085 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemPath",
2086 _("Paths"), _("Snap to paths"), INKSCAPE_ICON_SNAP_NODES_PATH, secondarySize,
2087 SP_ATTR_INKSCAPE_OBJECT_PATHS);
2089 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2090 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2091 }
2093 {
2094 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPathIntersections",
2095 _("Path intersections"), _("Snap to path intersections"),
2096 INKSCAPE_ICON_SNAP_NODES_INTERSECTION, secondarySize, SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS);
2098 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2099 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2100 }
2102 {
2103 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemNode",
2104 _("To nodes"), _("Snap to cusp nodes"), INKSCAPE_ICON_SNAP_NODES_CUSP, secondarySize,
2105 SP_ATTR_INKSCAPE_OBJECT_NODES);
2107 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2108 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2109 }
2111 {
2112 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToSmoothNodes",
2113 _("Smooth nodes"), _("Snap to smooth nodes"), INKSCAPE_ICON_SNAP_NODES_SMOOTH,
2114 secondarySize, SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES);
2116 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2117 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2118 }
2120 {
2121 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromLineMidpoints",
2122 _("Line Midpoints"), _("Snap from and to midpoints of line segments"),
2123 INKSCAPE_ICON_SNAP_NODES_MIDPOINT, secondarySize, SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS);
2125 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2126 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2127 }
2129 {
2130 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromObjectCenters",
2131 _("Object Centers"), _("Snap from and to centers of objects"),
2132 INKSCAPE_ICON_SNAP_NODES_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS);
2134 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2135 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2136 }
2138 {
2139 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromRotationCenter",
2140 _("Rotation Centers"), _("Snap from and to an item's rotation center"),
2141 INKSCAPE_ICON_SNAP_NODES_ROTATION_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_CENTER);
2143 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2144 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2145 }
2147 {
2148 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPageBorder",
2149 _("Page border"), _("Snap to the page border"), INKSCAPE_ICON_SNAP_PAGE,
2150 secondarySize, SP_ATTR_INKSCAPE_SNAP_PAGE);
2152 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2153 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2154 }
2156 {
2157 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGrids",
2158 _("Grids"), _("Snap to grids"), INKSCAPE_ICON_GRID_RECTANGULAR, secondarySize,
2159 SP_ATTR_INKSCAPE_SNAP_GRIDS);
2161 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2162 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2163 }
2165 {
2166 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGuides",
2167 _("Guides"), _("Snap to guides"), INKSCAPE_ICON_GUIDES, secondarySize,
2168 SP_ATTR_INKSCAPE_SNAP_TO_GUIDES);
2170 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2171 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2172 }
2174 /*{
2175 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGridGuideIntersections",
2176 _("Grid/guide intersections"), _("Snap to intersections of a grid with a guide"),
2177 INKSCAPE_ICON_SNAP_GRID_GUIDE_INTERSECTIONS, secondarySize,
2178 SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE);
2180 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2181 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2182 }*/
2184 GtkUIManager* mgr = gtk_ui_manager_new();
2185 GError* errVal = 0;
2187 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
2188 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
2190 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/SnapToolbar" );
2191 if ( prefs->getBool("/toolbox/icononly", true) ) {
2192 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
2193 }
2195 Inkscape::IconSize toolboxSize = prefToSize("/toolbox/secondary");
2196 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
2198 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
2199 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
2201 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
2203 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
2204 if ( child ) {
2205 gtk_container_remove( GTK_CONTAINER(toolbox), child );
2206 }
2208 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
2210 }
2212 void update_snap_toolbox(SPDesktop *desktop, SPEventContext */*eventcontext*/, GtkWidget *toolbox)
2213 {
2214 g_assert(desktop != NULL);
2215 g_assert(toolbox != NULL);
2217 SPNamedView *nv = sp_desktop_namedview(desktop);
2218 if (nv == NULL) {
2219 g_warning("Namedview cannot be retrieved (in update_snap_toolbox)!");
2220 return;
2221 }
2223 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2225 Glib::RefPtr<Gtk::Action> act1 = mainActions->get_action("ToggleSnapGlobal");
2226 Glib::RefPtr<Gtk::Action> act2 = mainActions->get_action("ToggleSnapFromBBoxCorner");
2227 Glib::RefPtr<Gtk::Action> act3 = mainActions->get_action("ToggleSnapToBBoxPath");
2228 Glib::RefPtr<Gtk::Action> act4 = mainActions->get_action("ToggleSnapToBBoxNode");
2229 Glib::RefPtr<Gtk::Action> act4b = mainActions->get_action("ToggleSnapToFromBBoxEdgeMidpoints");
2230 Glib::RefPtr<Gtk::Action> act4c = mainActions->get_action("ToggleSnapToFromBBoxCenters");
2231 Glib::RefPtr<Gtk::Action> act5 = mainActions->get_action("ToggleSnapFromNode");
2232 Glib::RefPtr<Gtk::Action> act6 = mainActions->get_action("ToggleSnapToItemPath");
2233 Glib::RefPtr<Gtk::Action> act6b = mainActions->get_action("ToggleSnapToPathIntersections");
2234 Glib::RefPtr<Gtk::Action> act7 = mainActions->get_action("ToggleSnapToItemNode");
2235 Glib::RefPtr<Gtk::Action> act8 = mainActions->get_action("ToggleSnapToSmoothNodes");
2236 Glib::RefPtr<Gtk::Action> act9 = mainActions->get_action("ToggleSnapToFromLineMidpoints");
2237 Glib::RefPtr<Gtk::Action> act10 = mainActions->get_action("ToggleSnapToFromObjectCenters");
2238 Glib::RefPtr<Gtk::Action> act11 = mainActions->get_action("ToggleSnapToFromRotationCenter");
2239 Glib::RefPtr<Gtk::Action> act12 = mainActions->get_action("ToggleSnapToPageBorder");
2240 //Glib::RefPtr<Gtk::Action> act13 = mainActions->get_action("ToggleSnapToGridGuideIntersections");
2241 Glib::RefPtr<Gtk::Action> act14 = mainActions->get_action("ToggleSnapToGrids");
2242 Glib::RefPtr<Gtk::Action> act15 = mainActions->get_action("ToggleSnapToGuides");
2245 if (!act1) {
2246 return; // The snap actions haven't been defined yet (might be the case during startup)
2247 }
2249 // The ..._set_active calls below will toggle the buttons, but this shouldn't lead to
2250 // changes in our document because we're only updating the UI;
2251 // Setting the "freeze" parameter to true will block the code in toggle_snap_callback()
2252 g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(TRUE));
2254 bool const c1 = nv->snap_manager.snapprefs.getSnapEnabledGlobally();
2255 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act1->gobj()), c1);
2257 bool const c2 = nv->snap_manager.snapprefs.getSnapModeBBox();
2258 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act2->gobj()), c2);
2259 gtk_action_set_sensitive(GTK_ACTION(act2->gobj()), c1);
2261 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act3->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxPath());
2262 gtk_action_set_sensitive(GTK_ACTION(act3->gobj()), c1 && c2);
2263 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxNode());
2264 gtk_action_set_sensitive(GTK_ACTION(act4->gobj()), c1 && c2);
2265 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4b->gobj()), nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints());
2266 gtk_action_set_sensitive(GTK_ACTION(act4b->gobj()), c1 && c2);
2267 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4c->gobj()), nv->snap_manager.snapprefs.getSnapBBoxMidpoints());
2268 gtk_action_set_sensitive(GTK_ACTION(act4c->gobj()), c1 && c2);
2270 bool const c3 = nv->snap_manager.snapprefs.getSnapModeNode();
2271 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act5->gobj()), c3);
2272 gtk_action_set_sensitive(GTK_ACTION(act5->gobj()), c1);
2274 bool const c4 = nv->snap_manager.snapprefs.getSnapToItemPath();
2275 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6->gobj()), c4);
2276 gtk_action_set_sensitive(GTK_ACTION(act6->gobj()), c1 && c3);
2277 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6b->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionCS());
2278 gtk_action_set_sensitive(GTK_ACTION(act6b->gobj()), c1 && c3 && c4);
2279 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act7->gobj()), nv->snap_manager.snapprefs.getSnapToItemNode());
2280 gtk_action_set_sensitive(GTK_ACTION(act7->gobj()), c1 && c3);
2281 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act8->gobj()), nv->snap_manager.snapprefs.getSnapSmoothNodes());
2282 gtk_action_set_sensitive(GTK_ACTION(act8->gobj()), c1 && c3);
2283 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act9->gobj()), nv->snap_manager.snapprefs.getSnapLineMidpoints());
2284 gtk_action_set_sensitive(GTK_ACTION(act9->gobj()), c1 && c3);
2285 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act10->gobj()), nv->snap_manager.snapprefs.getSnapObjectMidpoints());
2286 gtk_action_set_sensitive(GTK_ACTION(act10->gobj()), c1 && c3);
2287 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act11->gobj()), nv->snap_manager.snapprefs.getIncludeItemCenter());
2288 gtk_action_set_sensitive(GTK_ACTION(act11->gobj()), c1 && c3);
2290 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act12->gobj()), nv->snap_manager.snapprefs.getSnapToPageBorder());
2291 gtk_action_set_sensitive(GTK_ACTION(act12->gobj()), c1);
2292 //gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act13->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionGG());
2293 //gtk_action_set_sensitive(GTK_ACTION(act13->gobj()), c1);
2295 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act14->gobj()), nv->snap_manager.snapprefs.getSnapToGrids());
2296 gtk_action_set_sensitive(GTK_ACTION(act14->gobj()), c1);
2297 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act15->gobj()), nv->snap_manager.snapprefs.getSnapToGuides());
2298 gtk_action_set_sensitive(GTK_ACTION(act15->gobj()), c1);
2301 g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(FALSE)); // unfreeze (see above)
2302 }
2304 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
2305 {
2306 gtk_widget_show(toolbox_toplevel);
2307 GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
2309 GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
2310 if (!shown_toolbox) {
2311 return;
2312 }
2313 gtk_widget_show(toolbox);
2315 gtk_widget_show_all(shown_toolbox);
2316 }
2318 static GtkWidget *
2319 sp_empty_toolbox_new(SPDesktop *desktop)
2320 {
2321 GtkWidget *tbl = gtk_toolbar_new();
2322 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
2323 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
2325 gtk_widget_show_all(tbl);
2326 sp_set_font_size_smaller (tbl);
2328 return tbl;
2329 }
2331 #define MODE_LABEL_WIDTH 70
2333 //########################
2334 //## Star ##
2335 //########################
2337 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2338 {
2339 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2341 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2342 // do not remember prefs if this call is initiated by an undo change, because undoing object
2343 // creation sets bogus values to its attributes before it is deleted
2344 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2345 prefs->setInt("/tools/shapes/star/magnitude", (gint)adj->value);
2346 }
2348 // quit if run by the attr_changed listener
2349 if (g_object_get_data( dataKludge, "freeze" )) {
2350 return;
2351 }
2353 // in turn, prevent listener from responding
2354 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2356 bool modmade = false;
2358 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2359 GSList const *items = selection->itemList();
2360 for (; items != NULL; items = items->next) {
2361 if (SP_IS_STAR((SPItem *) items->data)) {
2362 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2363 sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
2364 sp_repr_set_svg_double(repr, "sodipodi:arg2",
2365 (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
2366 + M_PI / (gint)adj->value));
2367 SP_OBJECT((SPItem *) items->data)->updateRepr();
2368 modmade = true;
2369 }
2370 }
2371 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2372 _("Star: Change number of corners"));
2374 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2375 }
2377 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2378 {
2379 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2381 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2382 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2383 prefs->setDouble("/tools/shapes/star/proportion", adj->value);
2384 }
2386 // quit if run by the attr_changed listener
2387 if (g_object_get_data( dataKludge, "freeze" )) {
2388 return;
2389 }
2391 // in turn, prevent listener from responding
2392 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2394 bool modmade = false;
2395 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2396 GSList const *items = selection->itemList();
2397 for (; items != NULL; items = items->next) {
2398 if (SP_IS_STAR((SPItem *) items->data)) {
2399 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2401 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2402 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2403 if (r2 < r1) {
2404 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
2405 } else {
2406 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
2407 }
2409 SP_OBJECT((SPItem *) items->data)->updateRepr();
2410 modmade = true;
2411 }
2412 }
2414 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2415 _("Star: Change spoke ratio"));
2417 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2418 }
2420 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
2421 {
2422 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2423 bool flat = ege_select_one_action_get_active( act ) == 0;
2425 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2426 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2427 prefs->setBool( "/tools/shapes/star/isflatsided", flat);
2428 }
2430 // quit if run by the attr_changed listener
2431 if (g_object_get_data( dataKludge, "freeze" )) {
2432 return;
2433 }
2435 // in turn, prevent listener from responding
2436 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2438 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2439 GSList const *items = selection->itemList();
2440 GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2441 bool modmade = false;
2443 if ( prop_action ) {
2444 gtk_action_set_sensitive( prop_action, !flat );
2445 }
2447 for (; items != NULL; items = items->next) {
2448 if (SP_IS_STAR((SPItem *) items->data)) {
2449 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2450 repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
2451 SP_OBJECT((SPItem *) items->data)->updateRepr();
2452 modmade = true;
2453 }
2454 }
2456 if (modmade) {
2457 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2458 flat ? _("Make polygon") : _("Make star"));
2459 }
2461 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2462 }
2464 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2465 {
2466 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2468 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2469 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2470 prefs->setDouble("/tools/shapes/star/rounded", (gdouble) adj->value);
2471 }
2473 // quit if run by the attr_changed listener
2474 if (g_object_get_data( dataKludge, "freeze" )) {
2475 return;
2476 }
2478 // in turn, prevent listener from responding
2479 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2481 bool modmade = false;
2483 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2484 GSList const *items = selection->itemList();
2485 for (; items != NULL; items = items->next) {
2486 if (SP_IS_STAR((SPItem *) items->data)) {
2487 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2488 sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2489 SP_OBJECT(items->data)->updateRepr();
2490 modmade = true;
2491 }
2492 }
2493 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2494 _("Star: Change rounding"));
2496 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2497 }
2499 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2500 {
2501 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2503 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2504 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2505 prefs->setDouble("/tools/shapes/star/randomized", (gdouble) adj->value);
2506 }
2508 // quit if run by the attr_changed listener
2509 if (g_object_get_data( dataKludge, "freeze" )) {
2510 return;
2511 }
2513 // in turn, prevent listener from responding
2514 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2516 bool modmade = false;
2518 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2519 GSList const *items = selection->itemList();
2520 for (; items != NULL; items = items->next) {
2521 if (SP_IS_STAR((SPItem *) items->data)) {
2522 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2523 sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2524 SP_OBJECT(items->data)->updateRepr();
2525 modmade = true;
2526 }
2527 }
2528 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2529 _("Star: Change randomization"));
2531 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2532 }
2535 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2536 gchar const */*old_value*/, gchar const */*new_value*/,
2537 bool /*is_interactive*/, gpointer data)
2538 {
2539 GtkWidget *tbl = GTK_WIDGET(data);
2541 // quit if run by the _changed callbacks
2542 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2543 return;
2544 }
2546 // in turn, prevent callbacks from responding
2547 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2549 GtkAdjustment *adj = 0;
2551 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2552 bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2554 if (!strcmp(name, "inkscape:randomized")) {
2555 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2556 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2557 } else if (!strcmp(name, "inkscape:rounded")) {
2558 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2559 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2560 } else if (!strcmp(name, "inkscape:flatsided")) {
2561 GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2562 char const *flatsides = repr->attribute("inkscape:flatsided");
2563 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2564 if ( flatsides && !strcmp(flatsides,"false") ) {
2565 ege_select_one_action_set_active( flat_action, 1 );
2566 gtk_action_set_sensitive( prop_action, TRUE );
2567 } else {
2568 ege_select_one_action_set_active( flat_action, 0 );
2569 gtk_action_set_sensitive( prop_action, FALSE );
2570 }
2571 } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2572 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2573 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2574 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2575 if (r2 < r1) {
2576 gtk_adjustment_set_value(adj, r2/r1);
2577 } else {
2578 gtk_adjustment_set_value(adj, r1/r2);
2579 }
2580 } else if (!strcmp(name, "sodipodi:sides")) {
2581 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2582 gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2583 }
2585 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2586 }
2589 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2590 {
2591 NULL, /* child_added */
2592 NULL, /* child_removed */
2593 star_tb_event_attr_changed,
2594 NULL, /* content_changed */
2595 NULL /* order_changed */
2596 };
2599 /**
2600 * \param selection Should not be NULL.
2601 */
2602 static void
2603 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2604 {
2605 int n_selected = 0;
2606 Inkscape::XML::Node *repr = NULL;
2608 purge_repr_listener( tbl, tbl );
2610 for (GSList const *items = selection->itemList();
2611 items != NULL;
2612 items = items->next)
2613 {
2614 if (SP_IS_STAR((SPItem *) items->data)) {
2615 n_selected++;
2616 repr = SP_OBJECT_REPR((SPItem *) items->data);
2617 }
2618 }
2620 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2622 if (n_selected == 0) {
2623 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2624 } else if (n_selected == 1) {
2625 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2627 if (repr) {
2628 g_object_set_data( tbl, "repr", repr );
2629 Inkscape::GC::anchor(repr);
2630 sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2631 sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2632 }
2633 } else {
2634 // FIXME: implement averaging of all parameters for multiple selected stars
2635 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2636 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2637 }
2638 }
2641 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2642 {
2643 // FIXME: in this and all other _default functions, set some flag telling the value_changed
2644 // callbacks to lump all the changes for all selected objects in one undo step
2646 GtkAdjustment *adj = 0;
2648 // fixme: make settable in prefs!
2649 gint mag = 5;
2650 gdouble prop = 0.5;
2651 gboolean flat = FALSE;
2652 gdouble randomized = 0;
2653 gdouble rounded = 0;
2655 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2656 ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2658 GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2659 gtk_action_set_sensitive( sb2, !flat );
2661 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2662 gtk_adjustment_set_value(adj, mag);
2663 gtk_adjustment_value_changed(adj);
2665 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2666 gtk_adjustment_set_value(adj, prop);
2667 gtk_adjustment_value_changed(adj);
2669 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2670 gtk_adjustment_set_value(adj, rounded);
2671 gtk_adjustment_value_changed(adj);
2673 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2674 gtk_adjustment_set_value(adj, randomized);
2675 gtk_adjustment_value_changed(adj);
2676 }
2679 void
2680 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2681 {
2682 GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2683 if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2684 GtkWidget *l = gtk_label_new(NULL);
2685 gtk_label_set_markup(GTK_LABEL(l), title);
2686 gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2687 if ( GTK_IS_TOOLBAR(tbl) ) {
2688 gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2689 } else {
2690 gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2691 }
2692 gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2693 }
2696 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2697 {
2698 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
2700 {
2701 EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2702 ege_output_action_set_use_markup( act, TRUE );
2703 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2704 g_object_set_data( holder, "mode_action", act );
2705 }
2707 {
2708 EgeAdjustmentAction* eact = 0;
2709 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2710 bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2712 /* Flatsided checkbox */
2713 {
2714 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2716 GtkTreeIter iter;
2717 gtk_list_store_append( model, &iter );
2718 gtk_list_store_set( model, &iter,
2719 0, _("Polygon"),
2720 1, _("Regular polygon (with one handle) instead of a star"),
2721 2, INKSCAPE_ICON_DRAW_POLYGON,
2722 -1 );
2724 gtk_list_store_append( model, &iter );
2725 gtk_list_store_set( model, &iter,
2726 0, _("Star"),
2727 1, _("Star instead of a regular polygon (with one handle)"),
2728 2, INKSCAPE_ICON_DRAW_STAR,
2729 -1 );
2731 EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2732 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2733 g_object_set_data( holder, "flat_action", act );
2735 ege_select_one_action_set_appearance( act, "full" );
2736 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2737 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2738 ege_select_one_action_set_icon_column( act, 2 );
2739 ege_select_one_action_set_icon_size( act, secondarySize );
2740 ege_select_one_action_set_tooltip_column( act, 1 );
2742 ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2743 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2744 }
2746 /* Magnitude */
2747 {
2748 gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2749 gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2750 eact = create_adjustment_action( "MagnitudeAction",
2751 _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2752 "/tools/shapes/star/magnitude", 3,
2753 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2754 3, 1024, 1, 5,
2755 labels, values, G_N_ELEMENTS(labels),
2756 sp_stb_magnitude_value_changed,
2757 1.0, 0 );
2758 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2759 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2760 }
2762 /* Spoke ratio */
2763 {
2764 gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2765 gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2766 eact = create_adjustment_action( "SpokeAction",
2767 _("Spoke ratio"), _("Spoke ratio:"),
2768 // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2769 // Base radius is the same for the closest handle.
2770 _("Base radius to tip radius ratio"),
2771 "/tools/shapes/star/proportion", 0.5,
2772 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2773 0.01, 1.0, 0.01, 0.1,
2774 labels, values, G_N_ELEMENTS(labels),
2775 sp_stb_proportion_value_changed );
2776 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2777 g_object_set_data( holder, "prop_action", eact );
2778 }
2780 if ( !isFlatSided ) {
2781 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2782 } else {
2783 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2784 }
2786 /* Roundedness */
2787 {
2788 gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2789 gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2790 eact = create_adjustment_action( "RoundednessAction",
2791 _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2792 "/tools/shapes/star/rounded", 0.0,
2793 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2794 -10.0, 10.0, 0.01, 0.1,
2795 labels, values, G_N_ELEMENTS(labels),
2796 sp_stb_rounded_value_changed );
2797 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2798 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2799 }
2801 /* Randomization */
2802 {
2803 gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2804 gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2805 eact = create_adjustment_action( "RandomizationAction",
2806 _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2807 "/tools/shapes/star/randomized", 0.0,
2808 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2809 -10.0, 10.0, 0.001, 0.01,
2810 labels, values, G_N_ELEMENTS(labels),
2811 sp_stb_randomized_value_changed, 0.1, 3 );
2812 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2813 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2814 }
2815 }
2817 {
2818 /* Reset */
2819 {
2820 GtkAction* act = gtk_action_new( "StarResetAction",
2821 _("Defaults"),
2822 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2823 GTK_STOCK_CLEAR );
2824 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2825 gtk_action_group_add_action( mainActions, act );
2826 gtk_action_set_sensitive( act, TRUE );
2827 }
2828 }
2830 sigc::connection *connection = new sigc::connection(
2831 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2832 );
2833 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2834 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2835 }
2838 //########################
2839 //## Rect ##
2840 //########################
2842 static void sp_rtb_sensitivize( GObject *tbl )
2843 {
2844 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2845 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2846 GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2848 if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2849 gtk_action_set_sensitive( not_rounded, FALSE );
2850 } else {
2851 gtk_action_set_sensitive( not_rounded, TRUE );
2852 }
2853 }
2856 static void
2857 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2858 void (*setter)(SPRect *, gdouble))
2859 {
2860 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2862 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2863 SPUnit const *unit = tracker->getActiveUnit();
2865 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2866 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2867 prefs->setDouble(Glib::ustring("/tools/shapes/rect/") + value_name, sp_units_get_pixels(adj->value, *unit));
2868 }
2870 // quit if run by the attr_changed listener
2871 if (g_object_get_data( tbl, "freeze" )) {
2872 return;
2873 }
2875 // in turn, prevent listener from responding
2876 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2878 bool modmade = false;
2879 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2880 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2881 if (SP_IS_RECT(items->data)) {
2882 if (adj->value != 0) {
2883 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2884 } else {
2885 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2886 }
2887 modmade = true;
2888 }
2889 }
2891 sp_rtb_sensitivize( tbl );
2893 if (modmade) {
2894 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2895 _("Change rectangle"));
2896 }
2898 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2899 }
2901 static void
2902 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2903 {
2904 sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2905 }
2907 static void
2908 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2909 {
2910 sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2911 }
2913 static void
2914 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2915 {
2916 sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2917 }
2919 static void
2920 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2921 {
2922 sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2923 }
2927 static void
2928 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2929 {
2930 GtkAdjustment *adj = 0;
2932 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2933 gtk_adjustment_set_value(adj, 0.0);
2934 // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2935 gtk_adjustment_value_changed(adj);
2937 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2938 gtk_adjustment_set_value(adj, 0.0);
2939 gtk_adjustment_value_changed(adj);
2941 sp_rtb_sensitivize( obj );
2942 }
2944 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2945 gchar const */*old_value*/, gchar const */*new_value*/,
2946 bool /*is_interactive*/, gpointer data)
2947 {
2948 GObject *tbl = G_OBJECT(data);
2950 // quit if run by the _changed callbacks
2951 if (g_object_get_data( tbl, "freeze" )) {
2952 return;
2953 }
2955 // in turn, prevent callbacks from responding
2956 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2958 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2959 SPUnit const *unit = tracker->getActiveUnit();
2961 gpointer item = g_object_get_data( tbl, "item" );
2962 if (item && SP_IS_RECT(item)) {
2963 {
2964 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2965 gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2966 gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2967 }
2969 {
2970 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2971 gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2972 gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2973 }
2975 {
2976 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2977 gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2978 gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2979 }
2981 {
2982 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2983 gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2984 gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2985 }
2986 }
2988 sp_rtb_sensitivize( tbl );
2990 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2991 }
2994 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2995 NULL, /* child_added */
2996 NULL, /* child_removed */
2997 rect_tb_event_attr_changed,
2998 NULL, /* content_changed */
2999 NULL /* order_changed */
3000 };
3002 /**
3003 * \param selection should not be NULL.
3004 */
3005 static void
3006 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3007 {
3008 int n_selected = 0;
3009 Inkscape::XML::Node *repr = NULL;
3010 SPItem *item = NULL;
3012 if ( g_object_get_data( tbl, "repr" ) ) {
3013 g_object_set_data( tbl, "item", NULL );
3014 }
3015 purge_repr_listener( tbl, tbl );
3017 for (GSList const *items = selection->itemList();
3018 items != NULL;
3019 items = items->next) {
3020 if (SP_IS_RECT((SPItem *) items->data)) {
3021 n_selected++;
3022 item = (SPItem *) items->data;
3023 repr = SP_OBJECT_REPR(item);
3024 }
3025 }
3027 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3029 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3031 if (n_selected == 0) {
3032 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3034 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3035 gtk_action_set_sensitive(w, FALSE);
3036 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3037 gtk_action_set_sensitive(h, FALSE);
3039 } else if (n_selected == 1) {
3040 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3041 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3043 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3044 gtk_action_set_sensitive(w, TRUE);
3045 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3046 gtk_action_set_sensitive(h, TRUE);
3048 if (repr) {
3049 g_object_set_data( tbl, "repr", repr );
3050 g_object_set_data( tbl, "item", item );
3051 Inkscape::GC::anchor(repr);
3052 sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
3053 sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
3054 }
3055 } else {
3056 // FIXME: implement averaging of all parameters for multiple selected
3057 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3058 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3059 sp_rtb_sensitivize( tbl );
3060 }
3061 }
3064 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3065 {
3066 EgeAdjustmentAction* eact = 0;
3067 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3069 {
3070 EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
3071 ege_output_action_set_use_markup( act, TRUE );
3072 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3073 g_object_set_data( holder, "mode_action", act );
3074 }
3076 // rx/ry units menu: create
3077 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
3078 //tracker->addUnit( SP_UNIT_PERCENT, 0 );
3079 // fixme: add % meaning per cent of the width/height
3080 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
3081 g_object_set_data( holder, "tracker", tracker );
3083 /* W */
3084 {
3085 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3086 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3087 eact = create_adjustment_action( "RectWidthAction",
3088 _("Width"), _("W:"), _("Width of rectangle"),
3089 "/tools/shapes/rect/width", 0,
3090 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
3091 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3092 labels, values, G_N_ELEMENTS(labels),
3093 sp_rtb_width_value_changed );
3094 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3095 g_object_set_data( holder, "width_action", eact );
3096 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3097 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3098 }
3100 /* H */
3101 {
3102 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3103 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3104 eact = create_adjustment_action( "RectHeightAction",
3105 _("Height"), _("H:"), _("Height of rectangle"),
3106 "/tools/shapes/rect/height", 0,
3107 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3108 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3109 labels, values, G_N_ELEMENTS(labels),
3110 sp_rtb_height_value_changed );
3111 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3112 g_object_set_data( holder, "height_action", eact );
3113 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3114 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3115 }
3117 /* rx */
3118 {
3119 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3120 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3121 eact = create_adjustment_action( "RadiusXAction",
3122 _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
3123 "/tools/shapes/rect/rx", 0,
3124 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3125 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3126 labels, values, G_N_ELEMENTS(labels),
3127 sp_rtb_rx_value_changed);
3128 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3129 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3130 }
3132 /* ry */
3133 {
3134 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3135 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3136 eact = create_adjustment_action( "RadiusYAction",
3137 _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
3138 "/tools/shapes/rect/ry", 0,
3139 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3140 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3141 labels, values, G_N_ELEMENTS(labels),
3142 sp_rtb_ry_value_changed);
3143 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3144 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3145 }
3147 // add the units menu
3148 {
3149 GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
3150 gtk_action_group_add_action( mainActions, act );
3151 }
3153 /* Reset */
3154 {
3155 InkAction* inky = ink_action_new( "RectResetAction",
3156 _("Not rounded"),
3157 _("Make corners sharp"),
3158 INKSCAPE_ICON_RECTANGLE_MAKE_CORNERS_SHARP,
3159 secondarySize );
3160 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
3161 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3162 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3163 g_object_set_data( holder, "not_rounded", inky );
3164 }
3166 g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
3167 sp_rtb_sensitivize( holder );
3169 sigc::connection *connection = new sigc::connection(
3170 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
3171 );
3172 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3173 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3174 }
3176 //########################
3177 //## 3D Box ##
3178 //########################
3180 // normalize angle so that it lies in the interval [0,360]
3181 static double box3d_normalize_angle (double a) {
3182 double angle = a + ((int) (a/360.0))*360;
3183 if (angle < 0) {
3184 angle += 360.0;
3185 }
3186 return angle;
3187 }
3189 static void
3190 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
3191 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
3192 // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
3193 // have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
3194 // are reset).
3195 bool is_infinite = !persp3d_VP_is_finite(persp, axis);
3197 if (is_infinite) {
3198 gtk_toggle_action_set_active(tact, TRUE);
3199 gtk_action_set_sensitive(act, TRUE);
3201 double angle = persp3d_get_infinite_angle(persp, axis);
3202 if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
3203 gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
3204 }
3205 } else {
3206 gtk_toggle_action_set_active(tact, FALSE);
3207 gtk_action_set_sensitive(act, FALSE);
3208 }
3209 }
3211 static void
3212 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
3213 if (!persp_repr) {
3214 g_print ("No perspective given to box3d_resync_toolbar().\n");
3215 return;
3216 }
3218 GtkWidget *tbl = GTK_WIDGET(data);
3219 GtkAdjustment *adj = 0;
3220 GtkAction *act = 0;
3221 GtkToggleAction *tact = 0;
3222 Persp3D *persp = persp3d_get_from_repr(persp_repr);
3223 {
3224 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
3225 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
3226 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
3228 box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
3229 }
3230 {
3231 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
3232 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
3233 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
3235 box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
3236 }
3237 {
3238 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
3239 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
3240 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
3242 box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
3243 }
3244 }
3246 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3247 gchar const */*old_value*/, gchar const */*new_value*/,
3248 bool /*is_interactive*/, gpointer data)
3249 {
3250 GtkWidget *tbl = GTK_WIDGET(data);
3252 // quit if run by the attr_changed or selection changed listener
3253 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3254 return;
3255 }
3257 // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
3258 // sp_document_maybe_done() when the document is undo insensitive)
3259 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3261 // TODO: Only update the appropriate part of the toolbar
3262 // if (!strcmp(name, "inkscape:vp_z")) {
3263 box3d_resync_toolbar(repr, G_OBJECT(tbl));
3264 // }
3266 Persp3D *persp = persp3d_get_from_repr(repr);
3267 persp3d_update_box_reprs(persp);
3269 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3270 }
3272 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
3273 {
3274 NULL, /* child_added */
3275 NULL, /* child_removed */
3276 box3d_persp_tb_event_attr_changed,
3277 NULL, /* content_changed */
3278 NULL /* order_changed */
3279 };
3281 /**
3282 * \param selection Should not be NULL.
3283 */
3284 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
3285 // Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
3286 static void
3287 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3288 {
3289 // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
3290 // disable the angle entry fields for this direction (otherwise entering a value in them should only
3291 // update the perspectives with infinite VPs and leave the other ones untouched).
3293 Inkscape::XML::Node *persp_repr = NULL;
3294 purge_repr_listener(tbl, tbl);
3296 SPItem *item = selection->singleItem();
3297 if (item && SP_IS_BOX3D(item)) {
3298 // FIXME: Also deal with multiple selected boxes
3299 SPBox3D *box = SP_BOX3D(item);
3300 Persp3D *persp = box3d_get_perspective(box);
3301 persp_repr = SP_OBJECT_REPR(persp);
3302 if (persp_repr) {
3303 g_object_set_data(tbl, "repr", persp_repr);
3304 Inkscape::GC::anchor(persp_repr);
3305 sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
3306 sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
3307 }
3309 inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
3310 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3311 prefs->setString("/tools/shapes/3dbox/persp", persp_repr->attribute("id"));
3313 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
3314 box3d_resync_toolbar(persp_repr, tbl);
3315 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
3316 }
3317 }
3319 static void
3320 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
3321 {
3322 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3323 SPDocument *document = sp_desktop_document(desktop);
3325 // quit if run by the attr_changed or selection changed listener
3326 if (g_object_get_data( dataKludge, "freeze" )) {
3327 return;
3328 }
3330 // in turn, prevent listener from responding
3331 g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
3333 //Persp3D *persp = document->current_persp3d;
3334 std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
3335 if (sel_persps.empty()) {
3336 // this can happen when the document is created; we silently ignore it
3337 return;
3338 }
3339 Persp3D *persp = sel_persps.front();
3341 persp->tmat.set_infinite_direction (axis, adj->value);
3342 SP_OBJECT(persp)->updateRepr();
3344 // TODO: use the correct axis here, too
3345 sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
3347 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
3348 }
3351 static void
3352 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3353 {
3354 box3d_angle_value_changed(adj, dataKludge, Proj::X);
3355 }
3357 static void
3358 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3359 {
3360 box3d_angle_value_changed(adj, dataKludge, Proj::Y);
3361 }
3363 static void
3364 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3365 {
3366 box3d_angle_value_changed(adj, dataKludge, Proj::Z);
3367 }
3370 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
3371 {
3372 // TODO: Take all selected perspectives into account
3373 std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
3374 if (sel_persps.empty()) {
3375 // this can happen when the document is created; we silently ignore it
3376 return;
3377 }
3378 Persp3D *persp = sel_persps.front();
3380 bool set_infinite = gtk_toggle_action_get_active(act);
3381 persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
3382 }
3384 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3385 {
3386 box3d_vp_state_changed(act, box3d_angle, Proj::X);
3387 }
3389 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3390 {
3391 box3d_vp_state_changed(act, box3d_angle, Proj::Y);
3392 }
3394 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3395 {
3396 box3d_vp_state_changed(act, box3d_angle, Proj::Z);
3397 }
3399 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3400 {
3401 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3402 EgeAdjustmentAction* eact = 0;
3403 SPDocument *document = sp_desktop_document (desktop);
3404 Persp3D *persp = document->current_persp3d;
3406 EgeAdjustmentAction* box3d_angle_x = 0;
3407 EgeAdjustmentAction* box3d_angle_y = 0;
3408 EgeAdjustmentAction* box3d_angle_z = 0;
3410 /* Angle X */
3411 {
3412 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3413 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3414 eact = create_adjustment_action( "3DBoxAngleXAction",
3415 _("Angle in X direction"), _("Angle X:"),
3416 // Translators: PL is short for 'perspective line'
3417 _("Angle of PLs in X direction"),
3418 "/tools/shapes/3dbox/box3d_angle_x", 30,
3419 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
3420 -360.0, 360.0, 1.0, 10.0,
3421 labels, values, G_N_ELEMENTS(labels),
3422 box3d_angle_x_value_changed );
3423 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3424 g_object_set_data( holder, "box3d_angle_x_action", eact );
3425 box3d_angle_x = eact;
3426 }
3428 if (!persp3d_VP_is_finite(persp, Proj::X)) {
3429 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3430 } else {
3431 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3432 }
3435 /* VP X state */
3436 {
3437 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
3438 // Translators: VP is short for 'vanishing point'
3439 _("State of VP in X direction"),
3440 _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
3441 INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3442 Inkscape::ICON_SIZE_DECORATION );
3443 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3444 g_object_set_data( holder, "box3d_vp_x_state_action", act );
3445 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
3446 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3447 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3448 }
3450 /* Angle Y */
3451 {
3452 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3453 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3454 eact = create_adjustment_action( "3DBoxAngleYAction",
3455 _("Angle in Y direction"), _("Angle Y:"),
3456 // Translators: PL is short for 'perspective line'
3457 _("Angle of PLs in Y direction"),
3458 "/tools/shapes/3dbox/box3d_angle_y", 30,
3459 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3460 -360.0, 360.0, 1.0, 10.0,
3461 labels, values, G_N_ELEMENTS(labels),
3462 box3d_angle_y_value_changed );
3463 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3464 g_object_set_data( holder, "box3d_angle_y_action", eact );
3465 box3d_angle_y = eact;
3466 }
3468 if (!persp3d_VP_is_finite(persp, Proj::Y)) {
3469 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3470 } else {
3471 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3472 }
3474 /* VP Y state */
3475 {
3476 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
3477 // Translators: VP is short for 'vanishing point'
3478 _("State of VP in Y direction"),
3479 _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
3480 INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3481 Inkscape::ICON_SIZE_DECORATION );
3482 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3483 g_object_set_data( holder, "box3d_vp_y_state_action", act );
3484 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
3485 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3486 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3487 }
3489 /* Angle Z */
3490 {
3491 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3492 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3493 eact = create_adjustment_action( "3DBoxAngleZAction",
3494 _("Angle in Z direction"), _("Angle Z:"),
3495 // Translators: PL is short for 'perspective line'
3496 _("Angle of PLs in Z direction"),
3497 "/tools/shapes/3dbox/box3d_angle_z", 30,
3498 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3499 -360.0, 360.0, 1.0, 10.0,
3500 labels, values, G_N_ELEMENTS(labels),
3501 box3d_angle_z_value_changed );
3502 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3503 g_object_set_data( holder, "box3d_angle_z_action", eact );
3504 box3d_angle_z = eact;
3505 }
3507 if (!persp3d_VP_is_finite(persp, Proj::Z)) {
3508 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3509 } else {
3510 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3511 }
3513 /* VP Z state */
3514 {
3515 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3516 // Translators: VP is short for 'vanishing point'
3517 _("State of VP in Z direction"),
3518 _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3519 INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3520 Inkscape::ICON_SIZE_DECORATION );
3521 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3522 g_object_set_data( holder, "box3d_vp_z_state_action", act );
3523 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3524 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3525 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3526 }
3528 sigc::connection *connection = new sigc::connection(
3529 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3530 );
3531 g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3532 g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3533 }
3535 //########################
3536 //## Spiral ##
3537 //########################
3539 static void
3540 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, Glib::ustring const &value_name)
3541 {
3542 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3544 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3545 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3546 prefs->setDouble("/tools/shapes/spiral/" + value_name, adj->value);
3547 }
3549 // quit if run by the attr_changed listener
3550 if (g_object_get_data( tbl, "freeze" )) {
3551 return;
3552 }
3554 // in turn, prevent listener from responding
3555 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3557 gchar* namespaced_name = g_strconcat("sodipodi:", value_name.data(), NULL);
3559 bool modmade = false;
3560 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3561 items != NULL;
3562 items = items->next)
3563 {
3564 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3565 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3566 sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3567 SP_OBJECT((SPItem *) items->data)->updateRepr();
3568 modmade = true;
3569 }
3570 }
3572 g_free(namespaced_name);
3574 if (modmade) {
3575 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3576 _("Change spiral"));
3577 }
3579 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3580 }
3582 static void
3583 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3584 {
3585 sp_spl_tb_value_changed(adj, tbl, "revolution");
3586 }
3588 static void
3589 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3590 {
3591 sp_spl_tb_value_changed(adj, tbl, "expansion");
3592 }
3594 static void
3595 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3596 {
3597 sp_spl_tb_value_changed(adj, tbl, "t0");
3598 }
3600 static void
3601 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3602 {
3603 GtkWidget *tbl = GTK_WIDGET(obj);
3605 GtkAdjustment *adj;
3607 // fixme: make settable
3608 gdouble rev = 5;
3609 gdouble exp = 1.0;
3610 gdouble t0 = 0.0;
3612 adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3613 gtk_adjustment_set_value(adj, rev);
3614 gtk_adjustment_value_changed(adj);
3616 adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3617 gtk_adjustment_set_value(adj, exp);
3618 gtk_adjustment_value_changed(adj);
3620 adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3621 gtk_adjustment_set_value(adj, t0);
3622 gtk_adjustment_value_changed(adj);
3624 spinbutton_defocus(GTK_OBJECT(tbl));
3625 }
3628 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3629 gchar const */*old_value*/, gchar const */*new_value*/,
3630 bool /*is_interactive*/, gpointer data)
3631 {
3632 GtkWidget *tbl = GTK_WIDGET(data);
3634 // quit if run by the _changed callbacks
3635 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3636 return;
3637 }
3639 // in turn, prevent callbacks from responding
3640 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3642 GtkAdjustment *adj;
3643 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3644 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3646 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3647 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3649 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3650 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3652 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3653 }
3656 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3657 NULL, /* child_added */
3658 NULL, /* child_removed */
3659 spiral_tb_event_attr_changed,
3660 NULL, /* content_changed */
3661 NULL /* order_changed */
3662 };
3664 static void
3665 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3666 {
3667 int n_selected = 0;
3668 Inkscape::XML::Node *repr = NULL;
3670 purge_repr_listener( tbl, tbl );
3672 for (GSList const *items = selection->itemList();
3673 items != NULL;
3674 items = items->next)
3675 {
3676 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3677 n_selected++;
3678 repr = SP_OBJECT_REPR((SPItem *) items->data);
3679 }
3680 }
3682 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3684 if (n_selected == 0) {
3685 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3686 } else if (n_selected == 1) {
3687 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3689 if (repr) {
3690 g_object_set_data( tbl, "repr", repr );
3691 Inkscape::GC::anchor(repr);
3692 sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3693 sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3694 }
3695 } else {
3696 // FIXME: implement averaging of all parameters for multiple selected
3697 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3698 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3699 }
3700 }
3703 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3704 {
3705 EgeAdjustmentAction* eact = 0;
3706 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3708 {
3709 EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3710 ege_output_action_set_use_markup( act, TRUE );
3711 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3712 g_object_set_data( holder, "mode_action", act );
3713 }
3715 /* Revolution */
3716 {
3717 gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3718 gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3719 eact = create_adjustment_action( "SpiralRevolutionAction",
3720 _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3721 "/tools/shapes/spiral/revolution", 3.0,
3722 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3723 0.01, 1024.0, 0.1, 1.0,
3724 labels, values, G_N_ELEMENTS(labels),
3725 sp_spl_tb_revolution_value_changed, 1, 2);
3726 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3727 }
3729 /* Expansion */
3730 {
3731 gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3732 gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3733 eact = create_adjustment_action( "SpiralExpansionAction",
3734 _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3735 "/tools/shapes/spiral/expansion", 1.0,
3736 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3737 0.0, 1000.0, 0.01, 1.0,
3738 labels, values, G_N_ELEMENTS(labels),
3739 sp_spl_tb_expansion_value_changed);
3740 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3741 }
3743 /* T0 */
3744 {
3745 gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3746 gdouble values[] = {0, 0.5, 0.9};
3747 eact = create_adjustment_action( "SpiralT0Action",
3748 _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3749 "/tools/shapes/spiral/t0", 0.0,
3750 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3751 0.0, 0.999, 0.01, 1.0,
3752 labels, values, G_N_ELEMENTS(labels),
3753 sp_spl_tb_t0_value_changed);
3754 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3755 }
3757 /* Reset */
3758 {
3759 InkAction* inky = ink_action_new( "SpiralResetAction",
3760 _("Defaults"),
3761 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3762 GTK_STOCK_CLEAR,
3763 secondarySize );
3764 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3765 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3766 }
3769 sigc::connection *connection = new sigc::connection(
3770 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3771 );
3772 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3773 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3774 }
3776 //########################
3777 //## Pen/Pencil ##
3778 //########################
3780 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3781 static Glib::ustring const
3782 freehand_tool_name(GObject *dataKludge)
3783 {
3784 SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3785 return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3786 ? "/tools/freehand/pen"
3787 : "/tools/freehand/pencil" );
3788 }
3790 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3791 {
3792 gint mode = ege_select_one_action_get_active(act);
3794 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3795 prefs->setInt(freehand_tool_name(tbl) + "/freehand-mode", mode);
3797 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3799 // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3800 // preparatory work here
3801 if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3802 SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3803 sp_pen_context_set_polyline_mode(pc);
3804 }
3805 }
3807 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3808 {
3809 /* Freehand mode toggle buttons */
3810 {
3811 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3812 guint freehandMode = prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/freehand-mode" : "/tools/freehand/pen/freehand-mode" ), 0);
3813 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3815 {
3816 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3818 GtkTreeIter iter;
3819 gtk_list_store_append( model, &iter );
3820 gtk_list_store_set( model, &iter,
3821 0, _("Bezier"),
3822 1, _("Create regular Bezier path"),
3823 2, INKSCAPE_ICON_PATH_MODE_BEZIER,
3824 -1 );
3826 gtk_list_store_append( model, &iter );
3827 gtk_list_store_set( model, &iter,
3828 0, _("Spiro"),
3829 1, _("Create Spiro path"),
3830 2, INKSCAPE_ICON_PATH_MODE_SPIRO,
3831 -1 );
3833 if (!tool_is_pencil) {
3834 gtk_list_store_append( model, &iter );
3835 gtk_list_store_set( model, &iter,
3836 0, _("Zigzag"),
3837 1, _("Create a sequence of straight line segments"),
3838 2, INKSCAPE_ICON_PATH_MODE_POLYLINE,
3839 -1 );
3841 gtk_list_store_append( model, &iter );
3842 gtk_list_store_set( model, &iter,
3843 0, _("Paraxial"),
3844 1, _("Create a sequence of paraxial line segments"),
3845 2, INKSCAPE_ICON_PATH_MODE_POLYLINE_PARAXIAL,
3846 -1 );
3847 }
3849 EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3850 "FreehandModeActionPencil" :
3851 "FreehandModeActionPen",
3852 (_("Mode:")), ("Mode"), NULL, GTK_TREE_MODEL(model) );
3853 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3855 ege_select_one_action_set_appearance( act, "full" );
3856 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3857 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3858 ege_select_one_action_set_icon_column( act, 2 );
3859 ege_select_one_action_set_icon_size( act, secondarySize );
3860 ege_select_one_action_set_tooltip_column( act, 1 );
3862 ege_select_one_action_set_active( act, freehandMode);
3863 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3864 }
3865 }
3866 }
3868 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3869 gint shape = ege_select_one_action_get_active( act );
3870 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3871 prefs->setInt(freehand_tool_name(dataKludge) + "/shape", shape);
3872 }
3874 /**
3875 * \brief Generate the list of freehand advanced shape option entries.
3876 */
3877 GList * freehand_shape_dropdown_items_list() {
3878 GList *glist = NULL;
3880 glist = g_list_append (glist, _("None"));
3881 glist = g_list_append (glist, _("Triangle in"));
3882 glist = g_list_append (glist, _("Triangle out"));
3883 glist = g_list_append (glist, _("Ellipse"));
3884 glist = g_list_append (glist, _("From clipboard"));
3886 return glist;
3887 }
3889 static void
3890 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3891 /*advanced shape options */
3892 {
3893 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3894 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3896 GList* items = 0;
3897 gint count = 0;
3898 for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3899 {
3900 GtkTreeIter iter;
3901 gtk_list_store_append( model, &iter );
3902 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3903 count++;
3904 }
3905 g_list_free( items );
3906 items = 0;
3907 EgeSelectOneAction* act1 = ege_select_one_action_new(
3908 tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3909 _("Shape:"), ("Shape"), NULL, GTK_TREE_MODEL(model));
3910 g_object_set( act1, "short_label", _("Shape:"), NULL );
3911 ege_select_one_action_set_appearance( act1, "compact" );
3912 ege_select_one_action_set_active( act1, prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/shape" : "/tools/freehand/pen/shape" ), 0) );
3913 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3914 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3915 g_object_set_data( holder, "shape_action", act1 );
3916 }
3917 }
3919 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3920 {
3921 sp_add_freehand_mode_toggle(mainActions, holder, false);
3922 freehand_add_advanced_shape_options(mainActions, holder, false);
3923 }
3926 static void
3927 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3928 {
3929 GtkWidget *tbl = GTK_WIDGET(obj);
3931 GtkAdjustment *adj;
3933 // fixme: make settable
3934 gdouble tolerance = 4;
3936 adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3937 gtk_adjustment_set_value(adj, tolerance);
3938 gtk_adjustment_value_changed(adj);
3940 spinbutton_defocus(GTK_OBJECT(tbl));
3941 }
3943 static void
3944 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3945 {
3946 // quit if run by the attr_changed listener
3947 if (g_object_get_data( tbl, "freeze" )) {
3948 return;
3949 }
3950 // in turn, prevent listener from responding
3951 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3952 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3953 prefs->setDouble("/tools/freehand/pencil/tolerance", adj->value);
3954 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3955 }
3957 class PencilToleranceObserver : public Inkscape::Preferences::Observer {
3958 public:
3959 PencilToleranceObserver(Glib::ustring const &path, GObject *x) : Observer(path), _obj(x)
3960 {
3961 g_object_set_data(_obj, "prefobserver", this);
3962 }
3963 virtual ~PencilToleranceObserver() {
3964 if (g_object_get_data(_obj, "prefobserver") == this) {
3965 g_object_set_data(_obj, "prefobserver", NULL);
3966 }
3967 }
3968 virtual void notify(Inkscape::Preferences::Entry const &val) {
3969 GObject* tbl = _obj;
3970 if (g_object_get_data( tbl, "freeze" )) {
3971 return;
3972 }
3973 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3975 GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl, "tolerance");
3977 double v = val.getDouble(adj->value);
3978 gtk_adjustment_set_value(adj, v);
3979 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3980 }
3981 private:
3982 GObject *_obj;
3983 };
3986 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3987 {
3988 sp_add_freehand_mode_toggle(mainActions, holder, true);
3990 EgeAdjustmentAction* eact = 0;
3992 /* Tolerance */
3993 {
3994 gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
3995 gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
3996 eact = create_adjustment_action( "PencilToleranceAction",
3997 _("Smoothing:"), _("Smoothing: "),
3998 _("How much smoothing (simplifying) is applied to the line"),
3999 "/tools/freehand/pencil/tolerance",
4000 3.0,
4001 GTK_WIDGET(desktop->canvas), NULL,
4002 holder, TRUE, "altx-pencil",
4003 1, 100.0, 0.5, 1.0,
4004 labels, values, G_N_ELEMENTS(labels),
4005 sp_pencil_tb_tolerance_value_changed,
4006 1, 2);
4007 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4008 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4010 PencilToleranceObserver *obs =
4011 new PencilToleranceObserver("/tools/freehand/pencil/tolerance", G_OBJECT(holder));
4012 }
4014 /* advanced shape options */
4015 freehand_add_advanced_shape_options(mainActions, holder, true);
4017 /* Reset */
4018 {
4019 InkAction* inky = ink_action_new( "PencilResetAction",
4020 _("Defaults"),
4021 _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
4022 GTK_STOCK_CLEAR,
4023 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4024 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
4025 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4026 }
4028 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4030 }
4033 //########################
4034 //## Tweak ##
4035 //########################
4037 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4038 {
4039 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4040 prefs->setDouble( "/tools/tweak/width", adj->value * 0.01 );
4041 }
4043 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4044 {
4045 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4046 prefs->setDouble( "/tools/tweak/force", adj->value * 0.01 );
4047 }
4049 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4050 {
4051 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4052 prefs->setBool("/tools/tweak/usepressure", gtk_toggle_action_get_active(act));
4053 }
4055 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4056 {
4057 int mode = ege_select_one_action_get_active( act );
4058 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4059 prefs->setInt("/tools/tweak/mode", mode);
4061 GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
4062 GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
4063 GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
4064 GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
4065 GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
4066 GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
4067 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
4068 if (doh) gtk_action_set_sensitive (doh, TRUE);
4069 if (dos) gtk_action_set_sensitive (dos, TRUE);
4070 if (dol) gtk_action_set_sensitive (dol, TRUE);
4071 if (doo) gtk_action_set_sensitive (doo, TRUE);
4072 if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
4073 if (fid) gtk_action_set_sensitive (fid, FALSE);
4074 } else {
4075 if (doh) gtk_action_set_sensitive (doh, FALSE);
4076 if (dos) gtk_action_set_sensitive (dos, FALSE);
4077 if (dol) gtk_action_set_sensitive (dol, FALSE);
4078 if (doo) gtk_action_set_sensitive (doo, FALSE);
4079 if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
4080 if (fid) gtk_action_set_sensitive (fid, TRUE);
4081 }
4082 }
4084 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4085 {
4086 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4087 prefs->setDouble( "/tools/tweak/fidelity", adj->value * 0.01 );
4088 }
4090 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
4091 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4092 prefs->setBool("/tools/tweak/doh", gtk_toggle_action_get_active(act));
4093 }
4094 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
4095 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4096 prefs->setBool("/tools/tweak/dos", gtk_toggle_action_get_active(act));
4097 }
4098 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
4099 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4100 prefs->setBool("/tools/tweak/dol", gtk_toggle_action_get_active(act));
4101 }
4102 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
4103 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4104 prefs->setBool("/tools/tweak/doo", gtk_toggle_action_get_active(act));
4105 }
4107 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4108 {
4109 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
4110 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4112 {
4113 /* Width */
4114 gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
4115 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4116 EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
4117 _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
4118 "/tools/tweak/width", 15,
4119 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
4120 1, 100, 1.0, 10.0,
4121 labels, values, G_N_ELEMENTS(labels),
4122 sp_tweak_width_value_changed, 0.01, 0, 100 );
4123 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4124 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4125 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4126 }
4129 {
4130 /* Force */
4131 gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
4132 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4133 EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
4134 _("Force"), _("Force:"), _("The force of the tweak action"),
4135 "/tools/tweak/force", 20,
4136 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
4137 1, 100, 1.0, 10.0,
4138 labels, values, G_N_ELEMENTS(labels),
4139 sp_tweak_force_value_changed, 0.01, 0, 100 );
4140 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4141 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4142 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4143 }
4145 /* Mode */
4146 {
4147 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4149 GtkTreeIter iter;
4150 gtk_list_store_append( model, &iter );
4151 gtk_list_store_set( model, &iter,
4152 0, _("Move mode"),
4153 1, _("Move objects in any direction"),
4154 2, INKSCAPE_ICON_OBJECT_TWEAK_PUSH,
4155 -1 );
4157 gtk_list_store_append( model, &iter );
4158 gtk_list_store_set( model, &iter,
4159 0, _("Move in/out mode"),
4160 1, _("Move objects towards cursor; with Shift from cursor"),
4161 2, INKSCAPE_ICON_OBJECT_TWEAK_ATTRACT,
4162 -1 );
4164 gtk_list_store_append( model, &iter );
4165 gtk_list_store_set( model, &iter,
4166 0, _("Move jitter mode"),
4167 1, _("Move objects in random directions"),
4168 2, INKSCAPE_ICON_OBJECT_TWEAK_RANDOMIZE,
4169 -1 );
4171 gtk_list_store_append( model, &iter );
4172 gtk_list_store_set( model, &iter,
4173 0, _("Scale mode"),
4174 1, _("Shrink objects, with Shift enlarge"),
4175 2, INKSCAPE_ICON_OBJECT_TWEAK_SHRINK,
4176 -1 );
4178 gtk_list_store_append( model, &iter );
4179 gtk_list_store_set( model, &iter,
4180 0, _("Rotate mode"),
4181 1, _("Rotate objects, with Shift counterclockwise"),
4182 2, INKSCAPE_ICON_OBJECT_TWEAK_ROTATE,
4183 -1 );
4185 gtk_list_store_append( model, &iter );
4186 gtk_list_store_set( model, &iter,
4187 0, _("Duplicate/delete mode"),
4188 1, _("Duplicate objects, with Shift delete"),
4189 2, INKSCAPE_ICON_OBJECT_TWEAK_DUPLICATE,
4190 -1 );
4192 gtk_list_store_append( model, &iter );
4193 gtk_list_store_set( model, &iter,
4194 0, _("Push mode"),
4195 1, _("Push parts of paths in any direction"),
4196 2, INKSCAPE_ICON_PATH_TWEAK_PUSH,
4197 -1 );
4199 gtk_list_store_append( model, &iter );
4200 gtk_list_store_set( model, &iter,
4201 0, _("Shrink/grow mode"),
4202 1, _("Shrink (inset) parts of paths; with Shift grow (outset)"),
4203 2, INKSCAPE_ICON_PATH_TWEAK_SHRINK,
4204 -1 );
4206 gtk_list_store_append( model, &iter );
4207 gtk_list_store_set( model, &iter,
4208 0, _("Attract/repel mode"),
4209 1, _("Attract parts of paths towards cursor; with Shift from cursor"),
4210 2, INKSCAPE_ICON_PATH_TWEAK_ATTRACT,
4211 -1 );
4213 gtk_list_store_append( model, &iter );
4214 gtk_list_store_set( model, &iter,
4215 0, _("Roughen mode"),
4216 1, _("Roughen parts of paths"),
4217 2, INKSCAPE_ICON_PATH_TWEAK_ROUGHEN,
4218 -1 );
4220 gtk_list_store_append( model, &iter );
4221 gtk_list_store_set( model, &iter,
4222 0, _("Color paint mode"),
4223 1, _("Paint the tool's color upon selected objects"),
4224 2, INKSCAPE_ICON_OBJECT_TWEAK_PAINT,
4225 -1 );
4227 gtk_list_store_append( model, &iter );
4228 gtk_list_store_set( model, &iter,
4229 0, _("Color jitter mode"),
4230 1, _("Jitter the colors of selected objects"),
4231 2, INKSCAPE_ICON_OBJECT_TWEAK_JITTER_COLOR,
4232 -1 );
4234 gtk_list_store_append( model, &iter );
4235 gtk_list_store_set( model, &iter,
4236 0, _("Blur mode"),
4237 1, _("Blur selected objects more; with Shift, blur less"),
4238 2, INKSCAPE_ICON_OBJECT_TWEAK_BLUR,
4239 -1 );
4242 EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4243 g_object_set( act, "short_label", _("Mode:"), NULL );
4244 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4245 g_object_set_data( holder, "mode_action", act );
4247 ege_select_one_action_set_appearance( act, "full" );
4248 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4249 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4250 ege_select_one_action_set_icon_column( act, 2 );
4251 ege_select_one_action_set_icon_size( act, secondarySize );
4252 ege_select_one_action_set_tooltip_column( act, 1 );
4254 gint mode = prefs->getInt("/tools/tweak/mode", 0);
4255 ege_select_one_action_set_active( act, mode );
4256 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
4258 g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
4259 }
4261 guint mode = prefs->getInt("/tools/tweak/mode", 0);
4263 {
4264 EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
4265 ege_output_action_set_use_markup( act, TRUE );
4266 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4267 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4268 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4269 g_object_set_data( holder, "tweak_channels_label", act);
4270 }
4272 {
4273 InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
4274 _("Hue"),
4275 _("In color mode, act on objects' hue"),
4276 NULL,
4277 Inkscape::ICON_SIZE_DECORATION );
4278 //TRANSLATORS: "H" here stands for hue
4279 g_object_set( act, "short_label", _("H"), NULL );
4280 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4281 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
4282 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doh", true) );
4283 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4284 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4285 g_object_set_data( holder, "tweak_doh", act);
4286 }
4287 {
4288 InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
4289 _("Saturation"),
4290 _("In color mode, act on objects' saturation"),
4291 NULL,
4292 Inkscape::ICON_SIZE_DECORATION );
4293 //TRANSLATORS: "S" here stands for Saturation
4294 g_object_set( act, "short_label", _("S"), NULL );
4295 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4296 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
4297 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dos", true) );
4298 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4299 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4300 g_object_set_data( holder, "tweak_dos", act );
4301 }
4302 {
4303 InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
4304 _("Lightness"),
4305 _("In color mode, act on objects' lightness"),
4306 NULL,
4307 Inkscape::ICON_SIZE_DECORATION );
4308 //TRANSLATORS: "L" here stands for Lightness
4309 g_object_set( act, "short_label", _("L"), NULL );
4310 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4311 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
4312 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dol", true) );
4313 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4314 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4315 g_object_set_data( holder, "tweak_dol", act );
4316 }
4317 {
4318 InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
4319 _("Opacity"),
4320 _("In color mode, act on objects' opacity"),
4321 NULL,
4322 Inkscape::ICON_SIZE_DECORATION );
4323 //TRANSLATORS: "O" here stands for Opacity
4324 g_object_set( act, "short_label", _("O"), NULL );
4325 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4326 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
4327 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doo", true) );
4328 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4329 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4330 g_object_set_data( holder, "tweak_doo", act );
4331 }
4333 { /* Fidelity */
4334 gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
4335 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4336 EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
4337 _("Fidelity"), _("Fidelity:"),
4338 _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
4339 "/tools/tweak/fidelity", 50,
4340 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
4341 1, 100, 1.0, 10.0,
4342 labels, values, G_N_ELEMENTS(labels),
4343 sp_tweak_fidelity_value_changed, 0.01, 0, 100 );
4344 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4345 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4346 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
4347 gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
4348 g_object_set_data( holder, "tweak_fidelity", eact );
4349 }
4352 /* Use Pressure button */
4353 {
4354 InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
4355 _("Pressure"),
4356 _("Use the pressure of the input device to alter the force of tweak action"),
4357 INKSCAPE_ICON_DRAW_USE_PRESSURE,
4358 Inkscape::ICON_SIZE_DECORATION );
4359 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4360 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
4361 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/usepressure", true) );
4362 }
4364 }
4367 //########################
4368 //## Calligraphy ##
4369 //########################
4370 static void update_presets_list (GObject *tbl)
4371 {
4372 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4373 if (g_object_get_data(tbl, "presets_blocked"))
4374 return;
4376 EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4377 if (!sel) {
4378 // WTF!? This will cause a segfault if ever reached
4379 //ege_select_one_action_set_active(sel, 0);
4380 return;
4381 }
4383 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4385 int ege_index = 1;
4386 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++ege_index) {
4387 bool match = true;
4389 std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(*i);
4390 for (std::vector<Inkscape::Preferences::Entry>::iterator j = preset.begin(); j != preset.end(); ++j) {
4391 Glib::ustring entry_name = j->getEntryName();
4392 if (entry_name == "id" || entry_name == "name") continue;
4394 void *widget = g_object_get_data(tbl, entry_name.data());
4395 if (widget) {
4396 if (GTK_IS_ADJUSTMENT(widget)) {
4397 double v = j->getDouble();
4398 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4399 //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
4400 if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
4401 match = false;
4402 break;
4403 }
4404 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4405 bool v = j->getBool();
4406 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4407 //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
4408 if ( static_cast<bool>(gtk_toggle_action_get_active(toggle)) != v ) {
4409 match = false;
4410 break;
4411 }
4412 }
4413 }
4414 }
4416 if (match) {
4417 // newly added item is at the same index as the
4418 // save command, so we need to change twice for it to take effect
4419 ege_select_one_action_set_active(sel, 0);
4420 ege_select_one_action_set_active(sel, ege_index); // one-based index
4421 return;
4422 }
4423 }
4425 // no match found
4426 ege_select_one_action_set_active(sel, 0);
4427 }
4429 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
4430 {
4431 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4432 prefs->setDouble( "/tools/calligraphic/mass", adj->value * 0.01 );
4433 update_presets_list(tbl);
4434 }
4436 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
4437 {
4438 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4439 prefs->setDouble( "/tools/calligraphic/wiggle", adj->value * 0.01 );
4440 update_presets_list(tbl);
4441 }
4443 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
4444 {
4445 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4446 prefs->setDouble( "/tools/calligraphic/angle", adj->value );
4447 update_presets_list(tbl);
4448 }
4450 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
4451 {
4452 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4453 prefs->setDouble( "/tools/calligraphic/width", adj->value * 0.01 );
4454 update_presets_list(tbl);
4455 }
4457 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
4458 {
4459 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4460 prefs->setDouble("/tools/calligraphic/thinning", adj->value * 0.01 );
4461 update_presets_list(tbl);
4462 }
4464 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
4465 {
4466 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4467 prefs->setDouble( "/tools/calligraphic/flatness", adj->value * 0.01);
4468 update_presets_list(tbl);
4469 }
4471 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
4472 {
4473 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4474 prefs->setDouble( "/tools/calligraphic/tremor", adj->value * 0.01 );
4475 update_presets_list(tbl);
4476 }
4478 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
4479 {
4480 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4481 prefs->setDouble( "/tools/calligraphic/cap_rounding", adj->value );
4482 update_presets_list(tbl);
4483 }
4485 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject* tbl )
4486 {
4487 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4488 prefs->setBool("/tools/calligraphic/usepressure", gtk_toggle_action_get_active( act ));
4489 update_presets_list(tbl);
4490 }
4492 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject* tbl )
4493 {
4494 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4495 prefs->setBool("/tools/calligraphic/tracebackground", gtk_toggle_action_get_active( act ));
4496 update_presets_list(tbl);
4497 }
4499 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject* tbl )
4500 {
4501 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4502 GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
4503 prefs->setBool("/tools/calligraphic/usetilt", gtk_toggle_action_get_active( act ));
4504 update_presets_list(tbl);
4505 if (calligraphy_angle )
4506 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
4507 }
4510 static gchar const *const widget_names[] = {
4511 "width",
4512 "mass",
4513 "wiggle",
4514 "angle",
4515 "thinning",
4516 "tremor",
4517 "flatness",
4518 "cap_rounding",
4519 "usepressure",
4520 "tracebackground",
4521 "usetilt"
4522 };
4525 static void sp_dcc_build_presets_list(GObject *tbl)
4526 {
4527 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4529 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4530 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4531 gtk_list_store_clear (model);
4533 {
4534 GtkTreeIter iter;
4535 gtk_list_store_append( model, &iter );
4536 gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4537 }
4539 // iterate over all presets to populate the list
4540 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4541 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4542 int ii=1;
4544 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i) {
4545 GtkTreeIter iter;
4546 Glib::ustring preset_name = prefs->getString(*i + "/name");
4547 gtk_list_store_append( model, &iter );
4548 gtk_list_store_set( model, &iter, 0, preset_name.data(), 1, ii++, -1 );
4549 }
4551 {
4552 GtkTreeIter iter;
4553 gtk_list_store_append( model, &iter );
4554 gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4555 g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4556 }
4558 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4560 update_presets_list (tbl);
4561 }
4563 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4564 {
4565 using Inkscape::UI::Dialog::CalligraphicProfileRename;
4566 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4567 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4568 if (! desktop) return;
4570 if (g_object_get_data(tbl, "presets_blocked"))
4571 return;
4573 CalligraphicProfileRename::show(desktop);
4574 if ( !CalligraphicProfileRename::applied()) {
4575 // dialog cancelled
4576 update_presets_list (tbl);
4577 return;
4578 }
4579 Glib::ustring profile_name = CalligraphicProfileRename::getProfileName();
4581 if (profile_name.empty()) {
4582 // empty name entered
4583 update_presets_list (tbl);
4584 return;
4585 }
4587 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4589 // If there's a preset with the given name, find it and set save_path appropriately
4590 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4591 int total_presets = presets.size();
4592 int new_index = -1;
4593 Glib::ustring save_path; // profile pref path without a trailing slash
4595 int temp_index = 0;
4596 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++temp_index) {
4597 Glib::ustring name = prefs->getString(*i + "/name");
4598 if (!name.empty() && profile_name == name) {
4599 new_index = temp_index;
4600 save_path = *i;
4601 break;
4602 }
4603 }
4605 if (new_index == -1) {
4606 // no preset with this name, create
4607 new_index = total_presets + 1;
4608 gchar *profile_id = g_strdup_printf("/dcc%d", new_index);
4609 save_path = Glib::ustring("/tools/calligraphic/preset") + profile_id;
4610 g_free(profile_id);
4611 }
4613 for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4614 gchar const *const widget_name = widget_names[i];
4615 void *widget = g_object_get_data(tbl, widget_name);
4616 if (widget) {
4617 if (GTK_IS_ADJUSTMENT(widget)) {
4618 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4619 prefs->setDouble(save_path + "/" + widget_name, gtk_adjustment_get_value(adj));
4620 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4621 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4622 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4623 prefs->setBool(save_path + "/" + widget_name, gtk_toggle_action_get_active(toggle));
4624 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4625 } else {
4626 g_warning("Unknown widget type for preset: %s\n", widget_name);
4627 }
4628 } else {
4629 g_warning("Bad key when writing preset: %s\n", widget_name);
4630 }
4631 }
4632 prefs->setString(save_path + "/name", profile_name);
4634 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4635 sp_dcc_build_presets_list (tbl);
4636 }
4639 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4641 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4643 gint preset_index = ege_select_one_action_get_active( act );
4644 // This is necessary because EgeSelectOneAction spams us with GObject "changed" signal calls
4645 // even when the preset is not changed. It would be good to replace it with something more
4646 // modern. Index 0 means "No preset", so we don't do anything.
4647 if (preset_index == 0) return;
4649 gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4651 if (preset_index == save_presets_index) {
4652 // this is the Save command
4653 sp_dcc_save_profile(NULL, tbl);
4654 return;
4655 }
4657 if (g_object_get_data(tbl, "presets_blocked"))
4658 return;
4660 // preset_index is one-based so we subtract 1
4661 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4662 Glib::ustring preset_path = presets.at(preset_index - 1);
4664 if (!preset_path.empty()) {
4665 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
4667 std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(preset_path);
4669 // Shouldn't this be std::map?
4670 for (std::vector<Inkscape::Preferences::Entry>::iterator i = preset.begin(); i != preset.end(); ++i) {
4671 Glib::ustring entry_name = i->getEntryName();
4672 if (entry_name == "id" || entry_name == "name") continue;
4673 void *widget = g_object_get_data(tbl, entry_name.data());
4674 if (widget) {
4675 if (GTK_IS_ADJUSTMENT(widget)) {
4676 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4677 gtk_adjustment_set_value(adj, i->getDouble());
4678 //std::cout << "set adj " << attr_name << " to " << v << "\n";
4679 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4680 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4681 gtk_toggle_action_set_active(toggle, i->getBool());
4682 //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4683 } else {
4684 g_warning("Unknown widget type for preset: %s\n", entry_name.data());
4685 }
4686 } else {
4687 g_warning("Bad key found in a preset record: %s\n", entry_name.data());
4688 }
4689 }
4690 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4691 }
4692 }
4695 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4696 {
4697 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4698 {
4699 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4701 EgeAdjustmentAction* calligraphy_angle = 0;
4703 {
4704 /* Width */
4705 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4706 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4707 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4708 _("Pen Width"), _("Width:"),
4709 _("The width of the calligraphic pen (relative to the visible canvas area)"),
4710 "/tools/calligraphic/width", 15,
4711 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4712 1, 100, 1.0, 10.0,
4713 labels, values, G_N_ELEMENTS(labels),
4714 sp_ddc_width_value_changed, 0.01, 0, 100 );
4715 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4716 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4717 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4718 }
4720 {
4721 /* Thinning */
4722 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4723 gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4724 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4725 _("Stroke Thinning"), _("Thinning:"),
4726 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4727 "/tools/calligraphic/thinning", 10,
4728 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4729 -100, 100, 1, 10.0,
4730 labels, values, G_N_ELEMENTS(labels),
4731 sp_ddc_velthin_value_changed, 0.01, 0, 100);
4732 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4733 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4734 }
4736 {
4737 /* Angle */
4738 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4739 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4740 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4741 _("Pen Angle"), _("Angle:"),
4742 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4743 "/tools/calligraphic/angle", 30,
4744 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4745 -90.0, 90.0, 1.0, 10.0,
4746 labels, values, G_N_ELEMENTS(labels),
4747 sp_ddc_angle_value_changed, 1, 0 );
4748 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4749 g_object_set_data( holder, "angle_action", eact );
4750 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4751 calligraphy_angle = eact;
4752 }
4754 {
4755 /* Fixation */
4756 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4757 gdouble values[] = {0, 20, 40, 60, 90, 100};
4758 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4759 _("Fixation"), _("Fixation:"),
4760 _("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"),
4761 "/tools/calligraphic/flatness", 90,
4762 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4763 0.0, 100, 1.0, 10.0,
4764 labels, values, G_N_ELEMENTS(labels),
4765 sp_ddc_flatness_value_changed, 0.01, 0, 100 );
4766 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4767 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4768 }
4770 {
4771 /* Cap Rounding */
4772 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4773 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4774 // TRANSLATORS: "cap" means "end" (both start and finish) here
4775 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4776 _("Cap rounding"), _("Caps:"),
4777 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4778 "/tools/calligraphic/cap_rounding", 0.0,
4779 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4780 0.0, 5.0, 0.01, 0.1,
4781 labels, values, G_N_ELEMENTS(labels),
4782 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4783 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4784 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4785 }
4787 {
4788 /* Tremor */
4789 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4790 gdouble values[] = {0, 10, 20, 40, 60, 100};
4791 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4792 _("Stroke Tremor"), _("Tremor:"),
4793 _("Increase to make strokes rugged and trembling"),
4794 "/tools/calligraphic/tremor", 0.0,
4795 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4796 0.0, 100, 1, 10.0,
4797 labels, values, G_N_ELEMENTS(labels),
4798 sp_ddc_tremor_value_changed, 0.01, 0, 100 );
4800 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4801 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4802 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4803 }
4805 {
4806 /* Wiggle */
4807 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4808 gdouble values[] = {0, 20, 40, 60, 100};
4809 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4810 _("Pen Wiggle"), _("Wiggle:"),
4811 _("Increase to make the pen waver and wiggle"),
4812 "/tools/calligraphic/wiggle", 0.0,
4813 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4814 0.0, 100, 1, 10.0,
4815 labels, values, G_N_ELEMENTS(labels),
4816 sp_ddc_wiggle_value_changed, 0.01, 0, 100 );
4817 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4818 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4819 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4820 }
4822 {
4823 /* Mass */
4824 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4825 gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4826 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4827 _("Pen Mass"), _("Mass:"),
4828 _("Increase to make the pen drag behind, as if slowed by inertia"),
4829 "/tools/calligraphic/mass", 2.0,
4830 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4831 0.0, 100, 1, 10.0,
4832 labels, values, G_N_ELEMENTS(labels),
4833 sp_ddc_mass_value_changed, 0.01, 0, 100 );
4834 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4835 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4836 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4837 }
4840 /* Trace Background button */
4841 {
4842 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4843 _("Trace Background"),
4844 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4845 INKSCAPE_ICON_DRAW_TRACE_BACKGROUND,
4846 Inkscape::ICON_SIZE_DECORATION );
4847 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4848 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4849 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/tracebackground", false) );
4850 g_object_set_data( holder, "tracebackground", act );
4851 }
4853 /* Use Pressure button */
4854 {
4855 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4856 _("Pressure"),
4857 _("Use the pressure of the input device to alter the width of the pen"),
4858 INKSCAPE_ICON_DRAW_USE_PRESSURE,
4859 Inkscape::ICON_SIZE_DECORATION );
4860 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4861 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4862 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usepressure", true) );
4863 g_object_set_data( holder, "usepressure", act );
4864 }
4866 /* Use Tilt button */
4867 {
4868 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4869 _("Tilt"),
4870 _("Use the tilt of the input device to alter the angle of the pen's nib"),
4871 INKSCAPE_ICON_DRAW_USE_TILT,
4872 Inkscape::ICON_SIZE_DECORATION );
4873 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4874 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4875 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs->getBool("/tools/calligraphic/usetilt", true) );
4876 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usetilt", true) );
4877 g_object_set_data( holder, "usetilt", act );
4878 }
4880 /*calligraphic profile */
4881 {
4882 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
4883 EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
4884 ege_select_one_action_set_appearance (act1, "compact");
4885 g_object_set_data (holder, "profile_selector", act1 );
4887 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
4889 sp_dcc_build_presets_list (holder);
4891 g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
4892 gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
4893 }
4894 }
4895 }
4898 //########################
4899 //## Circle / Arc ##
4900 //########################
4902 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4903 {
4904 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4905 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4907 if (v1 == 0 && v2 == 0) {
4908 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4909 gtk_action_set_sensitive( ocb, FALSE );
4910 gtk_action_set_sensitive( make_whole, FALSE );
4911 }
4912 } else {
4913 gtk_action_set_sensitive( ocb, TRUE );
4914 gtk_action_set_sensitive( make_whole, TRUE );
4915 }
4916 }
4918 static void
4919 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4920 {
4921 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4923 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4924 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4925 prefs->setDouble(Glib::ustring("/tools/shapes/arc") + value_name, (adj->value * M_PI)/ 180);
4926 }
4928 // quit if run by the attr_changed listener
4929 if (g_object_get_data( tbl, "freeze" )) {
4930 return;
4931 }
4933 // in turn, prevent listener from responding
4934 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4936 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4938 bool modmade = false;
4939 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4940 items != NULL;
4941 items = items->next)
4942 {
4943 SPItem *item = SP_ITEM(items->data);
4945 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4947 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4948 SPArc *arc = SP_ARC(item);
4950 if (!strcmp(value_name, "start"))
4951 ge->start = (adj->value * M_PI)/ 180;
4952 else
4953 ge->end = (adj->value * M_PI)/ 180;
4955 sp_genericellipse_normalize(ge);
4956 ((SPObject *)arc)->updateRepr();
4957 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4959 modmade = true;
4960 }
4961 }
4963 g_free(namespaced_name);
4965 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4967 sp_arctb_sensitivize( tbl, adj->value, other->value );
4969 if (modmade) {
4970 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4971 _("Arc: Change start/end"));
4972 }
4974 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4975 }
4978 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
4979 {
4980 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
4981 }
4983 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4984 {
4985 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
4986 }
4989 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4990 {
4991 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4992 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4993 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4994 prefs->setBool("/tools/shapes/arc/open", ege_select_one_action_get_active(act) != 0);
4995 }
4997 // quit if run by the attr_changed listener
4998 if (g_object_get_data( tbl, "freeze" )) {
4999 return;
5000 }
5002 // in turn, prevent listener from responding
5003 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5005 bool modmade = false;
5007 if ( ege_select_one_action_get_active(act) != 0 ) {
5008 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5009 items != NULL;
5010 items = items->next)
5011 {
5012 if (SP_IS_ARC((SPItem *) items->data)) {
5013 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5014 repr->setAttribute("sodipodi:open", "true");
5015 SP_OBJECT((SPItem *) items->data)->updateRepr();
5016 modmade = true;
5017 }
5018 }
5019 } else {
5020 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5021 items != NULL;
5022 items = items->next)
5023 {
5024 if (SP_IS_ARC((SPItem *) items->data)) {
5025 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5026 repr->setAttribute("sodipodi:open", NULL);
5027 SP_OBJECT((SPItem *) items->data)->updateRepr();
5028 modmade = true;
5029 }
5030 }
5031 }
5033 if (modmade) {
5034 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
5035 _("Arc: Change open/closed"));
5036 }
5038 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5039 }
5041 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
5042 {
5043 GtkAdjustment *adj;
5044 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
5045 gtk_adjustment_set_value(adj, 0.0);
5046 gtk_adjustment_value_changed(adj);
5048 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
5049 gtk_adjustment_set_value(adj, 0.0);
5050 gtk_adjustment_value_changed(adj);
5052 spinbutton_defocus( GTK_OBJECT(obj) );
5053 }
5055 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
5056 gchar const */*old_value*/, gchar const */*new_value*/,
5057 bool /*is_interactive*/, gpointer data)
5058 {
5059 GObject *tbl = G_OBJECT(data);
5061 // quit if run by the _changed callbacks
5062 if (g_object_get_data( tbl, "freeze" )) {
5063 return;
5064 }
5066 // in turn, prevent callbacks from responding
5067 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5069 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
5070 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
5072 GtkAdjustment *adj1,*adj2;
5073 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
5074 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
5075 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
5076 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
5078 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
5080 char const *openstr = NULL;
5081 openstr = repr->attribute("sodipodi:open");
5082 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
5084 if (openstr) {
5085 ege_select_one_action_set_active( ocb, 1 );
5086 } else {
5087 ege_select_one_action_set_active( ocb, 0 );
5088 }
5090 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5091 }
5093 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
5094 NULL, /* child_added */
5095 NULL, /* child_removed */
5096 arc_tb_event_attr_changed,
5097 NULL, /* content_changed */
5098 NULL /* order_changed */
5099 };
5102 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
5103 {
5104 int n_selected = 0;
5105 Inkscape::XML::Node *repr = NULL;
5107 purge_repr_listener( tbl, tbl );
5109 for (GSList const *items = selection->itemList();
5110 items != NULL;
5111 items = items->next)
5112 {
5113 if (SP_IS_ARC((SPItem *) items->data)) {
5114 n_selected++;
5115 repr = SP_OBJECT_REPR((SPItem *) items->data);
5116 }
5117 }
5119 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
5121 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
5122 if (n_selected == 0) {
5123 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
5124 } else if (n_selected == 1) {
5125 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
5126 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5128 if (repr) {
5129 g_object_set_data( tbl, "repr", repr );
5130 Inkscape::GC::anchor(repr);
5131 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
5132 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
5133 }
5134 } else {
5135 // FIXME: implement averaging of all parameters for multiple selected
5136 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
5137 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5138 sp_arctb_sensitivize( tbl, 1, 0 );
5139 }
5140 }
5143 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5144 {
5145 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5147 EgeAdjustmentAction* eact = 0;
5148 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
5151 {
5152 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
5153 ege_output_action_set_use_markup( act, TRUE );
5154 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5155 g_object_set_data( holder, "mode_action", act );
5156 }
5158 /* Start */
5159 {
5160 eact = create_adjustment_action( "ArcStartAction",
5161 _("Start"), _("Start:"),
5162 _("The angle (in degrees) from the horizontal to the arc's start point"),
5163 "/tools/shapes/arc/start", 0.0,
5164 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
5165 -360.0, 360.0, 1.0, 10.0,
5166 0, 0, 0,
5167 sp_arctb_start_value_changed);
5168 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5169 }
5171 /* End */
5172 {
5173 eact = create_adjustment_action( "ArcEndAction",
5174 _("End"), _("End:"),
5175 _("The angle (in degrees) from the horizontal to the arc's end point"),
5176 "/tools/shapes/arc/end", 0.0,
5177 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
5178 -360.0, 360.0, 1.0, 10.0,
5179 0, 0, 0,
5180 sp_arctb_end_value_changed);
5181 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5182 }
5184 /* Segments / Pie checkbox */
5185 {
5186 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5188 GtkTreeIter iter;
5189 gtk_list_store_append( model, &iter );
5190 gtk_list_store_set( model, &iter,
5191 0, _("Closed arc"),
5192 1, _("Switch to segment (closed shape with two radii)"),
5193 2, INKSCAPE_ICON_DRAW_ELLIPSE_SEGMENT,
5194 -1 );
5196 gtk_list_store_append( model, &iter );
5197 gtk_list_store_set( model, &iter,
5198 0, _("Open Arc"),
5199 1, _("Switch to arc (unclosed shape)"),
5200 2, INKSCAPE_ICON_DRAW_ELLIPSE_ARC,
5201 -1 );
5203 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5204 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5205 g_object_set_data( holder, "open_action", act );
5207 ege_select_one_action_set_appearance( act, "full" );
5208 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5209 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5210 ege_select_one_action_set_icon_column( act, 2 );
5211 ege_select_one_action_set_icon_size( act, secondarySize );
5212 ege_select_one_action_set_tooltip_column( act, 1 );
5214 bool isClosed = !prefs->getBool("/tools/shapes/arc/open", false);
5215 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
5216 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
5217 }
5219 /* Make Whole */
5220 {
5221 InkAction* inky = ink_action_new( "ArcResetAction",
5222 _("Make whole"),
5223 _("Make the shape a whole ellipse, not arc or segment"),
5224 INKSCAPE_ICON_DRAW_ELLIPSE_WHOLE,
5225 secondarySize );
5226 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
5227 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5228 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
5229 g_object_set_data( holder, "make_whole", inky );
5230 }
5232 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
5233 // sensitivize make whole and open checkbox
5234 {
5235 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
5236 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
5237 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
5238 }
5241 sigc::connection *connection = new sigc::connection(
5242 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
5243 );
5244 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
5245 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
5246 }
5251 // toggle button callbacks and updaters
5253 //########################
5254 //## Dropper ##
5255 //########################
5257 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
5258 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5259 prefs->setInt( "/tools/dropper/pick", gtk_toggle_action_get_active( act ) );
5260 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
5261 if ( set_action ) {
5262 if ( gtk_toggle_action_get_active( act ) ) {
5263 gtk_action_set_sensitive( set_action, TRUE );
5264 } else {
5265 gtk_action_set_sensitive( set_action, FALSE );
5266 }
5267 }
5269 spinbutton_defocus(GTK_OBJECT(tbl));
5270 }
5272 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
5273 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5274 prefs->setBool( "/tools/dropper/setalpha", gtk_toggle_action_get_active( act ) );
5275 spinbutton_defocus(GTK_OBJECT(tbl));
5276 }
5279 /**
5280 * Dropper auxiliary toolbar construction and setup.
5281 *
5282 * TODO: Would like to add swatch of current color.
5283 * TODO: Add queue of last 5 or so colors selected with new swatches so that
5284 * can drag and drop places. Will provide a nice mixing palette.
5285 */
5286 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
5287 {
5288 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5289 gint pickAlpha = prefs->getInt( "/tools/dropper/pick", 1 );
5291 {
5292 EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
5293 ege_output_action_set_use_markup( act, TRUE );
5294 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5295 }
5297 {
5298 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
5299 _("Pick opacity"),
5300 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
5301 NULL,
5302 Inkscape::ICON_SIZE_DECORATION );
5303 g_object_set( act, "short_label", _("Pick"), NULL );
5304 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5305 g_object_set_data( holder, "pick_action", act );
5306 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
5307 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
5308 }
5310 {
5311 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
5312 _("Assign opacity"),
5313 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
5314 NULL,
5315 Inkscape::ICON_SIZE_DECORATION );
5316 g_object_set( act, "short_label", _("Assign"), NULL );
5317 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5318 g_object_set_data( holder, "set_action", act );
5319 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/dropper/setalpha", true) );
5320 // make sure it's disabled if we're not picking alpha
5321 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
5322 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
5323 }
5324 }
5327 //########################
5328 //## LPETool ##
5329 //########################
5331 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
5333 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
5334 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
5335 {
5336 using namespace Inkscape::LivePathEffect;
5338 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
5339 SPEventContext *ec = desktop->event_context;
5340 if (!SP_IS_LPETOOL_CONTEXT(ec)) {
5341 return;
5342 }
5344 // only take action if run by the attr_changed listener
5345 if (!g_object_get_data(tbl, "freeze")) {
5346 // in turn, prevent listener from responding
5347 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5349 gint mode = ege_select_one_action_get_active(act);
5350 EffectType type = lpesubtools[mode];
5352 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5353 bool success = lpetool_try_construction(lc, type);
5354 if (success) {
5355 // since the construction was already performed, we set the state back to inactive
5356 ege_select_one_action_set_active(act, 0);
5357 mode = 0;
5358 } else {
5359 // switch to the chosen subtool
5360 SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
5361 }
5363 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5364 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5365 prefs->setInt( "/tools/lpetool/mode", mode );
5366 }
5368 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
5369 }
5370 }
5372 void sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject */*tbl*/)
5373 {
5374 SPEventContext *ec = selection->desktop()->event_context;
5375 if (!SP_IS_LPETOOL_CONTEXT(ec))
5376 return;
5378 lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
5379 }
5381 void
5382 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
5383 {
5384 using namespace Inkscape::LivePathEffect;
5385 SPEventContext *ec = selection->desktop()->event_context;
5386 if (!SP_IS_LPETOOL_CONTEXT(ec))
5387 return;
5388 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
5390 lpetool_delete_measuring_items(lc);
5391 lpetool_create_measuring_items(lc, selection);
5393 // activate line segment combo box if a single item with LPELineSegment is selected
5394 GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
5395 SPItem *item = selection->singleItem();
5396 if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
5397 SPLPEItem *lpeitem = SP_LPE_ITEM(item);
5398 Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
5399 if (lpe && lpe->effectType() == LINE_SEGMENT) {
5400 LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
5401 g_object_set_data(tbl, "currentlpe", lpe);
5402 g_object_set_data(tbl, "currentlpeitem", lpeitem);
5403 gtk_action_set_sensitive(w, TRUE);
5404 ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
5405 } else {
5406 g_object_set_data(tbl, "currentlpe", NULL);
5407 g_object_set_data(tbl, "currentlpeitem", NULL);
5408 gtk_action_set_sensitive(w, FALSE);
5409 }
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 }
5417 static void
5418 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
5419 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5420 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5422 bool show = gtk_toggle_action_get_active( act );
5423 prefs->setBool("/tools/lpetool/show_bbox", show);
5425 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5426 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5427 lpetool_context_reset_limiting_bbox(lc);
5428 }
5429 }
5431 static void
5432 lpetool_toggle_show_measuring_info (GtkToggleAction *act, GObject *tbl) {
5433 SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
5434 if (!tools_isactive(desktop, TOOLS_LPETOOL))
5435 return;
5437 GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
5438 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5439 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5440 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5441 bool show = gtk_toggle_action_get_active( act );
5442 prefs->setBool("/tools/lpetool/show_measuring_info", show);
5443 lpetool_show_measuring_info(lc, show);
5444 gtk_action_set_sensitive(GTK_ACTION(unitact), show);
5445 }
5446 }
5448 static void lpetool_unit_changed(GtkAction* /*act*/, GObject* tbl) {
5449 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
5450 SPUnit const *unit = tracker->getActiveUnit();
5451 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5452 prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5454 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5455 if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
5456 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5457 lpetool_delete_measuring_items(lc);
5458 lpetool_create_measuring_items(lc);
5459 }
5460 }
5462 static void
5463 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
5464 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5465 Inkscape::Selection *selection = desktop->selection;
5467 Geom::OptRect bbox = selection->bounds();
5469 if (bbox) {
5470 Geom::Point A(bbox->min());
5471 Geom::Point B(bbox->max());
5473 A *= desktop->doc2dt();
5474 B *= desktop->doc2dt();
5476 // TODO: should we provide a way to store points in prefs?
5477 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5478 prefs->setDouble("/tools/lpetool/bbox_upperleftx", A[Geom::X]);
5479 prefs->setDouble("/tools/lpetool/bbox_upperlefty", A[Geom::Y]);
5480 prefs->setDouble("/tools/lpetool/bbox_lowerrightx", B[Geom::X]);
5481 prefs->setDouble("/tools/lpetool/bbox_lowerrighty", B[Geom::Y]);
5483 lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
5484 }
5486 gtk_toggle_action_set_active(act, false);
5487 }
5489 static void
5490 sp_line_segment_build_list(GObject *tbl)
5491 {
5492 g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
5494 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
5495 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
5496 gtk_list_store_clear (model);
5498 // TODO: we add the entries of rht combo box manually; later this should be done automatically
5499 {
5500 GtkTreeIter iter;
5501 gtk_list_store_append( model, &iter );
5502 gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
5503 gtk_list_store_append( model, &iter );
5504 gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
5505 gtk_list_store_append( model, &iter );
5506 gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
5507 gtk_list_store_append( model, &iter );
5508 gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
5509 }
5511 g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5512 }
5514 static void
5515 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
5516 using namespace Inkscape::LivePathEffect;
5518 // quit if run by the attr_changed listener
5519 if (g_object_get_data(tbl, "freeze")) {
5520 return;
5521 }
5523 // in turn, prevent listener from responding
5524 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5526 LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
5527 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5528 if (lpeitem) {
5529 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5530 lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
5531 sp_lpe_item_update_patheffect(lpeitem, true, true);
5532 }
5534 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5535 }
5537 static void
5538 lpetool_open_lpe_dialog (GtkToggleAction *act, gpointer data) {
5539 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5541 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5542 sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5543 }
5544 gtk_toggle_action_set_active(act, false);
5545 }
5547 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5548 {
5549 UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5550 tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5551 g_object_set_data(holder, "tracker", tracker);
5552 SPUnit const *unit = tracker->getActiveUnit();
5554 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5555 prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5557 /** Automatically create a list of LPEs that get added to the toolbar **/
5558 {
5559 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5561 GtkTreeIter iter;
5563 // the first toggle button represents the state that no subtool is active (remove this when
5564 // this can be modeled by EgeSelectOneAction or some other action)
5565 gtk_list_store_append( model, &iter );
5566 gtk_list_store_set( model, &iter,
5567 0, _("All inactive"),
5568 1, _("No geometric tool is active"),
5569 2, _("all_inactive"),
5570 -1 );
5572 Inkscape::LivePathEffect::EffectType type;
5573 for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5574 type = lpesubtools[i];
5575 gtk_list_store_append( model, &iter );
5576 gtk_list_store_set( model, &iter,
5577 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5578 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5579 2, Inkscape::LivePathEffect::LPETypeConverter.get_key(type).c_str(),
5580 -1 );
5581 }
5583 EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5584 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5585 g_object_set_data( holder, "lpetool_mode_action", act );
5587 ege_select_one_action_set_appearance( act, "full" );
5588 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5589 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5590 ege_select_one_action_set_icon_column( act, 2 );
5591 ege_select_one_action_set_tooltip_column( act, 1 );
5593 gint lpeToolMode = prefs->getInt("/tools/lpetool/mode", 0);
5594 ege_select_one_action_set_active( act, lpeToolMode );
5595 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5596 }
5598 /* Show limiting bounding box */
5599 {
5600 InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5601 _("Show limiting bounding box"),
5602 _("Show bounding box (used to cut infinite lines)"),
5603 "lpetool_show_bbox",
5604 Inkscape::ICON_SIZE_DECORATION );
5605 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5606 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5607 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_bbox", true ) );
5608 }
5610 /* Set limiting bounding box to bbox of current selection */
5611 {
5612 InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5613 _("Get limiting bounding box from selection"),
5614 _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5615 "lpetool_set_bbox",
5616 Inkscape::ICON_SIZE_DECORATION );
5617 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5618 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5619 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5620 }
5623 /* Combo box to choose line segment type */
5624 {
5625 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5626 EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5627 ege_select_one_action_set_appearance (act, "compact");
5628 g_object_set_data (holder, "lpetool_line_segment_action", act );
5630 g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5632 sp_line_segment_build_list (holder);
5634 g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5635 gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5636 gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5637 }
5639 /* Display measuring info for selected items */
5640 {
5641 InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5642 _("Display measuring info"),
5643 _("Display measuring info for selected items"),
5644 "lpetool_measuring_info",
5645 Inkscape::ICON_SIZE_DECORATION );
5646 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5647 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
5648 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_measuring_info", true ) );
5649 }
5651 // add the units menu
5652 {
5653 GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5654 gtk_action_group_add_action( mainActions, act );
5655 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5656 g_object_set_data(holder, "lpetool_units_action", act);
5657 gtk_action_set_sensitive(act, prefs->getBool("/tools/lpetool/show_measuring_info", true));
5658 }
5660 /* Open LPE dialog (to adapt parameters numerically) */
5661 {
5662 InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
5663 _("Open LPE dialog"),
5664 _("Open LPE dialog (to adapt parameters numerically)"),
5665 "lpetool_open_lpe_dialog",
5666 Inkscape::ICON_SIZE_DECORATION );
5667 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5668 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
5669 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5670 }
5672 //watch selection
5673 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5675 sigc::connection *c_selection_modified =
5676 new sigc::connection (sp_desktop_selection (desktop)->connectModified
5677 (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
5678 pool->add_connection ("selection-modified", c_selection_modified);
5680 sigc::connection *c_selection_changed =
5681 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5682 (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5683 pool->add_connection ("selection-changed", c_selection_changed);
5684 }
5686 //########################
5687 //## Eraser ##
5688 //########################
5690 static void sp_erc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
5691 {
5692 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5693 prefs->setDouble( "/tools/eraser/width", adj->value * 0.01 );
5694 update_presets_list(tbl);
5695 }
5697 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5698 {
5699 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5700 bool eraserMode = ege_select_one_action_get_active( act ) != 0;
5701 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5702 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5703 prefs->setBool( "/tools/eraser/mode", eraserMode );
5704 }
5706 // only take action if run by the attr_changed listener
5707 if (!g_object_get_data( tbl, "freeze" )) {
5708 // in turn, prevent listener from responding
5709 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5711 if ( eraserMode != 0 ) {
5712 } else {
5713 }
5714 // TODO finish implementation
5716 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5717 }
5718 }
5720 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5721 {
5722 {
5723 /* Width */
5724 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5725 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5726 EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5727 _("Pen Width"), _("Width:"),
5728 _("The width of the eraser pen (relative to the visible canvas area)"),
5729 "/tools/eraser/width", 15,
5730 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5731 1, 100, 1.0, 10.0,
5732 labels, values, G_N_ELEMENTS(labels),
5733 sp_erc_width_value_changed, 0.01, 0, 100 );
5734 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5735 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5736 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5737 }
5739 {
5740 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5742 GtkTreeIter iter;
5743 gtk_list_store_append( model, &iter );
5744 gtk_list_store_set( model, &iter,
5745 0, _("Delete"),
5746 1, _("Delete objects touched by the eraser"),
5747 2, INKSCAPE_ICON_DRAW_ERASER_DELETE_OBJECTS,
5748 -1 );
5750 gtk_list_store_append( model, &iter );
5751 gtk_list_store_set( model, &iter,
5752 0, _("Cut"),
5753 1, _("Cut out from objects"),
5754 2, INKSCAPE_ICON_PATH_DIFFERENCE,
5755 -1 );
5757 EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5758 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5759 g_object_set_data( holder, "eraser_mode_action", act );
5761 ege_select_one_action_set_appearance( act, "full" );
5762 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5763 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5764 ege_select_one_action_set_icon_column( act, 2 );
5765 ege_select_one_action_set_tooltip_column( act, 1 );
5767 /// @todo Convert to boolean?
5768 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5769 gint eraserMode = prefs->getBool("/tools/eraser/mode") ? 1 : 0;
5770 ege_select_one_action_set_active( act, eraserMode );
5771 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
5772 }
5774 }
5776 //########################
5777 //## Text Toolbox ##
5778 //########################
5779 /*
5780 static void
5781 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
5782 {
5783 //Call back for letter sizing spinbutton
5784 }
5786 static void
5787 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
5788 {
5789 //Call back for line height spinbutton
5790 }
5792 static void
5793 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5794 {
5795 //Call back for horizontal kerning spinbutton
5796 }
5798 static void
5799 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5800 {
5801 //Call back for vertical kerning spinbutton
5802 }
5804 static void
5805 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
5806 {
5807 //Call back for letter rotation spinbutton
5808 }*/
5810 namespace {
5812 bool popdown_visible = false;
5813 bool popdown_hasfocus = false;
5815 void
5816 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
5817 {
5818 SPStyle *query =
5819 sp_style_new (SP_ACTIVE_DOCUMENT);
5821 // int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5823 int result_family =
5824 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5826 int result_style =
5827 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5829 int result_numbers =
5830 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5832 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5834 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5835 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
5836 // there are no texts in selection, read from prefs
5838 sp_style_read_from_prefs(query, "/tools/text");
5840 if (g_object_get_data(tbl, "text_style_from_prefs")) {
5841 // do not reset the toolbar style from prefs if we already did it last time
5842 sp_style_unref(query);
5843 return;
5844 }
5845 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
5846 } else {
5847 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
5848 }
5850 if (query->text)
5851 {
5852 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
5853 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5854 gtk_entry_set_text (GTK_ENTRY (entry), "");
5856 } else if (query->text->font_specification.value || query->text->font_family.value) {
5858 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5860 // Get the font that corresponds
5861 Glib::ustring familyName;
5863 font_instance * font = font_factory::Default()->FaceFromStyle(query);
5864 if (font) {
5865 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
5866 font->Unref();
5867 font = NULL;
5868 }
5870 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
5872 Gtk::TreePath path;
5873 try {
5874 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
5875 } catch (...) {
5876 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
5877 sp_style_unref(query);
5878 return;
5879 }
5881 GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5882 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5884 g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
5886 gtk_tree_selection_select_path (tselection, path.gobj());
5887 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5889 g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
5890 }
5892 //Size
5893 {
5894 GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
5895 gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
5896 g_object_set_data(tbl, "size-block", gpointer(1));
5897 gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
5898 g_object_set_data(tbl, "size-block", gpointer(0));
5899 g_free(str);
5900 }
5902 //Anchor
5903 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
5904 {
5905 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
5906 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5907 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5908 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5909 }
5910 else
5911 {
5912 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
5913 {
5914 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
5915 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5916 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5917 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5918 }
5919 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
5920 {
5921 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
5922 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5923 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5924 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5925 }
5926 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
5927 {
5928 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
5929 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5930 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5931 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5932 }
5933 }
5935 //Style
5936 {
5937 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
5939 gboolean active = gtk_toggle_button_get_active (button);
5940 gboolean check = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
5942 if (active != check)
5943 {
5944 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5945 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5946 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5947 }
5948 }
5950 {
5951 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
5953 gboolean active = gtk_toggle_button_get_active (button);
5954 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
5956 if (active != check)
5957 {
5958 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5959 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5960 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5961 }
5962 }
5964 //Orientation
5965 //locking both buttons, changing one affect all group (both)
5966 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
5967 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5969 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
5970 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
5972 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
5973 {
5974 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5975 }
5976 else
5977 {
5978 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
5979 }
5980 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5981 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
5982 }
5984 sp_style_unref(query);
5985 }
5987 void
5988 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
5989 {
5990 sp_text_toolbox_selection_changed (selection, tbl);
5991 }
5993 void
5994 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
5995 {
5996 sp_text_toolbox_selection_changed (NULL, tbl);
5997 }
5999 void
6000 sp_text_toolbox_family_changed (GtkTreeSelection *selection,
6001 GObject *tbl)
6002 {
6003 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6004 GtkTreeModel *model = 0;
6005 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6006 GtkTreeIter iter;
6007 char *family = 0;
6009 gdk_pointer_ungrab (GDK_CURRENT_TIME);
6010 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
6012 if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
6013 return;
6014 }
6016 gtk_tree_model_get (model, &iter, 0, &family, -1);
6018 if (g_object_get_data (G_OBJECT (selection), "block"))
6019 {
6020 gtk_entry_set_text (GTK_ENTRY (entry), family);
6021 return;
6022 }
6024 gtk_entry_set_text (GTK_ENTRY (entry), family);
6026 SPStyle *query =
6027 sp_style_new (SP_ACTIVE_DOCUMENT);
6029 int result_fontspec =
6030 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6032 //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6034 SPCSSAttr *css = sp_repr_css_attr_new ();
6037 // First try to get the font spec from the stored value
6038 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
6040 if (fontSpec.empty()) {
6041 // Construct a new font specification if it does not yet exist
6042 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6043 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6044 fontFromStyle->Unref();
6045 }
6047 if (!fontSpec.empty()) {
6048 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
6049 if (!newFontSpec.empty() && fontSpec != newFontSpec) {
6050 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
6051 if (font) {
6052 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6054 // Set all the these just in case they were altered when finding the best
6055 // match for the new family and old style...
6057 gchar c[256];
6059 font->Family(c, 256);
6060 sp_repr_css_set_property (css, "font-family", c);
6062 font->Attribute( "weight", c, 256);
6063 sp_repr_css_set_property (css, "font-weight", c);
6065 font->Attribute("style", c, 256);
6066 sp_repr_css_set_property (css, "font-style", c);
6068 font->Attribute("stretch", c, 256);
6069 sp_repr_css_set_property (css, "font-stretch", c);
6071 font->Attribute("variant", c, 256);
6072 sp_repr_css_set_property (css, "font-variant", c);
6074 font->Unref();
6075 }
6076 }
6077 }
6079 // If querying returned nothing, set the default style of the tool (for new texts)
6080 if (result_fontspec == QUERY_STYLE_NOTHING)
6081 {
6082 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6083 prefs->setStyle("/tools/text/style", css);
6084 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
6085 }
6086 else
6087 {
6088 sp_desktop_set_style (desktop, css, true, true);
6089 }
6091 sp_style_unref(query);
6093 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6094 _("Text: Change font family"));
6095 sp_repr_css_attr_unref (css);
6096 g_free(family);
6097 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6099 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6100 }
6102 /* This is where execution comes when the contents of the font family box have been completed
6103 by the press of the return key */
6104 void
6105 sp_text_toolbox_family_entry_activate (GtkEntry *entry,
6106 GObject *tbl)
6107 {
6108 const char *family = gtk_entry_get_text (entry); // Fetch the requested font family
6110 // Try to match that to a known font. If not, then leave current font alone and remain focused on text box
6111 try {
6112 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
6113 GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
6114 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
6115 gtk_tree_selection_select_path (selection, path.gobj());
6116 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
6117 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6118 } catch (...) {
6119 if (family && strlen (family))
6120 {
6121 gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6122 }
6123 }
6124 }
6126 void
6127 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
6128 gpointer data)
6129 {
6130 if (g_object_get_data (G_OBJECT (button), "block")) return;
6131 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
6132 int prop = GPOINTER_TO_INT(data);
6134 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6135 SPCSSAttr *css = sp_repr_css_attr_new ();
6137 switch (prop)
6138 {
6139 case 0:
6140 {
6141 sp_repr_css_set_property (css, "text-anchor", "start");
6142 sp_repr_css_set_property (css, "text-align", "start");
6143 break;
6144 }
6145 case 1:
6146 {
6147 sp_repr_css_set_property (css, "text-anchor", "middle");
6148 sp_repr_css_set_property (css, "text-align", "center");
6149 break;
6150 }
6152 case 2:
6153 {
6154 sp_repr_css_set_property (css, "text-anchor", "end");
6155 sp_repr_css_set_property (css, "text-align", "end");
6156 break;
6157 }
6159 case 3:
6160 {
6161 sp_repr_css_set_property (css, "text-anchor", "start");
6162 sp_repr_css_set_property (css, "text-align", "justify");
6163 break;
6164 }
6165 }
6167 SPStyle *query =
6168 sp_style_new (SP_ACTIVE_DOCUMENT);
6169 int result_numbers =
6170 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6172 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6173 if (result_numbers == QUERY_STYLE_NOTHING)
6174 {
6175 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6176 prefs->setStyle("/tools/text/style", css);
6177 }
6179 sp_style_unref(query);
6181 sp_desktop_set_style (desktop, css, true, true);
6182 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6183 _("Text: Change alignment"));
6184 sp_repr_css_attr_unref (css);
6186 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6187 }
6189 void
6190 sp_text_toolbox_style_toggled (GtkToggleButton *button,
6191 gpointer data)
6192 {
6193 if (g_object_get_data (G_OBJECT (button), "block")) return;
6195 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6196 SPCSSAttr *css = sp_repr_css_attr_new ();
6197 int prop = GPOINTER_TO_INT(data);
6198 bool active = gtk_toggle_button_get_active (button);
6200 SPStyle *query =
6201 sp_style_new (SP_ACTIVE_DOCUMENT);
6203 int result_fontspec =
6204 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6206 //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6207 //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6208 //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6210 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
6211 Glib::ustring newFontSpec = "";
6213 if (fontSpec.empty()) {
6214 // Construct a new font specification if it does not yet exist
6215 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6216 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6217 fontFromStyle->Unref();
6218 }
6220 switch (prop)
6221 {
6222 case 0:
6223 {
6224 if (!fontSpec.empty()) {
6225 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
6226 }
6227 if (fontSpec != newFontSpec) {
6228 // Don't even set the bold if the font didn't exist on the system
6229 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
6230 }
6231 break;
6232 }
6234 case 1:
6235 {
6236 if (!fontSpec.empty()) {
6237 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
6238 }
6239 if (fontSpec != newFontSpec) {
6240 // Don't even set the italic if the font didn't exist on the system
6241 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
6242 }
6243 break;
6244 }
6245 }
6247 if (!newFontSpec.empty()) {
6248 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6249 }
6251 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6252 if (result_fontspec == QUERY_STYLE_NOTHING)
6253 {
6254 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6255 prefs->setStyle("/tools/text/style", css);
6256 }
6258 sp_style_unref(query);
6260 sp_desktop_set_style (desktop, css, true, true);
6261 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6262 _("Text: Change font style"));
6263 sp_repr_css_attr_unref (css);
6265 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6266 }
6268 void
6269 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
6270 gpointer data)
6271 {
6272 if (g_object_get_data (G_OBJECT (button), "block")) {
6273 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6274 return;
6275 }
6277 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6278 SPCSSAttr *css = sp_repr_css_attr_new ();
6279 int prop = GPOINTER_TO_INT(data);
6281 switch (prop)
6282 {
6283 case 0:
6284 {
6285 sp_repr_css_set_property (css, "writing-mode", "lr");
6286 break;
6287 }
6289 case 1:
6290 {
6291 sp_repr_css_set_property (css, "writing-mode", "tb");
6292 break;
6293 }
6294 }
6296 SPStyle *query =
6297 sp_style_new (SP_ACTIVE_DOCUMENT);
6298 int result_numbers =
6299 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6301 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6302 if (result_numbers == QUERY_STYLE_NOTHING)
6303 {
6304 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6305 prefs->setStyle("/tools/text/style", css);
6306 }
6308 sp_desktop_set_style (desktop, css, true, true);
6309 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6310 _("Text: Change orientation"));
6311 sp_repr_css_attr_unref (css);
6313 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6314 }
6316 gboolean
6317 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6318 {
6319 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6320 if (!desktop) return FALSE;
6322 switch (get_group0_keyval (event)) {
6323 case GDK_Escape: // defocus
6324 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6325 sp_text_toolbox_selection_changed (NULL, tbl); // update
6326 return TRUE; // I consumed the event
6327 break;
6328 }
6329 return FALSE;
6330 }
6332 gboolean
6333 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
6334 {
6335 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6336 if (!desktop) return FALSE;
6338 switch (get_group0_keyval (event)) {
6339 case GDK_KP_Enter:
6340 case GDK_Return:
6341 case GDK_Escape: // defocus
6342 gtk_widget_hide (w);
6343 popdown_visible = false;
6344 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6345 return TRUE; // I consumed the event
6346 break;
6347 case GDK_w:
6348 case GDK_W:
6349 if (event->state & GDK_CONTROL_MASK) {
6350 gtk_widget_hide (w);
6351 popdown_visible = false;
6352 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6353 return TRUE; // I consumed the event
6354 }
6355 break;
6356 }
6357 return FALSE;
6358 }
6361 void
6362 sp_text_toolbox_size_changed (GtkComboBox *cbox,
6363 GObject *tbl)
6364 {
6365 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6367 if (g_object_get_data (tbl, "size-block")) return;
6369 // If this is not from selecting a size in the list (in which case get_active will give the
6370 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
6371 // process this event. This fixes GTK's stupid insistence on sending an activate change every
6372 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
6373 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
6374 return;
6376 gdouble value = -1;
6377 {
6378 gchar *endptr;
6379 gchar *const text = gtk_combo_box_get_active_text(cbox);
6380 if (text) {
6381 value = g_strtod(text, &endptr);
6382 if (endptr == text) { // Conversion failed, non-numeric input.
6383 value = -1;
6384 }
6385 g_free(text);
6386 }
6387 }
6388 if (value <= 0) {
6389 return; // could not parse value
6390 }
6392 SPCSSAttr *css = sp_repr_css_attr_new ();
6393 Inkscape::CSSOStringStream osfs;
6394 osfs << value;
6395 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
6397 SPStyle *query =
6398 sp_style_new (SP_ACTIVE_DOCUMENT);
6399 int result_numbers =
6400 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6402 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6403 if (result_numbers == QUERY_STYLE_NOTHING)
6404 {
6405 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6406 prefs->setStyle("/tools/text/style", css);
6407 }
6409 sp_style_unref(query);
6411 sp_desktop_set_style (desktop, css, true, true);
6412 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
6413 _("Text: Change font size"));
6414 sp_repr_css_attr_unref (css);
6416 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6417 }
6419 gboolean
6420 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
6421 {
6422 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6423 if (!desktop) return FALSE;
6425 if (!g_object_get_data (tbl, "esc-pressed")) {
6426 g_object_set_data (tbl, "enter-pressed", gpointer(1));
6427 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6428 sp_text_toolbox_size_changed (cbox, tbl);
6429 g_object_set_data (tbl, "enter-pressed", gpointer(0));
6430 }
6431 return FALSE; // I consumed the event
6432 }
6435 gboolean
6436 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6437 {
6438 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6439 if (!desktop) return FALSE;
6441 switch (get_group0_keyval (event)) {
6442 case GDK_Escape: // defocus
6443 g_object_set_data (tbl, "esc-pressed", gpointer(1));
6444 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6445 g_object_set_data (tbl, "esc-pressed", gpointer(0));
6446 return TRUE; // I consumed the event
6447 break;
6448 case GDK_Return: // defocus
6449 case GDK_KP_Enter:
6450 g_object_set_data (tbl, "enter-pressed", gpointer(1));
6451 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6452 sp_text_toolbox_size_changed (cbox, tbl);
6453 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6454 g_object_set_data (tbl, "enter-pressed", gpointer(0));
6455 return TRUE; // I consumed the event
6456 break;
6457 }
6458 return FALSE;
6459 }
6461 void
6462 sp_text_toolbox_text_popdown_clicked (GtkButton */*button*/,
6463 GObject *tbl)
6464 {
6465 GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
6466 GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6467 int x, y;
6469 if (!popdown_visible)
6470 {
6471 gdk_window_get_origin (widget->window, &x, &y);
6472 gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
6473 gtk_widget_show_all (popdown);
6474 //sp_transientize (popdown);
6476 gdk_pointer_grab (widget->window, TRUE,
6477 GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
6478 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
6479 GDK_POINTER_MOTION_MASK),
6480 NULL, NULL, GDK_CURRENT_TIME);
6482 gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
6484 popdown_visible = true;
6485 }
6486 else
6487 {
6488 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6489 gdk_pointer_ungrab (GDK_CURRENT_TIME);
6490 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
6491 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6492 gtk_widget_hide (popdown);
6493 popdown_visible = false;
6494 }
6495 }
6497 gboolean
6498 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
6499 GdkEventFocus */*event*/,
6500 GObject */*tbl*/)
6501 {
6502 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
6503 return FALSE;
6504 }
6506 gboolean
6507 sp_text_toolbox_popdown_focus_out (GtkWidget *popdown,
6508 GdkEventFocus */*event*/,
6509 GObject */*tbl*/)
6510 {
6511 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6513 if (popdown_hasfocus) {
6514 gtk_widget_hide (popdown);
6515 popdown_hasfocus = false;
6516 popdown_visible = false;
6517 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6518 return TRUE;
6519 }
6520 return FALSE;
6521 }
6523 gboolean
6524 sp_text_toolbox_popdown_focus_in (GtkWidget */*popdown*/,
6525 GdkEventFocus */*event*/,
6526 GObject */*tbl*/)
6527 {
6528 popdown_hasfocus = true;
6529 return TRUE;
6530 }
6533 void
6534 cell_data_func (GtkTreeViewColumn */*column*/,
6535 GtkCellRenderer *cell,
6536 GtkTreeModel *tree_model,
6537 GtkTreeIter *iter,
6538 gpointer /*data*/)
6539 {
6540 gchar *family;
6541 gtk_tree_model_get(tree_model, iter, 0, &family, -1);
6542 gchar *const family_escaped = g_markup_escape_text(family, -1);
6544 static char const *const sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
6545 gchar *const sample_escaped = g_markup_escape_text(sample, -1);
6547 std::stringstream markup;
6548 markup << family_escaped << " <span foreground='darkgray' font_family='"
6549 << family_escaped << "'>" << sample_escaped << "</span>";
6550 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6552 g_free(family);
6553 g_free(family_escaped);
6554 g_free(sample_escaped);
6555 }
6557 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
6558 GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
6559 if (completion) {
6560 gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
6561 g_object_unref (completion);
6562 }
6563 }
6565 GtkWidget*
6566 sp_text_toolbox_new (SPDesktop *desktop)
6567 {
6568 GtkToolbar *tbl = GTK_TOOLBAR(gtk_toolbar_new());
6569 GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("/toolbox/secondary", 1));
6571 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
6572 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
6574 GtkTooltips *tt = gtk_tooltips_new();
6575 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
6577 ////////////Family
6578 //Window
6579 GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
6580 gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
6582 //Entry
6583 GtkWidget *entry = gtk_entry_new ();
6584 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
6585 GtkEntryCompletion *completion = gtk_entry_completion_new ();
6586 gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
6587 gtk_entry_completion_set_text_column (completion, 0);
6588 gtk_entry_completion_set_minimum_key_length (completion, 1);
6589 g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
6590 gtk_entry_set_completion (GTK_ENTRY(entry), completion);
6591 gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
6592 gtk_toolbar_append_widget( tbl, entry, "", "" );
6593 g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
6595 //Button
6596 GtkWidget *button = gtk_button_new ();
6597 gtk_container_add (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
6598 gtk_toolbar_append_widget( tbl, button, "", "");
6600 //Popdown
6601 GtkWidget *sw = gtk_scrolled_window_new (NULL, NULL);
6602 GtkWidget *treeview = gtk_tree_view_new ();
6604 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
6605 GtkTreeViewColumn *column = gtk_tree_view_column_new ();
6606 gtk_tree_view_column_pack_start (column, cell, FALSE);
6607 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
6608 gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
6609 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
6611 gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
6612 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
6613 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
6615 //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
6617 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
6618 gtk_container_add (GTK_CONTAINER (sw), treeview);
6620 gtk_container_add (GTK_CONTAINER (window), sw);
6621 gtk_widget_set_size_request (window, 300, 450);
6623 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
6624 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
6625 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
6627 g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
6629 g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
6630 g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
6631 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
6633 GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
6634 g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6636 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
6637 g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
6638 g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
6639 g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
6640 g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
6642 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
6643 GtkWidget *box = gtk_event_box_new ();
6644 gtk_container_add (GTK_CONTAINER (box), image);
6645 gtk_toolbar_append_widget( tbl, box, "", "");
6646 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
6647 GtkTooltips *tooltips = gtk_tooltips_new ();
6648 gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
6649 gtk_widget_hide (GTK_WIDGET (box));
6650 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
6652 ////////////Size
6653 gchar const *const sizes[] = {
6654 "4", "6", "8", "9", "10", "11", "12", "13", "14",
6655 "16", "18", "20", "22", "24", "28",
6656 "32", "36", "40", "48", "56", "64", "72", "144"
6657 };
6659 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
6660 for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
6661 gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
6662 }
6663 gtk_widget_set_size_request (cbox, 80, -1);
6664 gtk_toolbar_append_widget( tbl, cbox, "", "");
6665 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
6666 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
6667 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
6668 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
6670 ////////////Text anchor
6671 GtkWidget *group = gtk_radio_button_new (NULL);
6672 GtkWidget *row = gtk_hbox_new (FALSE, 4);
6673 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
6675 // left
6676 GtkWidget *rbutton = group;
6677 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6678 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
6679 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6681 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6682 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
6683 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
6684 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
6686 // center
6687 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6688 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6689 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
6690 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6692 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6693 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
6694 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
6695 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
6697 // right
6698 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6699 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6700 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
6701 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6703 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6704 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
6705 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
6706 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
6708 // fill
6709 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6710 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6711 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
6712 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6714 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6715 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
6716 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
6717 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
6719 gtk_toolbar_append_widget( tbl, row, "", "");
6721 //spacer
6722 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6724 ////////////Text style
6725 row = gtk_hbox_new (FALSE, 4);
6727 // bold
6728 rbutton = gtk_toggle_button_new ();
6729 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6730 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
6731 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6732 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
6734 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6735 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
6736 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
6738 // italic
6739 rbutton = gtk_toggle_button_new ();
6740 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6741 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
6742 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6743 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
6745 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6746 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
6747 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
6749 gtk_toolbar_append_widget( tbl, row, "", "");
6751 //spacer
6752 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6754 // Text orientation
6755 group = gtk_radio_button_new (NULL);
6756 row = gtk_hbox_new (FALSE, 4);
6757 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
6759 // horizontal
6760 rbutton = group;
6761 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6762 gtk_container_add (GTK_CONTAINER (rbutton),
6763 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_HORIZONTAL));
6764 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6765 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
6767 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6768 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
6769 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
6771 // vertical
6772 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6773 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6774 gtk_container_add (GTK_CONTAINER (rbutton),
6775 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_VERTICAL));
6776 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6777 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
6779 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6780 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
6781 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
6782 gtk_toolbar_append_widget( tbl, row, "", "" );
6785 //watch selection
6786 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
6788 sigc::connection *c_selection_changed =
6789 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
6790 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
6791 pool->add_connection ("selection-changed", c_selection_changed);
6793 sigc::connection *c_selection_modified =
6794 new sigc::connection (sp_desktop_selection (desktop)->connectModified
6795 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
6796 pool->add_connection ("selection-modified", c_selection_modified);
6798 sigc::connection *c_subselection_changed =
6799 new sigc::connection (desktop->connectToolSubselectionChanged
6800 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
6801 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
6803 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
6806 gtk_widget_show_all( GTK_WIDGET(tbl) );
6808 return GTK_WIDGET(tbl);
6809 } // end of sp_text_toolbox_new()
6811 }//<unnamed> namespace
6814 //#########################
6815 //## Connector ##
6816 //#########################
6818 static void sp_connector_path_set_avoid(void)
6819 {
6820 cc_selection_set_avoid(true);
6821 }
6824 static void sp_connector_path_set_ignore(void)
6825 {
6826 cc_selection_set_avoid(false);
6827 }
6831 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
6832 {
6833 // quit if run by the _changed callbacks
6834 if (g_object_get_data( tbl, "freeze" )) {
6835 return;
6836 }
6838 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
6839 SPDocument *doc = sp_desktop_document(desktop);
6841 if (!sp_document_get_undo_sensitive(doc))
6842 {
6843 return;
6844 }
6846 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6848 if ( repr->attribute("inkscape:connector-spacing") ) {
6849 gdouble priorValue = gtk_adjustment_get_value(adj);
6850 sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
6851 if ( priorValue == gtk_adjustment_get_value(adj) ) {
6852 return;
6853 }
6854 } else if ( adj->value == defaultConnSpacing ) {
6855 return;
6856 }
6858 // in turn, prevent callbacks from responding
6859 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6861 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
6862 SP_OBJECT(desktop->namedview)->updateRepr();
6864 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
6865 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
6866 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
6867 Geom::Matrix m = Geom::identity();
6868 avoid_item_move(&m, item);
6869 }
6871 if (items) {
6872 g_slist_free(items);
6873 }
6875 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
6876 _("Change connector spacing"));
6878 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6880 spinbutton_defocus(GTK_OBJECT(tbl));
6881 }
6883 static void sp_connector_graph_layout(void)
6884 {
6885 if (!SP_ACTIVE_DESKTOP) return;
6886 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6888 // hack for clones, see comment in align-and-distribute.cpp
6889 int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
6890 prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
6892 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
6894 prefs->setInt("/options/clonecompensation/value", saved_compensation);
6896 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
6897 }
6899 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6900 {
6901 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6902 prefs->setBool("/tools/connector/directedlayout",
6903 gtk_toggle_action_get_active( act ));
6904 }
6906 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6907 {
6908 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6909 prefs->setBool("/tools/connector/avoidoverlaplayout",
6910 gtk_toggle_action_get_active( act ));
6911 }
6914 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
6915 {
6916 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6917 prefs->setDouble("/tools/connector/length", adj->value);
6918 spinbutton_defocus(GTK_OBJECT(tbl));
6919 }
6921 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
6922 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
6923 bool /*is_interactive*/, gpointer data)
6924 {
6925 GtkWidget *tbl = GTK_WIDGET(data);
6927 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6928 return;
6929 }
6930 if (strcmp(name, "inkscape:connector-spacing") != 0) {
6931 return;
6932 }
6934 GtkAdjustment *adj = (GtkAdjustment*)
6935 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
6936 gdouble spacing = defaultConnSpacing;
6937 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
6939 gtk_adjustment_set_value(adj, spacing);
6940 }
6943 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
6944 NULL, /* child_added */
6945 NULL, /* child_removed */
6946 connector_tb_event_attr_changed,
6947 NULL, /* content_changed */
6948 NULL /* order_changed */
6949 };
6952 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
6953 {
6954 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6955 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
6957 {
6958 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
6959 _("Avoid"),
6960 _("Make connectors avoid selected objects"),
6961 INKSCAPE_ICON_CONNECTOR_AVOID,
6962 secondarySize );
6963 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
6964 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6965 }
6967 {
6968 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
6969 _("Ignore"),
6970 _("Make connectors ignore selected objects"),
6971 INKSCAPE_ICON_CONNECTOR_IGNORE,
6972 secondarySize );
6973 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
6974 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6975 }
6977 EgeAdjustmentAction* eact = 0;
6979 // Spacing spinbox
6980 eact = create_adjustment_action( "ConnectorSpacingAction",
6981 _("Connector Spacing"), _("Spacing:"),
6982 _("The amount of space left around objects by auto-routing connectors"),
6983 "/tools/connector/spacing", defaultConnSpacing,
6984 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
6985 0, 100, 1.0, 10.0,
6986 0, 0, 0,
6987 connector_spacing_changed, 1, 0 );
6988 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6990 // Graph (connector network) layout
6991 {
6992 InkAction* inky = ink_action_new( "ConnectorGraphAction",
6993 _("Graph"),
6994 _("Nicely arrange selected connector network"),
6995 INKSCAPE_ICON_DISTRIBUTE_GRAPH,
6996 secondarySize );
6997 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
6998 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6999 }
7001 // Default connector length spinbox
7002 eact = create_adjustment_action( "ConnectorLengthAction",
7003 _("Connector Length"), _("Length:"),
7004 _("Ideal length for connectors when layout is applied"),
7005 "/tools/connector/length", 100,
7006 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
7007 10, 1000, 10.0, 100.0,
7008 0, 0, 0,
7009 connector_length_changed, 1, 0 );
7010 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7013 // Directed edges toggle button
7014 {
7015 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
7016 _("Downwards"),
7017 _("Make connectors with end-markers (arrows) point downwards"),
7018 INKSCAPE_ICON_DISTRIBUTE_GRAPH_DIRECTED,
7019 Inkscape::ICON_SIZE_DECORATION );
7020 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7022 bool tbuttonstate = prefs->getBool("/tools/connector/directedlayout");
7023 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7025 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
7026 }
7028 // Avoid overlaps toggle button
7029 {
7030 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
7031 _("Remove overlaps"),
7032 _("Do not allow overlapping shapes"),
7033 INKSCAPE_ICON_DISTRIBUTE_REMOVE_OVERLAPS,
7034 Inkscape::ICON_SIZE_DECORATION );
7035 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7037 bool tbuttonstate = prefs->getBool("/tools/connector/avoidoverlaplayout");
7038 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), (tbuttonstate ? TRUE : FALSE ));
7040 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
7041 }
7043 // Code to watch for changes to the connector-spacing attribute in
7044 // the XML.
7045 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7046 g_assert(repr != NULL);
7048 purge_repr_listener( holder, holder );
7050 if (repr) {
7051 g_object_set_data( holder, "repr", repr );
7052 Inkscape::GC::anchor(repr);
7053 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
7054 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
7055 }
7056 } // end of sp_connector_toolbox_prep()
7059 //#########################
7060 //## Paintbucket ##
7061 //#########################
7063 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
7064 {
7065 gint channels = ege_select_one_action_get_active( act );
7066 flood_channels_set_channels( channels );
7067 }
7069 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
7070 {
7071 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7072 prefs->setInt("/tools/paintbucket/threshold", (gint)adj->value);
7073 }
7075 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
7076 {
7077 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7078 prefs->setBool("/tools/paintbucket/autogap", ege_select_one_action_get_active( act ));
7079 }
7081 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
7082 {
7083 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
7084 SPUnit const *unit = tracker->getActiveUnit();
7085 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7087 prefs->setDouble("/tools/paintbucket/offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
7088 prefs->setString("/tools/paintbucket/offsetunits", sp_unit_get_abbreviation(unit));
7089 }
7091 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
7092 {
7093 // FIXME: make defaults settable via Inkscape Options
7094 struct KeyValue {
7095 char const *key;
7096 double value;
7097 } const key_values[] = {
7098 {"threshold", 15},
7099 {"offset", 0.0}
7100 };
7102 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
7103 KeyValue const &kv = key_values[i];
7104 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
7105 if ( adj ) {
7106 gtk_adjustment_set_value(adj, kv.value);
7107 }
7108 }
7110 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
7111 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
7112 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
7113 ege_select_one_action_set_active( autogap_action, 0 );
7114 }
7116 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
7117 {
7118 EgeAdjustmentAction* eact = 0;
7119 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7121 {
7122 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7124 GList* items = 0;
7125 gint count = 0;
7126 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
7127 {
7128 GtkTreeIter iter;
7129 gtk_list_store_append( model, &iter );
7130 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7131 count++;
7132 }
7133 g_list_free( items );
7134 items = 0;
7135 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
7136 g_object_set( act1, "short_label", _("Fill by:"), NULL );
7137 ege_select_one_action_set_appearance( act1, "compact" );
7138 ege_select_one_action_set_active( act1, prefs->getInt("/tools/paintbucket/channels", 0) );
7139 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
7140 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
7141 g_object_set_data( holder, "channels_action", act1 );
7142 }
7144 // Spacing spinbox
7145 {
7146 eact = create_adjustment_action(
7147 "ThresholdAction",
7148 _("Fill Threshold"), _("Threshold:"),
7149 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
7150 "/tools/paintbucket/threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
7151 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
7152 0, 0, 0,
7153 paintbucket_threshold_changed, 1, 0 );
7155 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
7156 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7157 }
7159 // Create the units menu.
7160 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
7161 Glib::ustring stored_unit = prefs->getString("/tools/paintbucket/offsetunits");
7162 if (!stored_unit.empty())
7163 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit.data()));
7164 g_object_set_data( holder, "tracker", tracker );
7165 {
7166 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
7167 gtk_action_group_add_action( mainActions, act );
7168 }
7170 // Offset spinbox
7171 {
7172 eact = create_adjustment_action(
7173 "OffsetAction",
7174 _("Grow/shrink by"), _("Grow/shrink by:"),
7175 _("The amount to grow (positive) or shrink (negative) the created fill path"),
7176 "/tools/paintbucket/offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
7177 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
7178 0, 0, 0,
7179 paintbucket_offset_changed, 1, 2);
7180 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
7182 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7183 }
7185 /* Auto Gap */
7186 {
7187 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7189 GList* items = 0;
7190 gint count = 0;
7191 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
7192 {
7193 GtkTreeIter iter;
7194 gtk_list_store_append( model, &iter );
7195 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7196 count++;
7197 }
7198 g_list_free( items );
7199 items = 0;
7200 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
7201 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
7202 ege_select_one_action_set_appearance( act2, "compact" );
7203 ege_select_one_action_set_active( act2, prefs->getBool("/tools/paintbucket/autogap") );
7204 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
7205 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
7206 g_object_set_data( holder, "autogap_action", act2 );
7207 }
7209 /* Reset */
7210 {
7211 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
7212 _("Defaults"),
7213 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
7214 GTK_STOCK_CLEAR );
7215 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
7216 gtk_action_group_add_action( mainActions, act );
7217 gtk_action_set_sensitive( act, TRUE );
7218 }
7220 }
7222 /*
7223 Local Variables:
7224 mode:c++
7225 c-file-style:"stroustrup"
7226 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
7227 indent-tabs-mode:nil
7228 fill-column:99
7229 End:
7230 */
7231 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :