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