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