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_EDIT_CLONE,
727 SP_VERB_EDIT_COPY,
728 SP_VERB_EDIT_CUT,
729 SP_VERB_EDIT_DUPLICATE,
730 SP_VERB_EDIT_PASTE,
731 SP_VERB_EDIT_REDO,
732 SP_VERB_EDIT_UNDO,
733 SP_VERB_EDIT_UNLINK_CLONE,
734 SP_VERB_FILE_EXPORT,
735 SP_VERB_FILE_IMPORT,
736 SP_VERB_FILE_NEW,
737 SP_VERB_FILE_OPEN,
738 SP_VERB_FILE_PRINT,
739 SP_VERB_FILE_SAVE,
740 SP_VERB_OBJECT_TO_CURVE,
741 SP_VERB_SELECTION_GROUP,
742 SP_VERB_SELECTION_OUTLINE,
743 SP_VERB_SELECTION_UNGROUP,
744 SP_VERB_ZOOM_1_1,
745 SP_VERB_ZOOM_1_2,
746 SP_VERB_ZOOM_2_1,
747 SP_VERB_ZOOM_DRAWING,
748 SP_VERB_ZOOM_IN,
749 SP_VERB_ZOOM_NEXT,
750 SP_VERB_ZOOM_OUT,
751 SP_VERB_ZOOM_PAGE,
752 SP_VERB_ZOOM_PAGE_WIDTH,
753 SP_VERB_ZOOM_PREV,
754 SP_VERB_ZOOM_SELECTION,
755 };
757 Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
759 static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
760 Glib::RefPtr<Gtk::ActionGroup> mainActions;
761 if ( groups.find(desktop) != groups.end() ) {
762 mainActions = groups[desktop];
763 }
765 if ( !mainActions ) {
766 mainActions = Gtk::ActionGroup::create("main");
767 groups[desktop] = mainActions;
768 }
770 for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
771 Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
772 if ( verb ) {
773 if (!mainActions->get_action(verb->get_id())) {
774 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
775 mainActions->add(Glib::wrap(act));
776 }
777 }
778 }
780 if ( !mainActions->get_action("ToolZoom") ) {
781 GtkTooltips *tt = gtk_tooltips_new();
782 for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
783 Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
784 if ( va ) {
785 mainActions->add(va);
786 if ( i == 0 ) {
787 va->set_active(true);
788 }
789 }
790 }
791 }
794 return mainActions;
795 }
798 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
799 {
800 gtk_widget_set_size_request( widget,
801 widget->allocation.width,
802 widget->allocation.height );
803 }
805 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
806 {
807 gtk_widget_set_size_request( widget, -1, -1 );
808 }
812 GtkWidget *
813 sp_tool_toolbox_new()
814 {
815 GtkTooltips *tt = gtk_tooltips_new();
816 GtkWidget* tb = gtk_toolbar_new();
817 gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
818 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
820 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
821 g_object_set_data(G_OBJECT(tb), "tooltips", tt);
823 gtk_widget_set_sensitive(tb, FALSE);
825 GtkWidget *hb = gtk_handle_box_new();
826 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
827 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
828 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
830 gtk_container_add(GTK_CONTAINER(hb), tb);
831 gtk_widget_show(GTK_WIDGET(tb));
833 sigc::connection* conn = new sigc::connection;
834 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
836 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
837 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
839 return hb;
840 }
842 GtkWidget *
843 sp_aux_toolbox_new()
844 {
845 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
847 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
849 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
851 gtk_widget_set_sensitive(tb, FALSE);
853 GtkWidget *hb = gtk_handle_box_new();
854 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
855 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
856 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
858 gtk_container_add(GTK_CONTAINER(hb), tb);
859 gtk_widget_show(GTK_WIDGET(tb));
861 sigc::connection* conn = new sigc::connection;
862 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
864 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
865 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
867 return hb;
868 }
870 //####################################
871 //# Commands Bar
872 //####################################
874 GtkWidget *
875 sp_commands_toolbox_new()
876 {
877 GtkWidget *tb = gtk_toolbar_new();
879 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
880 gtk_widget_set_sensitive(tb, FALSE);
882 GtkWidget *hb = gtk_handle_box_new();
883 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
884 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
885 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
887 gtk_container_add(GTK_CONTAINER(hb), tb);
888 gtk_widget_show(GTK_WIDGET(tb));
890 sigc::connection* conn = new sigc::connection;
891 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
893 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
894 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
896 return hb;
897 }
899 GtkWidget *
900 sp_snap_toolbox_new()
901 {
902 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
903 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
904 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
906 //GtkWidget *tb = gtk_toolbar_new();
907 //g_object_set_data(G_OBJECT(tb), "desktop", NULL);
909 gtk_widget_set_sensitive(tb, FALSE);
911 GtkWidget *hb = gtk_handle_box_new();
912 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
913 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
914 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
916 gtk_container_add(GTK_CONTAINER(hb), tb);
917 gtk_widget_show(GTK_WIDGET(tb));
919 sigc::connection* conn = new sigc::connection;
920 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
922 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
923 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
925 return hb;
926 }
928 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
929 gchar const *label, gchar const *shortLabel, gchar const *tooltip,
930 Glib::ustring const &path, gdouble def,
931 GtkWidget *focusTarget,
932 GtkWidget *us,
933 GObject *dataKludge,
934 gboolean altx, gchar const *altx_mark,
935 gdouble lower, gdouble upper, gdouble step, gdouble page,
936 gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
937 void (*callback)(GtkAdjustment *, GObject *),
938 gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
939 {
940 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
941 GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs->getDouble(path, def) * factor,
942 lower, upper, step, page, 0 ) );
943 if (us) {
944 sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
945 }
947 gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
949 EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
950 if ( shortLabel ) {
951 g_object_set( act, "short_label", shortLabel, NULL );
952 }
954 if ( (descrCount > 0) && descrLabels && descrValues ) {
955 ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
956 }
958 if ( focusTarget ) {
959 ege_adjustment_action_set_focuswidget( act, focusTarget );
960 }
962 if ( altx && altx_mark ) {
963 g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
964 }
966 if ( dataKludge ) {
967 // Rather lame, but it's the only place where we need to get the entry name
968 // but we don't have an Entry
969 g_object_set_data( dataKludge, prefs->getEntry(path).getEntryName().data(), adj );
970 }
972 // Using a cast just to make sure we pass in the right kind of function pointer
973 g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
975 return act;
976 }
979 //####################################
980 //# node editing callbacks
981 //####################################
983 /**
984 * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
985 */
986 static ShapeEditor *get_current_shape_editor()
987 {
988 if (!SP_ACTIVE_DESKTOP) {
989 return NULL;
990 }
992 SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
994 if (!SP_IS_NODE_CONTEXT(event_context)) {
995 return NULL;
996 }
998 return event_context->shape_editor;
999 }
1002 void
1003 sp_node_path_edit_add(void)
1004 {
1005 ShapeEditor *shape_editor = get_current_shape_editor();
1006 if (shape_editor) shape_editor->add_node();
1007 }
1009 void
1010 sp_node_path_edit_delete(void)
1011 {
1012 ShapeEditor *shape_editor = get_current_shape_editor();
1013 if (shape_editor) shape_editor->delete_nodes_preserving_shape();
1014 }
1016 void
1017 sp_node_path_edit_delete_segment(void)
1018 {
1019 ShapeEditor *shape_editor = get_current_shape_editor();
1020 if (shape_editor) shape_editor->delete_segment();
1021 }
1023 void
1024 sp_node_path_edit_break(void)
1025 {
1026 ShapeEditor *shape_editor = get_current_shape_editor();
1027 if (shape_editor) shape_editor->break_at_nodes();
1028 }
1030 void
1031 sp_node_path_edit_join(void)
1032 {
1033 ShapeEditor *shape_editor = get_current_shape_editor();
1034 if (shape_editor) shape_editor->join_nodes();
1035 }
1037 void
1038 sp_node_path_edit_join_segment(void)
1039 {
1040 ShapeEditor *shape_editor = get_current_shape_editor();
1041 if (shape_editor) shape_editor->join_segments();
1042 }
1044 void
1045 sp_node_path_edit_toline(void)
1046 {
1047 ShapeEditor *shape_editor = get_current_shape_editor();
1048 if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1049 }
1051 void
1052 sp_node_path_edit_tocurve(void)
1053 {
1054 ShapeEditor *shape_editor = get_current_shape_editor();
1055 if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1056 }
1058 void
1059 sp_node_path_edit_cusp(void)
1060 {
1061 ShapeEditor *shape_editor = get_current_shape_editor();
1062 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1063 }
1065 void
1066 sp_node_path_edit_smooth(void)
1067 {
1068 ShapeEditor *shape_editor = get_current_shape_editor();
1069 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1070 }
1072 void
1073 sp_node_path_edit_symmetrical(void)
1074 {
1075 ShapeEditor *shape_editor = get_current_shape_editor();
1076 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1077 }
1079 void
1080 sp_node_path_edit_auto(void)
1081 {
1082 ShapeEditor *shape_editor = get_current_shape_editor();
1083 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_AUTO);
1084 }
1086 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1087 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1088 bool show = gtk_toggle_action_get_active( act );
1089 prefs->setBool("/tools/nodes/show_handles", show);
1090 ShapeEditor *shape_editor = get_current_shape_editor();
1091 if (shape_editor) shape_editor->show_handles(show);
1092 }
1094 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1095 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1096 bool show = gtk_toggle_action_get_active( act );
1097 prefs->setBool("/tools/nodes/show_helperpath", show);
1098 ShapeEditor *shape_editor = get_current_shape_editor();
1099 if (shape_editor) shape_editor->show_helperpath(show);
1100 }
1102 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1103 sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1104 }
1106 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1107 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1108 }
1110 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1111 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1112 }
1114 /* is called when the node selection is modified */
1115 static void
1116 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1117 {
1118 GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1119 GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1120 GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1121 GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1123 // quit if run by the attr_changed listener
1124 if (g_object_get_data( tbl, "freeze" )) {
1125 return;
1126 }
1128 // in turn, prevent listener from responding
1129 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1131 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1132 SPUnit const *unit = tracker->getActiveUnit();
1134 ShapeEditor *shape_editor = get_current_shape_editor();
1135 if (shape_editor && shape_editor->has_nodepath()) {
1136 Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1137 int n_selected = 0;
1138 if (nodepath) {
1139 n_selected = nodepath->numSelected();
1140 }
1142 if (n_selected == 0) {
1143 gtk_action_set_sensitive(xact, FALSE);
1144 gtk_action_set_sensitive(yact, FALSE);
1145 } else {
1146 gtk_action_set_sensitive(xact, TRUE);
1147 gtk_action_set_sensitive(yact, TRUE);
1148 Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1149 Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1151 if (n_selected == 1) {
1152 Geom::Point sel_node = nodepath->singleSelectedCoords();
1153 if (oldx != sel_node[Geom::X] || oldy != sel_node[Geom::Y]) {
1154 gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[Geom::X], *unit));
1155 gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[Geom::Y], *unit));
1156 }
1157 } else {
1158 boost::optional<Geom::Coord> x = sp_node_selected_common_coord(nodepath, Geom::X);
1159 boost::optional<Geom::Coord> y = sp_node_selected_common_coord(nodepath, Geom::Y);
1160 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1161 /* Note: Currently x and y will always have a value, even if the coordinates of the
1162 selected nodes don't coincide (in this case we use the coordinates of the center
1163 of the bounding box). So the entries are never set to zero. */
1164 // FIXME: Maybe we should clear the entry if several nodes are selected
1165 // instead of providing a kind of average value
1166 gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1167 gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1168 }
1169 }
1170 }
1171 } else {
1172 // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1173 gtk_action_set_sensitive(xact, FALSE);
1174 gtk_action_set_sensitive(yact, FALSE);
1175 }
1177 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1178 }
1180 static void
1181 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1182 {
1183 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1184 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1186 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1187 SPUnit const *unit = tracker->getActiveUnit();
1189 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1190 prefs->setDouble(Glib::ustring("/tools/nodes/") + value_name, sp_units_get_pixels(adj->value, *unit));
1191 }
1193 // quit if run by the attr_changed listener
1194 if (g_object_get_data( tbl, "freeze" )) {
1195 return;
1196 }
1198 // in turn, prevent listener from responding
1199 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1201 ShapeEditor *shape_editor = get_current_shape_editor();
1202 if (shape_editor && shape_editor->has_nodepath()) {
1203 double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1204 if (!strcmp(value_name, "x")) {
1205 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::X);
1206 }
1207 if (!strcmp(value_name, "y")) {
1208 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::Y);
1209 }
1210 }
1212 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1213 }
1215 static void
1216 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1217 {
1218 sp_node_path_value_changed(adj, tbl, "x");
1219 }
1221 static void
1222 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1223 {
1224 sp_node_path_value_changed(adj, tbl, "y");
1225 }
1227 void
1228 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1229 {
1230 {
1231 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1232 SPItem *item = selection->singleItem();
1233 if (item && SP_IS_LPE_ITEM(item)) {
1234 if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1235 gtk_action_set_sensitive(w, TRUE);
1236 } else {
1237 gtk_action_set_sensitive(w, FALSE);
1238 }
1239 } else {
1240 gtk_action_set_sensitive(w, FALSE);
1241 }
1242 }
1244 {
1245 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1246 SPItem *item = selection->singleItem();
1247 if (item && item->clip_ref && item->clip_ref->getObject()) {
1248 gtk_action_set_sensitive(w, TRUE);
1249 } else {
1250 gtk_action_set_sensitive(w, FALSE);
1251 }
1252 }
1254 {
1255 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1256 SPItem *item = selection->singleItem();
1257 if (item && item->mask_ref && item->mask_ref->getObject()) {
1258 gtk_action_set_sensitive(w, TRUE);
1259 } else {
1260 gtk_action_set_sensitive(w, FALSE);
1261 }
1262 }
1263 }
1265 void
1266 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1267 {
1268 sp_node_toolbox_sel_changed (selection, tbl);
1269 }
1273 //################################
1274 //## Node Editing Toolbox ##
1275 //################################
1277 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1278 {
1279 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1280 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1281 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1282 g_object_set_data( holder, "tracker", tracker );
1284 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
1286 {
1287 InkAction* inky = ink_action_new( "NodeInsertAction",
1288 _("Insert node"),
1289 _("Insert new nodes into selected segments"),
1290 INKSCAPE_ICON_NODE_ADD,
1291 secondarySize );
1292 g_object_set( inky, "short_label", _("Insert"), NULL );
1293 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1294 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1295 }
1297 {
1298 InkAction* inky = ink_action_new( "NodeDeleteAction",
1299 _("Delete node"),
1300 _("Delete selected nodes"),
1301 INKSCAPE_ICON_NODE_DELETE,
1302 secondarySize );
1303 g_object_set( inky, "short_label", _("Delete"), NULL );
1304 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1305 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1306 }
1308 {
1309 InkAction* inky = ink_action_new( "NodeJoinAction",
1310 _("Join endnodes"),
1311 _("Join selected endnodes"),
1312 INKSCAPE_ICON_NODE_JOIN,
1313 secondarySize );
1314 g_object_set( inky, "short_label", _("Join"), NULL );
1315 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1316 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1317 }
1319 {
1320 InkAction* inky = ink_action_new( "NodeBreakAction",
1321 _("Break nodes"),
1322 _("Break path at selected nodes"),
1323 INKSCAPE_ICON_NODE_BREAK,
1324 secondarySize );
1325 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1326 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1327 }
1330 {
1331 InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1332 _("Join with segment"),
1333 _("Join selected endnodes with a new segment"),
1334 INKSCAPE_ICON_NODE_JOIN_SEGMENT,
1335 secondarySize );
1336 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1337 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1338 }
1340 {
1341 InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1342 _("Delete segment"),
1343 _("Delete segment between two non-endpoint nodes"),
1344 INKSCAPE_ICON_NODE_DELETE_SEGMENT,
1345 secondarySize );
1346 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1347 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1348 }
1350 {
1351 InkAction* inky = ink_action_new( "NodeCuspAction",
1352 _("Node Cusp"),
1353 _("Make selected nodes corner"),
1354 INKSCAPE_ICON_NODE_TYPE_CUSP,
1355 secondarySize );
1356 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1357 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1358 }
1360 {
1361 InkAction* inky = ink_action_new( "NodeSmoothAction",
1362 _("Node Smooth"),
1363 _("Make selected nodes smooth"),
1364 INKSCAPE_ICON_NODE_TYPE_SMOOTH,
1365 secondarySize );
1366 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1367 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1368 }
1370 {
1371 InkAction* inky = ink_action_new( "NodeSymmetricAction",
1372 _("Node Symmetric"),
1373 _("Make selected nodes symmetric"),
1374 INKSCAPE_ICON_NODE_TYPE_SYMMETRIC,
1375 secondarySize );
1376 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1377 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1378 }
1380 {
1381 InkAction* inky = ink_action_new( "NodeAutoAction",
1382 _("Node Auto"),
1383 _("Make selected nodes auto-smooth"),
1384 INKSCAPE_ICON_NODE_TYPE_AUTO_SMOOTH,
1385 secondarySize );
1386 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_auto), 0 );
1387 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1388 }
1390 {
1391 InkAction* inky = ink_action_new( "NodeLineAction",
1392 _("Node Line"),
1393 _("Make selected segments lines"),
1394 INKSCAPE_ICON_NODE_SEGMENT_LINE,
1395 secondarySize );
1396 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1397 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1398 }
1400 {
1401 InkAction* inky = ink_action_new( "NodeCurveAction",
1402 _("Node Curve"),
1403 _("Make selected segments curves"),
1404 INKSCAPE_ICON_NODE_SEGMENT_CURVE,
1405 secondarySize );
1406 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1407 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1408 }
1410 {
1411 InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1412 _("Show Handles"),
1413 _("Show the Bezier handles of selected nodes"),
1414 INKSCAPE_ICON_SHOW_NODE_HANDLES,
1415 Inkscape::ICON_SIZE_DECORATION );
1416 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1417 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1418 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_handles", true) );
1419 }
1421 {
1422 InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1423 _("Show Outline"),
1424 _("Show the outline of the path"),
1425 INKSCAPE_ICON_SHOW_PATH_OUTLINE,
1426 Inkscape::ICON_SIZE_DECORATION );
1427 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1428 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1429 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_helperpath", false) );
1430 }
1432 {
1433 InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1434 _("Next path effect parameter"),
1435 _("Show next path effect parameter for editing"),
1436 INKSCAPE_ICON_PATH_EFFECT_PARAMETER_NEXT,
1437 Inkscape::ICON_SIZE_DECORATION );
1438 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1439 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1440 g_object_set_data( holder, "nodes_lpeedit", inky);
1441 }
1443 {
1444 InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1445 _("Edit clipping path"),
1446 _("Edit the clipping path of the object"),
1447 INKSCAPE_ICON_PATH_CLIP_EDIT,
1448 Inkscape::ICON_SIZE_DECORATION );
1449 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1450 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1451 g_object_set_data( holder, "nodes_clippathedit", inky);
1452 }
1454 {
1455 InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1456 _("Edit mask path"),
1457 _("Edit the mask of the object"),
1458 INKSCAPE_ICON_PATH_MASK_EDIT,
1459 Inkscape::ICON_SIZE_DECORATION );
1460 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1461 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1462 g_object_set_data( holder, "nodes_maskedit", inky);
1463 }
1465 /* X coord of selected node(s) */
1466 {
1467 EgeAdjustmentAction* eact = 0;
1468 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1469 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1470 eact = create_adjustment_action( "NodeXAction",
1471 _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1472 "/tools/nodes/Xcoord", 0,
1473 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1474 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1475 labels, values, G_N_ELEMENTS(labels),
1476 sp_node_path_x_value_changed );
1477 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1478 g_object_set_data( holder, "nodes_x_action", eact );
1479 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1480 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1481 }
1483 /* Y coord of selected node(s) */
1484 {
1485 EgeAdjustmentAction* eact = 0;
1486 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1487 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1488 eact = create_adjustment_action( "NodeYAction",
1489 _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1490 "/tools/nodes/Ycoord", 0,
1491 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1492 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1493 labels, values, G_N_ELEMENTS(labels),
1494 sp_node_path_y_value_changed );
1495 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1496 g_object_set_data( holder, "nodes_y_action", eact );
1497 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1498 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1499 }
1501 // add the units menu
1502 {
1503 GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1504 gtk_action_group_add_action( mainActions, act );
1505 }
1508 sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1510 //watch selection
1511 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1513 sigc::connection *c_selection_changed =
1514 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1515 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1516 pool->add_connection ("selection-changed", c_selection_changed);
1518 sigc::connection *c_selection_modified =
1519 new sigc::connection (sp_desktop_selection (desktop)->connectModified
1520 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1521 pool->add_connection ("selection-modified", c_selection_modified);
1523 sigc::connection *c_subselection_changed =
1524 new sigc::connection (desktop->connectToolSubselectionChanged
1525 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1526 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1528 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1530 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1531 } // end of sp_node_toolbox_prep()
1534 //########################
1535 //## Zoom Toolbox ##
1536 //########################
1538 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1539 {
1540 // no custom GtkAction setup needed
1541 } // end of sp_zoom_toolbox_prep()
1543 void
1544 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1545 {
1546 toolbox_set_desktop(toolbox,
1547 desktop,
1548 setup_tool_toolbox,
1549 update_tool_toolbox,
1550 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1551 "event_context_connection")));
1552 }
1555 void
1556 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1557 {
1558 toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1559 desktop,
1560 setup_aux_toolbox,
1561 update_aux_toolbox,
1562 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1563 "event_context_connection")));
1564 }
1566 void
1567 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1568 {
1569 toolbox_set_desktop(toolbox,
1570 desktop,
1571 setup_commands_toolbox,
1572 update_commands_toolbox,
1573 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1574 "event_context_connection")));
1575 }
1577 void
1578 sp_snap_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1579 {
1580 toolbox_set_desktop(toolbox,
1581 desktop,
1582 setup_snap_toolbox,
1583 update_snap_toolbox,
1584 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1585 "event_context_connection")));
1586 }
1589 static void
1590 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1591 {
1592 gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1593 SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1595 if (old_desktop) {
1596 GList *children, *iter;
1598 children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1599 for ( iter = children ; iter ; iter = iter->next ) {
1600 gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1601 }
1602 g_list_free(children);
1603 }
1605 g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1607 if (desktop) {
1608 gtk_widget_set_sensitive(toolbox, TRUE);
1609 setup_func(toolbox, desktop);
1610 update_func(desktop, desktop->event_context, toolbox);
1611 *conn = desktop->connectEventContextChanged
1612 (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1613 } else {
1614 gtk_widget_set_sensitive(toolbox, FALSE);
1615 }
1617 } // end of toolbox_set_desktop()
1620 static void
1621 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1622 {
1623 gchar const * descr =
1624 "<ui>"
1625 " <toolbar name='ToolToolbar'>"
1626 " <toolitem action='ToolSelector' />"
1627 " <toolitem action='ToolNode' />"
1628 " <toolitem action='ToolTweak' />"
1629 " <toolitem action='ToolZoom' />"
1630 " <toolitem action='ToolRect' />"
1631 " <toolitem action='Tool3DBox' />"
1632 " <toolitem action='ToolArc' />"
1633 " <toolitem action='ToolStar' />"
1634 " <toolitem action='ToolSpiral' />"
1635 " <toolitem action='ToolPencil' />"
1636 " <toolitem action='ToolPen' />"
1637 " <toolitem action='ToolCalligraphic' />"
1638 " <toolitem action='ToolEraser' />"
1639 // " <toolitem action='ToolLPETool' />"
1640 " <toolitem action='ToolPaintBucket' />"
1641 " <toolitem action='ToolText' />"
1642 " <toolitem action='ToolConnector' />"
1643 " <toolitem action='ToolGradient' />"
1644 " <toolitem action='ToolDropper' />"
1645 " </toolbar>"
1646 "</ui>";
1647 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1648 GtkUIManager* mgr = gtk_ui_manager_new();
1649 GError* errVal = 0;
1650 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1652 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1653 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1655 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" );
1656 if ( prefs->getBool("/toolbox/icononly", true) ) {
1657 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1658 }
1659 Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
1660 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1662 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
1663 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1665 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1667 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1668 if ( child ) {
1669 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1670 }
1672 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1673 // Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
1674 }
1677 static void
1678 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1679 {
1680 gchar const *const tname = ( eventcontext
1681 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1682 : NULL );
1683 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1685 for (int i = 0 ; tools[i].type_name ; i++ ) {
1686 Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1687 if ( act ) {
1688 bool setActive = tname && !strcmp(tname, tools[i].type_name);
1689 Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1690 if ( verbAct ) {
1691 verbAct->set_active(setActive);
1692 }
1693 }
1694 }
1695 }
1697 static void
1698 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1699 {
1700 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1701 GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1702 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1703 GtkUIManager* mgr = gtk_ui_manager_new();
1704 GError* errVal = 0;
1705 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1706 gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1708 std::map<std::string, GtkWidget*> dataHolders;
1710 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1711 if ( aux_toolboxes[i].prep_func ) {
1712 // converted to GtkActions and UIManager
1714 GtkWidget* kludge = gtk_toolbar_new();
1715 g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1716 g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1717 dataHolders[aux_toolboxes[i].type_name] = kludge;
1718 aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1719 } else {
1721 GtkWidget *sub_toolbox = 0;
1722 if (aux_toolboxes[i].create_func == NULL)
1723 sub_toolbox = sp_empty_toolbox_new(desktop);
1724 else {
1725 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1726 }
1728 gtk_size_group_add_widget( grouper, sub_toolbox );
1730 gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1731 g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1733 }
1734 }
1736 // Second pass to create toolbars *after* all GtkActions are created
1737 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1738 if ( aux_toolboxes[i].prep_func ) {
1739 // converted to GtkActions and UIManager
1741 GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1743 GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1744 gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1746 gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1747 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1748 g_free( tmp );
1749 tmp = 0;
1751 if ( prefs->getBool( "/toolbox/icononly", true) ) {
1752 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1753 }
1755 Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
1756 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1758 gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1760 if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1761 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1762 swatch->setDesktop( desktop );
1763 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1764 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1765 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1766 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 );
1767 }
1769 gtk_widget_show_all( holder );
1770 sp_set_font_size_smaller( holder );
1772 gtk_size_group_add_widget( grouper, holder );
1774 gtk_container_add( GTK_CONTAINER(toolbox), holder );
1775 g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1776 }
1777 }
1779 g_object_unref( G_OBJECT(grouper) );
1780 }
1782 static void
1783 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1784 {
1785 gchar const *tname = ( eventcontext
1786 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1787 : NULL );
1788 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1789 GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1790 if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1791 gtk_widget_show_all(sub_toolbox);
1792 g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1793 } else {
1794 gtk_widget_hide(sub_toolbox);
1795 }
1796 }
1797 }
1799 static void
1800 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1801 {
1802 gchar const * descr =
1803 "<ui>"
1804 " <toolbar name='CommandsToolbar'>"
1805 " <toolitem action='FileNew' />"
1806 " <toolitem action='FileOpen' />"
1807 " <toolitem action='FileSave' />"
1808 " <toolitem action='FilePrint' />"
1809 " <separator />"
1810 " <toolitem action='FileImport' />"
1811 " <toolitem action='FileExport' />"
1812 " <separator />"
1813 " <toolitem action='EditUndo' />"
1814 " <toolitem action='EditRedo' />"
1815 " <separator />"
1816 " <toolitem action='EditCopy' />"
1817 " <toolitem action='EditCut' />"
1818 " <toolitem action='EditPaste' />"
1819 " <separator />"
1820 " <toolitem action='ZoomSelection' />"
1821 " <toolitem action='ZoomDrawing' />"
1822 " <toolitem action='ZoomPage' />"
1823 " <separator />"
1824 " <toolitem action='EditDuplicate' />"
1825 " <toolitem action='EditClone' />"
1826 " <toolitem action='EditUnlinkClone' />"
1827 " <separator />"
1828 " <toolitem action='SelectionGroup' />"
1829 " <toolitem action='SelectionUnGroup' />"
1830 " <separator />"
1831 " <toolitem action='DialogFillStroke' />"
1832 " <toolitem action='DialogText' />"
1833 " <toolitem action='DialogXMLEditor' />"
1834 " <toolitem action='DialogAlignDistribute' />"
1835 " <separator />"
1836 " <toolitem action='DialogPreferences' />"
1837 " <toolitem action='DialogDocumentProperties' />"
1838 " </toolbar>"
1839 "</ui>";
1840 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1841 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1843 GtkUIManager* mgr = gtk_ui_manager_new();
1844 GError* errVal = 0;
1846 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1847 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1849 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1850 if ( prefs->getBool("/toolbox/icononly", true) ) {
1851 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1852 }
1854 Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
1855 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1857 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1858 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1861 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1863 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1864 if ( child ) {
1865 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1866 }
1868 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1869 }
1871 static void
1872 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1873 {
1874 }
1876 void toggle_snap_callback (GtkToggleAction *act, gpointer data) { //data points to the toolbox
1878 if (g_object_get_data(G_OBJECT(data), "freeze" )) {
1879 return;
1880 }
1882 gpointer ptr = g_object_get_data(G_OBJECT(data), "desktop");
1883 g_assert(ptr != NULL);
1885 SPDesktop *dt = reinterpret_cast<SPDesktop*>(ptr);
1886 SPNamedView *nv = sp_desktop_namedview(dt);
1888 if (dt == NULL || nv == NULL) {
1889 g_warning("No desktop or namedview specified (in toggle_snap_callback)!");
1890 return;
1891 }
1893 Inkscape::XML::Node *repr = SP_OBJECT_REPR(nv);
1895 if (repr == NULL) {
1896 g_warning("This namedview doesn't have a xml representation attached!");
1897 return;
1898 }
1900 bool v = false;
1901 SPAttributeEnum attr = (SPAttributeEnum) GPOINTER_TO_INT(g_object_get_data(G_OBJECT(act), "SP_ATTR_INKSCAPE"));
1903 switch (attr) {
1904 case SP_ATTR_INKSCAPE_SNAP_GLOBAL:
1905 dt->toggleSnapGlobal();
1906 break;
1907 case SP_ATTR_INKSCAPE_SNAP_BBOX:
1908 v = nv->snap_manager.snapprefs.getSnapModeBBox();
1909 sp_repr_set_boolean(repr, "inkscape:snap-bbox", !v);
1910 break;
1911 case SP_ATTR_INKSCAPE_BBOX_PATHS:
1912 v = nv->snap_manager.snapprefs.getSnapToBBoxPath();
1913 sp_repr_set_boolean(repr, "inkscape:bbox-paths", !v);
1914 break;
1915 case SP_ATTR_INKSCAPE_BBOX_NODES:
1916 v = nv->snap_manager.snapprefs.getSnapToBBoxNode();
1917 sp_repr_set_boolean(repr, "inkscape:bbox-nodes", !v);
1918 break;
1919 case SP_ATTR_INKSCAPE_SNAP_NODES:
1920 v = nv->snap_manager.snapprefs.getSnapModeNode();
1921 sp_repr_set_boolean(repr, "inkscape:snap-nodes", !v);
1922 break;
1923 case SP_ATTR_INKSCAPE_OBJECT_PATHS:
1924 v = nv->snap_manager.snapprefs.getSnapToItemPath();
1925 sp_repr_set_boolean(repr, "inkscape:object-paths", !v);
1926 break;
1927 case SP_ATTR_INKSCAPE_OBJECT_NODES:
1928 v = nv->snap_manager.snapprefs.getSnapToItemNode();
1929 sp_repr_set_boolean(repr, "inkscape:object-nodes", !v);
1930 break;
1931 case SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES:
1932 v = nv->snap_manager.snapprefs.getSnapSmoothNodes();
1933 sp_repr_set_boolean(repr, "inkscape:snap-smooth-nodes", !v);
1934 break;
1935 case SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS:
1936 v = nv->snap_manager.snapprefs.getSnapIntersectionCS();
1937 sp_repr_set_boolean(repr, "inkscape:snap-intersection-paths", !v);
1938 break;
1939 case SP_ATTR_INKSCAPE_SNAP_CENTER:
1940 v = nv->snap_manager.snapprefs.getIncludeItemCenter();
1941 sp_repr_set_boolean(repr, "inkscape:snap-center", !v);
1942 break;
1943 case SP_ATTR_INKSCAPE_SNAP_GRIDS:
1944 v = nv->snap_manager.snapprefs.getSnapToGrids();
1945 sp_repr_set_boolean(repr, "inkscape:snap-grids", !v);
1946 break;
1947 case SP_ATTR_INKSCAPE_SNAP_TO_GUIDES:
1948 v = nv->snap_manager.snapprefs.getSnapToGuides();
1949 sp_repr_set_boolean(repr, "inkscape:snap-to-guides", !v);
1950 break;
1951 case SP_ATTR_INKSCAPE_SNAP_PAGE:
1952 v = nv->snap_manager.snapprefs.getSnapToPageBorder();
1953 sp_repr_set_boolean(repr, "inkscape:snap-page", !v);
1954 break;
1955 /*case SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE:
1956 v = nv->snap_manager.snapprefs.getSnapIntersectionGG();
1957 sp_repr_set_boolean(repr, "inkscape:snap-intersection-grid-guide", !v);
1958 break;*/
1959 case SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS:
1960 v = nv->snap_manager.snapprefs.getSnapLineMidpoints();
1961 sp_repr_set_boolean(repr, "inkscape:snap-midpoints", !v);
1962 break;
1963 case SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS:
1964 v = nv->snap_manager.snapprefs.getSnapObjectMidpoints();
1965 sp_repr_set_boolean(repr, "inkscape:snap-object-midpoints", !v);
1966 break;
1967 case SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS:
1968 v = nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints();
1969 sp_repr_set_boolean(repr, "inkscape:snap-bbox-edge-midpoints", !v);
1970 break;
1971 case SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS:
1972 v = nv->snap_manager.snapprefs.getSnapBBoxMidpoints();
1973 sp_repr_set_boolean(repr, "inkscape:snap-bbox-midpoints", !v);
1974 break;
1975 default:
1976 g_warning("toggle_snap_callback has been called with an ID for which no action has been defined");
1977 break;
1978 }
1980 // The snapping preferences are stored in the document, and therefore toggling makes the document dirty
1981 SPDocument *doc = SP_OBJECT_DOCUMENT(nv);
1982 doc->setModifiedSinceSave();
1983 }
1985 void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1986 {
1987 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1988 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
1990 gchar const * descr =
1991 "<ui>"
1992 " <toolbar name='SnapToolbar'>"
1993 " <toolitem action='ToggleSnapGlobal' />"
1994 " <separator />"
1995 " <toolitem action='ToggleSnapFromBBoxCorner' />"
1996 " <toolitem action='ToggleSnapToBBoxPath' />"
1997 " <toolitem action='ToggleSnapToBBoxNode' />"
1998 " <toolitem action='ToggleSnapToFromBBoxEdgeMidpoints' />"
1999 " <toolitem action='ToggleSnapToFromBBoxCenters' />"
2000 " <separator />"
2001 " <toolitem action='ToggleSnapFromNode' />"
2002 " <toolitem action='ToggleSnapToItemPath' />"
2003 " <toolitem action='ToggleSnapToPathIntersections' />"
2004 " <toolitem action='ToggleSnapToItemNode' />"
2005 " <toolitem action='ToggleSnapToSmoothNodes' />"
2006 " <toolitem action='ToggleSnapToFromLineMidpoints' />"
2007 " <toolitem action='ToggleSnapToFromObjectCenters' />"
2008 " <toolitem action='ToggleSnapToFromRotationCenter' />"
2009 " <separator />"
2010 " <toolitem action='ToggleSnapToPageBorder' />"
2011 " <toolitem action='ToggleSnapToGrids' />"
2012 " <toolitem action='ToggleSnapToGuides' />"
2013 //" <toolitem action='ToggleSnapToGridGuideIntersections' />"
2014 " </toolbar>"
2015 "</ui>";
2017 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
2019 {
2020 InkToggleAction* act = ink_toggle_action_new("ToggleSnapGlobal",
2021 _("Snap"), _("Enable snapping"), INKSCAPE_ICON_SNAP, secondarySize,
2022 SP_ATTR_INKSCAPE_SNAP_GLOBAL);
2024 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2025 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2026 }
2028 {
2029 InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromBBoxCorner",
2030 _("Bounding box"), _("Snap bounding box corners"), INKSCAPE_ICON_SNAP_BOUNDING_BOX,
2031 secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX);
2033 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2034 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2035 }
2037 {
2038 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxPath",
2039 _("Bounding box edges"), _("Snap to edges of a bounding box"),
2040 INKSCAPE_ICON_SNAP_BOUNDING_BOX_EDGES, secondarySize, SP_ATTR_INKSCAPE_BBOX_PATHS);
2042 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2043 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2044 }
2046 {
2047 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxNode",
2048 _("Bounding box corners"), _("Snap to bounding box corners"),
2049 INKSCAPE_ICON_SNAP_BOUNDING_BOX_CORNERS, secondarySize, SP_ATTR_INKSCAPE_BBOX_NODES);
2051 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2052 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2053 }
2055 {
2056 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxEdgeMidpoints",
2057 _("BBox Edge Midpoints"), _("Snap from and to midpoints of bounding box edges"),
2058 INKSCAPE_ICON_SNAP_BOUNDING_BOX_MIDPOINTS, secondarySize,
2059 SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS);
2061 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2062 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2063 }
2065 {
2066 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxCenters",
2067 _("BBox Centers"), _("Snapping from and to centers of bounding boxes"),
2068 INKSCAPE_ICON_SNAP_BOUNDING_BOX_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS);
2070 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2071 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2072 }
2074 {
2075 InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromNode",
2076 _("Nodes"), _("Snap nodes"), INKSCAPE_ICON_SNAP_NODES, secondarySize, SP_ATTR_INKSCAPE_SNAP_NODES);
2078 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2079 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2080 }
2082 {
2083 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemPath",
2084 _("Paths"), _("Snap to paths"), INKSCAPE_ICON_SNAP_NODES_PATH, secondarySize,
2085 SP_ATTR_INKSCAPE_OBJECT_PATHS);
2087 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2088 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2089 }
2091 {
2092 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPathIntersections",
2093 _("Path intersections"), _("Snap to path intersections"),
2094 INKSCAPE_ICON_SNAP_NODES_INTERSECTION, secondarySize, SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS);
2096 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2097 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2098 }
2100 {
2101 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemNode",
2102 _("To nodes"), _("Snap to cusp nodes"), INKSCAPE_ICON_SNAP_NODES_CUSP, secondarySize,
2103 SP_ATTR_INKSCAPE_OBJECT_NODES);
2105 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2106 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2107 }
2109 {
2110 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToSmoothNodes",
2111 _("Smooth nodes"), _("Snap to smooth nodes"), INKSCAPE_ICON_SNAP_NODES_SMOOTH,
2112 secondarySize, SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES);
2114 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2115 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2116 }
2118 {
2119 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromLineMidpoints",
2120 _("Line Midpoints"), _("Snap from and to midpoints of line segments"),
2121 INKSCAPE_ICON_SNAP_NODES_MIDPOINT, secondarySize, SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS);
2123 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2124 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2125 }
2127 {
2128 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromObjectCenters",
2129 _("Object Centers"), _("Snap from and to centers of objects"),
2130 INKSCAPE_ICON_SNAP_NODES_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS);
2132 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2133 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2134 }
2136 {
2137 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromRotationCenter",
2138 _("Rotation Centers"), _("Snap from and to an item's rotation center"),
2139 INKSCAPE_ICON_SNAP_NODES_ROTATION_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_CENTER);
2141 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2142 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2143 }
2145 {
2146 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPageBorder",
2147 _("Page border"), _("Snap to the page border"), INKSCAPE_ICON_SNAP_PAGE,
2148 secondarySize, SP_ATTR_INKSCAPE_SNAP_PAGE);
2150 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2151 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2152 }
2154 {
2155 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGrids",
2156 _("Grids"), _("Snap to grids"), INKSCAPE_ICON_GRID_RECTANGULAR, secondarySize,
2157 SP_ATTR_INKSCAPE_SNAP_GRIDS);
2159 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2160 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2161 }
2163 {
2164 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGuides",
2165 _("Guides"), _("Snap to guides"), INKSCAPE_ICON_GUIDES, secondarySize,
2166 SP_ATTR_INKSCAPE_SNAP_TO_GUIDES);
2168 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2169 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2170 }
2172 /*{
2173 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGridGuideIntersections",
2174 _("Grid/guide intersections"), _("Snap to intersections of a grid with a guide"),
2175 INKSCAPE_ICON_SNAP_GRID_GUIDE_INTERSECTIONS, secondarySize,
2176 SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE);
2178 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2179 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2180 }*/
2182 GtkUIManager* mgr = gtk_ui_manager_new();
2183 GError* errVal = 0;
2185 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
2186 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
2188 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/SnapToolbar" );
2189 if ( prefs->getBool("/toolbox/icononly", true) ) {
2190 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
2191 }
2193 Inkscape::IconSize toolboxSize = prefToSize("/toolbox/secondary");
2194 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
2196 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
2197 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
2199 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
2201 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
2202 if ( child ) {
2203 gtk_container_remove( GTK_CONTAINER(toolbox), child );
2204 }
2206 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
2208 }
2210 void update_snap_toolbox(SPDesktop *desktop, SPEventContext */*eventcontext*/, GtkWidget *toolbox)
2211 {
2212 g_assert(desktop != NULL);
2213 g_assert(toolbox != NULL);
2215 SPNamedView *nv = sp_desktop_namedview(desktop);
2216 if (nv == NULL) {
2217 g_warning("Namedview cannot be retrieved (in update_snap_toolbox)!");
2218 return;
2219 }
2221 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2223 Glib::RefPtr<Gtk::Action> act1 = mainActions->get_action("ToggleSnapGlobal");
2224 Glib::RefPtr<Gtk::Action> act2 = mainActions->get_action("ToggleSnapFromBBoxCorner");
2225 Glib::RefPtr<Gtk::Action> act3 = mainActions->get_action("ToggleSnapToBBoxPath");
2226 Glib::RefPtr<Gtk::Action> act4 = mainActions->get_action("ToggleSnapToBBoxNode");
2227 Glib::RefPtr<Gtk::Action> act4b = mainActions->get_action("ToggleSnapToFromBBoxEdgeMidpoints");
2228 Glib::RefPtr<Gtk::Action> act4c = mainActions->get_action("ToggleSnapToFromBBoxCenters");
2229 Glib::RefPtr<Gtk::Action> act5 = mainActions->get_action("ToggleSnapFromNode");
2230 Glib::RefPtr<Gtk::Action> act6 = mainActions->get_action("ToggleSnapToItemPath");
2231 Glib::RefPtr<Gtk::Action> act6b = mainActions->get_action("ToggleSnapToPathIntersections");
2232 Glib::RefPtr<Gtk::Action> act7 = mainActions->get_action("ToggleSnapToItemNode");
2233 Glib::RefPtr<Gtk::Action> act8 = mainActions->get_action("ToggleSnapToSmoothNodes");
2234 Glib::RefPtr<Gtk::Action> act9 = mainActions->get_action("ToggleSnapToFromLineMidpoints");
2235 Glib::RefPtr<Gtk::Action> act10 = mainActions->get_action("ToggleSnapToFromObjectCenters");
2236 Glib::RefPtr<Gtk::Action> act11 = mainActions->get_action("ToggleSnapToFromRotationCenter");
2237 Glib::RefPtr<Gtk::Action> act12 = mainActions->get_action("ToggleSnapToPageBorder");
2238 //Glib::RefPtr<Gtk::Action> act13 = mainActions->get_action("ToggleSnapToGridGuideIntersections");
2239 Glib::RefPtr<Gtk::Action> act14 = mainActions->get_action("ToggleSnapToGrids");
2240 Glib::RefPtr<Gtk::Action> act15 = mainActions->get_action("ToggleSnapToGuides");
2243 if (!act1) {
2244 return; // The snap actions haven't been defined yet (might be the case during startup)
2245 }
2247 // The ..._set_active calls below will toggle the buttons, but this shouldn't lead to
2248 // changes in our document because we're only updating the UI;
2249 // Setting the "freeze" parameter to true will block the code in toggle_snap_callback()
2250 g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(TRUE));
2252 bool const c1 = nv->snap_manager.snapprefs.getSnapEnabledGlobally();
2253 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act1->gobj()), c1);
2255 bool const c2 = nv->snap_manager.snapprefs.getSnapModeBBox();
2256 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act2->gobj()), c2);
2257 gtk_action_set_sensitive(GTK_ACTION(act2->gobj()), c1);
2259 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act3->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxPath());
2260 gtk_action_set_sensitive(GTK_ACTION(act3->gobj()), c1 && c2);
2261 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxNode());
2262 gtk_action_set_sensitive(GTK_ACTION(act4->gobj()), c1 && c2);
2263 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4b->gobj()), nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints());
2264 gtk_action_set_sensitive(GTK_ACTION(act4b->gobj()), c1 && c2);
2265 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4c->gobj()), nv->snap_manager.snapprefs.getSnapBBoxMidpoints());
2266 gtk_action_set_sensitive(GTK_ACTION(act4c->gobj()), c1 && c2);
2268 bool const c3 = nv->snap_manager.snapprefs.getSnapModeNode();
2269 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act5->gobj()), c3);
2270 gtk_action_set_sensitive(GTK_ACTION(act5->gobj()), c1);
2272 bool const c4 = nv->snap_manager.snapprefs.getSnapToItemPath();
2273 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6->gobj()), c4);
2274 gtk_action_set_sensitive(GTK_ACTION(act6->gobj()), c1 && c3);
2275 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6b->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionCS());
2276 gtk_action_set_sensitive(GTK_ACTION(act6b->gobj()), c1 && c3 && c4);
2277 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act7->gobj()), nv->snap_manager.snapprefs.getSnapToItemNode());
2278 gtk_action_set_sensitive(GTK_ACTION(act7->gobj()), c1 && c3);
2279 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act8->gobj()), nv->snap_manager.snapprefs.getSnapSmoothNodes());
2280 gtk_action_set_sensitive(GTK_ACTION(act8->gobj()), c1 && c3);
2281 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act9->gobj()), nv->snap_manager.snapprefs.getSnapLineMidpoints());
2282 gtk_action_set_sensitive(GTK_ACTION(act9->gobj()), c1 && c3);
2283 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act10->gobj()), nv->snap_manager.snapprefs.getSnapObjectMidpoints());
2284 gtk_action_set_sensitive(GTK_ACTION(act10->gobj()), c1 && c3);
2285 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act11->gobj()), nv->snap_manager.snapprefs.getIncludeItemCenter());
2286 gtk_action_set_sensitive(GTK_ACTION(act11->gobj()), c1 && c3);
2288 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act12->gobj()), nv->snap_manager.snapprefs.getSnapToPageBorder());
2289 gtk_action_set_sensitive(GTK_ACTION(act12->gobj()), c1);
2290 //gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act13->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionGG());
2291 //gtk_action_set_sensitive(GTK_ACTION(act13->gobj()), c1);
2293 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act14->gobj()), nv->snap_manager.snapprefs.getSnapToGrids());
2294 gtk_action_set_sensitive(GTK_ACTION(act14->gobj()), c1);
2295 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act15->gobj()), nv->snap_manager.snapprefs.getSnapToGuides());
2296 gtk_action_set_sensitive(GTK_ACTION(act15->gobj()), c1);
2299 g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(FALSE)); // unfreeze (see above)
2300 }
2302 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
2303 {
2304 gtk_widget_show(toolbox_toplevel);
2305 GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
2307 GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
2308 if (!shown_toolbox) {
2309 return;
2310 }
2311 gtk_widget_show(toolbox);
2313 gtk_widget_show_all(shown_toolbox);
2314 }
2316 static GtkWidget *
2317 sp_empty_toolbox_new(SPDesktop *desktop)
2318 {
2319 GtkWidget *tbl = gtk_toolbar_new();
2320 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
2321 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
2323 gtk_widget_show_all(tbl);
2324 sp_set_font_size_smaller (tbl);
2326 return tbl;
2327 }
2329 #define MODE_LABEL_WIDTH 70
2331 //########################
2332 //## Star ##
2333 //########################
2335 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2336 {
2337 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2339 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2340 // do not remember prefs if this call is initiated by an undo change, because undoing object
2341 // creation sets bogus values to its attributes before it is deleted
2342 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2343 prefs->setInt("/tools/shapes/star/magnitude", (gint)adj->value);
2344 }
2346 // quit if run by the attr_changed listener
2347 if (g_object_get_data( dataKludge, "freeze" )) {
2348 return;
2349 }
2351 // in turn, prevent listener from responding
2352 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2354 bool modmade = false;
2356 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2357 GSList const *items = selection->itemList();
2358 for (; items != NULL; items = items->next) {
2359 if (SP_IS_STAR((SPItem *) items->data)) {
2360 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2361 sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
2362 sp_repr_set_svg_double(repr, "sodipodi:arg2",
2363 (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
2364 + M_PI / (gint)adj->value));
2365 SP_OBJECT((SPItem *) items->data)->updateRepr();
2366 modmade = true;
2367 }
2368 }
2369 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2370 _("Star: Change number of corners"));
2372 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2373 }
2375 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2376 {
2377 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2379 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2380 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2381 prefs->setDouble("/tools/shapes/star/proportion", adj->value);
2382 }
2384 // quit if run by the attr_changed listener
2385 if (g_object_get_data( dataKludge, "freeze" )) {
2386 return;
2387 }
2389 // in turn, prevent listener from responding
2390 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2392 bool modmade = false;
2393 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2394 GSList const *items = selection->itemList();
2395 for (; items != NULL; items = items->next) {
2396 if (SP_IS_STAR((SPItem *) items->data)) {
2397 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2399 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2400 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2401 if (r2 < r1) {
2402 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
2403 } else {
2404 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
2405 }
2407 SP_OBJECT((SPItem *) items->data)->updateRepr();
2408 modmade = true;
2409 }
2410 }
2412 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2413 _("Star: Change spoke ratio"));
2415 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2416 }
2418 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
2419 {
2420 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2421 bool flat = ege_select_one_action_get_active( act ) == 0;
2423 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2424 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2425 prefs->setBool( "/tools/shapes/star/isflatsided", flat);
2426 }
2428 // quit if run by the attr_changed listener
2429 if (g_object_get_data( dataKludge, "freeze" )) {
2430 return;
2431 }
2433 // in turn, prevent listener from responding
2434 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2436 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2437 GSList const *items = selection->itemList();
2438 GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2439 bool modmade = false;
2441 if ( prop_action ) {
2442 gtk_action_set_sensitive( prop_action, !flat );
2443 }
2445 for (; items != NULL; items = items->next) {
2446 if (SP_IS_STAR((SPItem *) items->data)) {
2447 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2448 repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
2449 SP_OBJECT((SPItem *) items->data)->updateRepr();
2450 modmade = true;
2451 }
2452 }
2454 if (modmade) {
2455 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2456 flat ? _("Make polygon") : _("Make star"));
2457 }
2459 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2460 }
2462 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2463 {
2464 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2466 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2467 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2468 prefs->setDouble("/tools/shapes/star/rounded", (gdouble) adj->value);
2469 }
2471 // quit if run by the attr_changed listener
2472 if (g_object_get_data( dataKludge, "freeze" )) {
2473 return;
2474 }
2476 // in turn, prevent listener from responding
2477 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2479 bool modmade = false;
2481 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2482 GSList const *items = selection->itemList();
2483 for (; items != NULL; items = items->next) {
2484 if (SP_IS_STAR((SPItem *) items->data)) {
2485 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2486 sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2487 SP_OBJECT(items->data)->updateRepr();
2488 modmade = true;
2489 }
2490 }
2491 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2492 _("Star: Change rounding"));
2494 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2495 }
2497 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2498 {
2499 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2501 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2502 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2503 prefs->setDouble("/tools/shapes/star/randomized", (gdouble) adj->value);
2504 }
2506 // quit if run by the attr_changed listener
2507 if (g_object_get_data( dataKludge, "freeze" )) {
2508 return;
2509 }
2511 // in turn, prevent listener from responding
2512 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2514 bool modmade = false;
2516 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2517 GSList const *items = selection->itemList();
2518 for (; items != NULL; items = items->next) {
2519 if (SP_IS_STAR((SPItem *) items->data)) {
2520 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2521 sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2522 SP_OBJECT(items->data)->updateRepr();
2523 modmade = true;
2524 }
2525 }
2526 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2527 _("Star: Change randomization"));
2529 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2530 }
2533 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2534 gchar const */*old_value*/, gchar const */*new_value*/,
2535 bool /*is_interactive*/, gpointer data)
2536 {
2537 GtkWidget *tbl = GTK_WIDGET(data);
2539 // quit if run by the _changed callbacks
2540 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2541 return;
2542 }
2544 // in turn, prevent callbacks from responding
2545 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2547 GtkAdjustment *adj = 0;
2549 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2550 bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2552 if (!strcmp(name, "inkscape:randomized")) {
2553 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2554 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2555 } else if (!strcmp(name, "inkscape:rounded")) {
2556 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2557 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2558 } else if (!strcmp(name, "inkscape:flatsided")) {
2559 GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2560 char const *flatsides = repr->attribute("inkscape:flatsided");
2561 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2562 if ( flatsides && !strcmp(flatsides,"false") ) {
2563 ege_select_one_action_set_active( flat_action, 1 );
2564 gtk_action_set_sensitive( prop_action, TRUE );
2565 } else {
2566 ege_select_one_action_set_active( flat_action, 0 );
2567 gtk_action_set_sensitive( prop_action, FALSE );
2568 }
2569 } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2570 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2571 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2572 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2573 if (r2 < r1) {
2574 gtk_adjustment_set_value(adj, r2/r1);
2575 } else {
2576 gtk_adjustment_set_value(adj, r1/r2);
2577 }
2578 } else if (!strcmp(name, "sodipodi:sides")) {
2579 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2580 gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2581 }
2583 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2584 }
2587 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2588 {
2589 NULL, /* child_added */
2590 NULL, /* child_removed */
2591 star_tb_event_attr_changed,
2592 NULL, /* content_changed */
2593 NULL /* order_changed */
2594 };
2597 /**
2598 * \param selection Should not be NULL.
2599 */
2600 static void
2601 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2602 {
2603 int n_selected = 0;
2604 Inkscape::XML::Node *repr = NULL;
2606 purge_repr_listener( tbl, tbl );
2608 for (GSList const *items = selection->itemList();
2609 items != NULL;
2610 items = items->next)
2611 {
2612 if (SP_IS_STAR((SPItem *) items->data)) {
2613 n_selected++;
2614 repr = SP_OBJECT_REPR((SPItem *) items->data);
2615 }
2616 }
2618 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2620 if (n_selected == 0) {
2621 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2622 } else if (n_selected == 1) {
2623 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2625 if (repr) {
2626 g_object_set_data( tbl, "repr", repr );
2627 Inkscape::GC::anchor(repr);
2628 sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2629 sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2630 }
2631 } else {
2632 // FIXME: implement averaging of all parameters for multiple selected stars
2633 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2634 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2635 }
2636 }
2639 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2640 {
2641 // FIXME: in this and all other _default functions, set some flag telling the value_changed
2642 // callbacks to lump all the changes for all selected objects in one undo step
2644 GtkAdjustment *adj = 0;
2646 // fixme: make settable in prefs!
2647 gint mag = 5;
2648 gdouble prop = 0.5;
2649 gboolean flat = FALSE;
2650 gdouble randomized = 0;
2651 gdouble rounded = 0;
2653 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2654 ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2656 GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2657 gtk_action_set_sensitive( sb2, !flat );
2659 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2660 gtk_adjustment_set_value(adj, mag);
2661 gtk_adjustment_value_changed(adj);
2663 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2664 gtk_adjustment_set_value(adj, prop);
2665 gtk_adjustment_value_changed(adj);
2667 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2668 gtk_adjustment_set_value(adj, rounded);
2669 gtk_adjustment_value_changed(adj);
2671 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2672 gtk_adjustment_set_value(adj, randomized);
2673 gtk_adjustment_value_changed(adj);
2674 }
2677 void
2678 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2679 {
2680 GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2681 if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2682 GtkWidget *l = gtk_label_new(NULL);
2683 gtk_label_set_markup(GTK_LABEL(l), title);
2684 gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2685 if ( GTK_IS_TOOLBAR(tbl) ) {
2686 gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2687 } else {
2688 gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2689 }
2690 gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2691 }
2694 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2695 {
2696 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
2698 {
2699 EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2700 ege_output_action_set_use_markup( act, TRUE );
2701 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2702 g_object_set_data( holder, "mode_action", act );
2703 }
2705 {
2706 EgeAdjustmentAction* eact = 0;
2707 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2708 bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2710 /* Flatsided checkbox */
2711 {
2712 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2714 GtkTreeIter iter;
2715 gtk_list_store_append( model, &iter );
2716 gtk_list_store_set( model, &iter,
2717 0, _("Polygon"),
2718 1, _("Regular polygon (with one handle) instead of a star"),
2719 2, INKSCAPE_ICON_DRAW_POLYGON,
2720 -1 );
2722 gtk_list_store_append( model, &iter );
2723 gtk_list_store_set( model, &iter,
2724 0, _("Star"),
2725 1, _("Star instead of a regular polygon (with one handle)"),
2726 2, INKSCAPE_ICON_DRAW_STAR,
2727 -1 );
2729 EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2730 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2731 g_object_set_data( holder, "flat_action", act );
2733 ege_select_one_action_set_appearance( act, "full" );
2734 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2735 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2736 ege_select_one_action_set_icon_column( act, 2 );
2737 ege_select_one_action_set_icon_size( act, secondarySize );
2738 ege_select_one_action_set_tooltip_column( act, 1 );
2740 ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2741 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2742 }
2744 /* Magnitude */
2745 {
2746 gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2747 gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2748 eact = create_adjustment_action( "MagnitudeAction",
2749 _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2750 "/tools/shapes/star/magnitude", 3,
2751 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2752 3, 1024, 1, 5,
2753 labels, values, G_N_ELEMENTS(labels),
2754 sp_stb_magnitude_value_changed,
2755 1.0, 0 );
2756 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2757 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2758 }
2760 /* Spoke ratio */
2761 {
2762 gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2763 gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2764 eact = create_adjustment_action( "SpokeAction",
2765 _("Spoke ratio"), _("Spoke ratio:"),
2766 // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2767 // Base radius is the same for the closest handle.
2768 _("Base radius to tip radius ratio"),
2769 "/tools/shapes/star/proportion", 0.5,
2770 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2771 0.01, 1.0, 0.01, 0.1,
2772 labels, values, G_N_ELEMENTS(labels),
2773 sp_stb_proportion_value_changed );
2774 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2775 g_object_set_data( holder, "prop_action", eact );
2776 }
2778 if ( !isFlatSided ) {
2779 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2780 } else {
2781 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2782 }
2784 /* Roundedness */
2785 {
2786 gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2787 gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2788 eact = create_adjustment_action( "RoundednessAction",
2789 _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2790 "/tools/shapes/star/rounded", 0.0,
2791 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2792 -10.0, 10.0, 0.01, 0.1,
2793 labels, values, G_N_ELEMENTS(labels),
2794 sp_stb_rounded_value_changed );
2795 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2796 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2797 }
2799 /* Randomization */
2800 {
2801 gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2802 gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2803 eact = create_adjustment_action( "RandomizationAction",
2804 _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2805 "/tools/shapes/star/randomized", 0.0,
2806 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2807 -10.0, 10.0, 0.001, 0.01,
2808 labels, values, G_N_ELEMENTS(labels),
2809 sp_stb_randomized_value_changed, 0.1, 3 );
2810 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2811 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2812 }
2813 }
2815 {
2816 /* Reset */
2817 {
2818 GtkAction* act = gtk_action_new( "StarResetAction",
2819 _("Defaults"),
2820 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2821 GTK_STOCK_CLEAR );
2822 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2823 gtk_action_group_add_action( mainActions, act );
2824 gtk_action_set_sensitive( act, TRUE );
2825 }
2826 }
2828 sigc::connection *connection = new sigc::connection(
2829 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2830 );
2831 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2832 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2833 }
2836 //########################
2837 //## Rect ##
2838 //########################
2840 static void sp_rtb_sensitivize( GObject *tbl )
2841 {
2842 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2843 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2844 GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2846 if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2847 gtk_action_set_sensitive( not_rounded, FALSE );
2848 } else {
2849 gtk_action_set_sensitive( not_rounded, TRUE );
2850 }
2851 }
2854 static void
2855 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2856 void (*setter)(SPRect *, gdouble))
2857 {
2858 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2860 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2861 SPUnit const *unit = tracker->getActiveUnit();
2863 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2864 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2865 prefs->setDouble(Glib::ustring("/tools/shapes/rect/") + value_name, sp_units_get_pixels(adj->value, *unit));
2866 }
2868 // quit if run by the attr_changed listener
2869 if (g_object_get_data( tbl, "freeze" )) {
2870 return;
2871 }
2873 // in turn, prevent listener from responding
2874 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2876 bool modmade = false;
2877 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2878 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2879 if (SP_IS_RECT(items->data)) {
2880 if (adj->value != 0) {
2881 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2882 } else {
2883 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2884 }
2885 modmade = true;
2886 }
2887 }
2889 sp_rtb_sensitivize( tbl );
2891 if (modmade) {
2892 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2893 _("Change rectangle"));
2894 }
2896 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2897 }
2899 static void
2900 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2901 {
2902 sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2903 }
2905 static void
2906 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2907 {
2908 sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2909 }
2911 static void
2912 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2913 {
2914 sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2915 }
2917 static void
2918 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2919 {
2920 sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2921 }
2925 static void
2926 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2927 {
2928 GtkAdjustment *adj = 0;
2930 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2931 gtk_adjustment_set_value(adj, 0.0);
2932 // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2933 gtk_adjustment_value_changed(adj);
2935 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2936 gtk_adjustment_set_value(adj, 0.0);
2937 gtk_adjustment_value_changed(adj);
2939 sp_rtb_sensitivize( obj );
2940 }
2942 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2943 gchar const */*old_value*/, gchar const */*new_value*/,
2944 bool /*is_interactive*/, gpointer data)
2945 {
2946 GObject *tbl = G_OBJECT(data);
2948 // quit if run by the _changed callbacks
2949 if (g_object_get_data( tbl, "freeze" )) {
2950 return;
2951 }
2953 // in turn, prevent callbacks from responding
2954 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2956 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2957 SPUnit const *unit = tracker->getActiveUnit();
2959 gpointer item = g_object_get_data( tbl, "item" );
2960 if (item && SP_IS_RECT(item)) {
2961 {
2962 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2963 gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2964 gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2965 }
2967 {
2968 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2969 gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2970 gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2971 }
2973 {
2974 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2975 gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2976 gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2977 }
2979 {
2980 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2981 gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2982 gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2983 }
2984 }
2986 sp_rtb_sensitivize( tbl );
2988 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2989 }
2992 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2993 NULL, /* child_added */
2994 NULL, /* child_removed */
2995 rect_tb_event_attr_changed,
2996 NULL, /* content_changed */
2997 NULL /* order_changed */
2998 };
3000 /**
3001 * \param selection should not be NULL.
3002 */
3003 static void
3004 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3005 {
3006 int n_selected = 0;
3007 Inkscape::XML::Node *repr = NULL;
3008 SPItem *item = NULL;
3010 if ( g_object_get_data( tbl, "repr" ) ) {
3011 g_object_set_data( tbl, "item", NULL );
3012 }
3013 purge_repr_listener( tbl, tbl );
3015 for (GSList const *items = selection->itemList();
3016 items != NULL;
3017 items = items->next) {
3018 if (SP_IS_RECT((SPItem *) items->data)) {
3019 n_selected++;
3020 item = (SPItem *) items->data;
3021 repr = SP_OBJECT_REPR(item);
3022 }
3023 }
3025 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3027 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3029 if (n_selected == 0) {
3030 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3032 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3033 gtk_action_set_sensitive(w, FALSE);
3034 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3035 gtk_action_set_sensitive(h, FALSE);
3037 } else if (n_selected == 1) {
3038 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3039 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3041 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3042 gtk_action_set_sensitive(w, TRUE);
3043 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3044 gtk_action_set_sensitive(h, TRUE);
3046 if (repr) {
3047 g_object_set_data( tbl, "repr", repr );
3048 g_object_set_data( tbl, "item", item );
3049 Inkscape::GC::anchor(repr);
3050 sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
3051 sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
3052 }
3053 } else {
3054 // FIXME: implement averaging of all parameters for multiple selected
3055 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3056 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3057 sp_rtb_sensitivize( tbl );
3058 }
3059 }
3062 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3063 {
3064 EgeAdjustmentAction* eact = 0;
3065 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3067 {
3068 EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
3069 ege_output_action_set_use_markup( act, TRUE );
3070 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3071 g_object_set_data( holder, "mode_action", act );
3072 }
3074 // rx/ry units menu: create
3075 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
3076 //tracker->addUnit( SP_UNIT_PERCENT, 0 );
3077 // fixme: add % meaning per cent of the width/height
3078 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
3079 g_object_set_data( holder, "tracker", tracker );
3081 /* W */
3082 {
3083 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3084 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3085 eact = create_adjustment_action( "RectWidthAction",
3086 _("Width"), _("W:"), _("Width of rectangle"),
3087 "/tools/shapes/rect/width", 0,
3088 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
3089 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3090 labels, values, G_N_ELEMENTS(labels),
3091 sp_rtb_width_value_changed );
3092 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3093 g_object_set_data( holder, "width_action", eact );
3094 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3095 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3096 }
3098 /* H */
3099 {
3100 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3101 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3102 eact = create_adjustment_action( "RectHeightAction",
3103 _("Height"), _("H:"), _("Height of rectangle"),
3104 "/tools/shapes/rect/height", 0,
3105 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3106 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3107 labels, values, G_N_ELEMENTS(labels),
3108 sp_rtb_height_value_changed );
3109 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3110 g_object_set_data( holder, "height_action", eact );
3111 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3112 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3113 }
3115 /* rx */
3116 {
3117 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3118 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3119 eact = create_adjustment_action( "RadiusXAction",
3120 _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
3121 "/tools/shapes/rect/rx", 0,
3122 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3123 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3124 labels, values, G_N_ELEMENTS(labels),
3125 sp_rtb_rx_value_changed);
3126 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3127 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3128 }
3130 /* ry */
3131 {
3132 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3133 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3134 eact = create_adjustment_action( "RadiusYAction",
3135 _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
3136 "/tools/shapes/rect/ry", 0,
3137 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3138 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3139 labels, values, G_N_ELEMENTS(labels),
3140 sp_rtb_ry_value_changed);
3141 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3142 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3143 }
3145 // add the units menu
3146 {
3147 GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
3148 gtk_action_group_add_action( mainActions, act );
3149 }
3151 /* Reset */
3152 {
3153 InkAction* inky = ink_action_new( "RectResetAction",
3154 _("Not rounded"),
3155 _("Make corners sharp"),
3156 INKSCAPE_ICON_RECTANGLE_MAKE_CORNERS_SHARP,
3157 secondarySize );
3158 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
3159 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3160 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3161 g_object_set_data( holder, "not_rounded", inky );
3162 }
3164 g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
3165 sp_rtb_sensitivize( holder );
3167 sigc::connection *connection = new sigc::connection(
3168 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
3169 );
3170 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3171 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3172 }
3174 //########################
3175 //## 3D Box ##
3176 //########################
3178 // normalize angle so that it lies in the interval [0,360]
3179 static double box3d_normalize_angle (double a) {
3180 double angle = a + ((int) (a/360.0))*360;
3181 if (angle < 0) {
3182 angle += 360.0;
3183 }
3184 return angle;
3185 }
3187 static void
3188 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
3189 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
3190 // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
3191 // have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
3192 // are reset).
3193 bool is_infinite = !persp3d_VP_is_finite(persp, axis);
3195 if (is_infinite) {
3196 gtk_toggle_action_set_active(tact, TRUE);
3197 gtk_action_set_sensitive(act, TRUE);
3199 double angle = persp3d_get_infinite_angle(persp, axis);
3200 if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
3201 gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
3202 }
3203 } else {
3204 gtk_toggle_action_set_active(tact, FALSE);
3205 gtk_action_set_sensitive(act, FALSE);
3206 }
3207 }
3209 static void
3210 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
3211 if (!persp_repr) {
3212 g_print ("No perspective given to box3d_resync_toolbar().\n");
3213 return;
3214 }
3216 GtkWidget *tbl = GTK_WIDGET(data);
3217 GtkAdjustment *adj = 0;
3218 GtkAction *act = 0;
3219 GtkToggleAction *tact = 0;
3220 Persp3D *persp = persp3d_get_from_repr(persp_repr);
3221 {
3222 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
3223 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
3224 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
3226 box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
3227 }
3228 {
3229 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
3230 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
3231 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
3233 box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
3234 }
3235 {
3236 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
3237 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
3238 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
3240 box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
3241 }
3242 }
3244 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3245 gchar const */*old_value*/, gchar const */*new_value*/,
3246 bool /*is_interactive*/, gpointer data)
3247 {
3248 GtkWidget *tbl = GTK_WIDGET(data);
3250 // quit if run by the attr_changed or selection changed listener
3251 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3252 return;
3253 }
3255 // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
3256 // sp_document_maybe_done() when the document is undo insensitive)
3257 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3259 // TODO: Only update the appropriate part of the toolbar
3260 // if (!strcmp(name, "inkscape:vp_z")) {
3261 box3d_resync_toolbar(repr, G_OBJECT(tbl));
3262 // }
3264 Persp3D *persp = persp3d_get_from_repr(repr);
3265 persp3d_update_box_reprs(persp);
3267 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3268 }
3270 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
3271 {
3272 NULL, /* child_added */
3273 NULL, /* child_removed */
3274 box3d_persp_tb_event_attr_changed,
3275 NULL, /* content_changed */
3276 NULL /* order_changed */
3277 };
3279 /**
3280 * \param selection Should not be NULL.
3281 */
3282 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
3283 // Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
3284 static void
3285 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3286 {
3287 // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
3288 // disable the angle entry fields for this direction (otherwise entering a value in them should only
3289 // update the perspectives with infinite VPs and leave the other ones untouched).
3291 Inkscape::XML::Node *persp_repr = NULL;
3292 purge_repr_listener(tbl, tbl);
3294 SPItem *item = selection->singleItem();
3295 if (item && SP_IS_BOX3D(item)) {
3296 // FIXME: Also deal with multiple selected boxes
3297 SPBox3D *box = SP_BOX3D(item);
3298 Persp3D *persp = box3d_get_perspective(box);
3299 persp_repr = SP_OBJECT_REPR(persp);
3300 if (persp_repr) {
3301 g_object_set_data(tbl, "repr", persp_repr);
3302 Inkscape::GC::anchor(persp_repr);
3303 sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
3304 sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
3305 }
3307 inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
3308 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3309 prefs->setString("/tools/shapes/3dbox/persp", persp_repr->attribute("id"));
3311 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
3312 box3d_resync_toolbar(persp_repr, tbl);
3313 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
3314 }
3315 }
3317 static void
3318 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
3319 {
3320 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3321 SPDocument *document = sp_desktop_document(desktop);
3323 // quit if run by the attr_changed or selection changed listener
3324 if (g_object_get_data( dataKludge, "freeze" )) {
3325 return;
3326 }
3328 // in turn, prevent listener from responding
3329 g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
3331 //Persp3D *persp = document->current_persp3d;
3332 std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
3333 if (sel_persps.empty()) {
3334 // this can happen when the document is created; we silently ignore it
3335 return;
3336 }
3337 Persp3D *persp = sel_persps.front();
3339 persp->tmat.set_infinite_direction (axis, adj->value);
3340 SP_OBJECT(persp)->updateRepr();
3342 // TODO: use the correct axis here, too
3343 sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
3345 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
3346 }
3349 static void
3350 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3351 {
3352 box3d_angle_value_changed(adj, dataKludge, Proj::X);
3353 }
3355 static void
3356 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3357 {
3358 box3d_angle_value_changed(adj, dataKludge, Proj::Y);
3359 }
3361 static void
3362 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3363 {
3364 box3d_angle_value_changed(adj, dataKludge, Proj::Z);
3365 }
3368 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
3369 {
3370 // TODO: Take all selected perspectives into account
3371 std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
3372 if (sel_persps.empty()) {
3373 // this can happen when the document is created; we silently ignore it
3374 return;
3375 }
3376 Persp3D *persp = sel_persps.front();
3378 bool set_infinite = gtk_toggle_action_get_active(act);
3379 persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
3380 }
3382 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3383 {
3384 box3d_vp_state_changed(act, box3d_angle, Proj::X);
3385 }
3387 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3388 {
3389 box3d_vp_state_changed(act, box3d_angle, Proj::Y);
3390 }
3392 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3393 {
3394 box3d_vp_state_changed(act, box3d_angle, Proj::Z);
3395 }
3397 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3398 {
3399 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3400 EgeAdjustmentAction* eact = 0;
3401 SPDocument *document = sp_desktop_document (desktop);
3402 Persp3D *persp = document->current_persp3d;
3404 EgeAdjustmentAction* box3d_angle_x = 0;
3405 EgeAdjustmentAction* box3d_angle_y = 0;
3406 EgeAdjustmentAction* box3d_angle_z = 0;
3408 /* Angle X */
3409 {
3410 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3411 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3412 eact = create_adjustment_action( "3DBoxAngleXAction",
3413 _("Angle in X direction"), _("Angle X:"),
3414 // Translators: PL is short for 'perspective line'
3415 _("Angle of PLs in X direction"),
3416 "/tools/shapes/3dbox/box3d_angle_x", 30,
3417 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
3418 -360.0, 360.0, 1.0, 10.0,
3419 labels, values, G_N_ELEMENTS(labels),
3420 box3d_angle_x_value_changed );
3421 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3422 g_object_set_data( holder, "box3d_angle_x_action", eact );
3423 box3d_angle_x = eact;
3424 }
3426 if (!persp3d_VP_is_finite(persp, Proj::X)) {
3427 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3428 } else {
3429 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3430 }
3433 /* VP X state */
3434 {
3435 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
3436 // Translators: VP is short for 'vanishing point'
3437 _("State of VP in X direction"),
3438 _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
3439 INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3440 Inkscape::ICON_SIZE_DECORATION );
3441 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3442 g_object_set_data( holder, "box3d_vp_x_state_action", act );
3443 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
3444 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3445 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3446 }
3448 /* Angle Y */
3449 {
3450 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3451 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3452 eact = create_adjustment_action( "3DBoxAngleYAction",
3453 _("Angle in Y direction"), _("Angle Y:"),
3454 // Translators: PL is short for 'perspective line'
3455 _("Angle of PLs in Y direction"),
3456 "/tools/shapes/3dbox/box3d_angle_y", 30,
3457 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3458 -360.0, 360.0, 1.0, 10.0,
3459 labels, values, G_N_ELEMENTS(labels),
3460 box3d_angle_y_value_changed );
3461 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3462 g_object_set_data( holder, "box3d_angle_y_action", eact );
3463 box3d_angle_y = eact;
3464 }
3466 if (!persp3d_VP_is_finite(persp, Proj::Y)) {
3467 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3468 } else {
3469 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3470 }
3472 /* VP Y state */
3473 {
3474 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
3475 // Translators: VP is short for 'vanishing point'
3476 _("State of VP in Y direction"),
3477 _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
3478 INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3479 Inkscape::ICON_SIZE_DECORATION );
3480 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3481 g_object_set_data( holder, "box3d_vp_y_state_action", act );
3482 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
3483 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3484 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3485 }
3487 /* Angle Z */
3488 {
3489 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3490 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3491 eact = create_adjustment_action( "3DBoxAngleZAction",
3492 _("Angle in Z direction"), _("Angle Z:"),
3493 // Translators: PL is short for 'perspective line'
3494 _("Angle of PLs in Z direction"),
3495 "/tools/shapes/3dbox/box3d_angle_z", 30,
3496 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3497 -360.0, 360.0, 1.0, 10.0,
3498 labels, values, G_N_ELEMENTS(labels),
3499 box3d_angle_z_value_changed );
3500 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3501 g_object_set_data( holder, "box3d_angle_z_action", eact );
3502 box3d_angle_z = eact;
3503 }
3505 if (!persp3d_VP_is_finite(persp, Proj::Z)) {
3506 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3507 } else {
3508 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3509 }
3511 /* VP Z state */
3512 {
3513 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3514 // Translators: VP is short for 'vanishing point'
3515 _("State of VP in Z direction"),
3516 _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3517 INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3518 Inkscape::ICON_SIZE_DECORATION );
3519 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3520 g_object_set_data( holder, "box3d_vp_z_state_action", act );
3521 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3522 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3523 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3524 }
3526 sigc::connection *connection = new sigc::connection(
3527 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3528 );
3529 g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3530 g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3531 }
3533 //########################
3534 //## Spiral ##
3535 //########################
3537 static void
3538 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, Glib::ustring const &value_name)
3539 {
3540 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3542 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3543 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3544 prefs->setDouble("/tools/shapes/spiral/" + value_name, adj->value);
3545 }
3547 // quit if run by the attr_changed listener
3548 if (g_object_get_data( tbl, "freeze" )) {
3549 return;
3550 }
3552 // in turn, prevent listener from responding
3553 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3555 gchar* namespaced_name = g_strconcat("sodipodi:", value_name.data(), NULL);
3557 bool modmade = false;
3558 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3559 items != NULL;
3560 items = items->next)
3561 {
3562 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3563 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3564 sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3565 SP_OBJECT((SPItem *) items->data)->updateRepr();
3566 modmade = true;
3567 }
3568 }
3570 g_free(namespaced_name);
3572 if (modmade) {
3573 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3574 _("Change spiral"));
3575 }
3577 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3578 }
3580 static void
3581 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3582 {
3583 sp_spl_tb_value_changed(adj, tbl, "revolution");
3584 }
3586 static void
3587 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3588 {
3589 sp_spl_tb_value_changed(adj, tbl, "expansion");
3590 }
3592 static void
3593 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3594 {
3595 sp_spl_tb_value_changed(adj, tbl, "t0");
3596 }
3598 static void
3599 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3600 {
3601 GtkWidget *tbl = GTK_WIDGET(obj);
3603 GtkAdjustment *adj;
3605 // fixme: make settable
3606 gdouble rev = 5;
3607 gdouble exp = 1.0;
3608 gdouble t0 = 0.0;
3610 adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3611 gtk_adjustment_set_value(adj, rev);
3612 gtk_adjustment_value_changed(adj);
3614 adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3615 gtk_adjustment_set_value(adj, exp);
3616 gtk_adjustment_value_changed(adj);
3618 adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3619 gtk_adjustment_set_value(adj, t0);
3620 gtk_adjustment_value_changed(adj);
3622 spinbutton_defocus(GTK_OBJECT(tbl));
3623 }
3626 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3627 gchar const */*old_value*/, gchar const */*new_value*/,
3628 bool /*is_interactive*/, gpointer data)
3629 {
3630 GtkWidget *tbl = GTK_WIDGET(data);
3632 // quit if run by the _changed callbacks
3633 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3634 return;
3635 }
3637 // in turn, prevent callbacks from responding
3638 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3640 GtkAdjustment *adj;
3641 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3642 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3644 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3645 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3647 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3648 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3650 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3651 }
3654 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3655 NULL, /* child_added */
3656 NULL, /* child_removed */
3657 spiral_tb_event_attr_changed,
3658 NULL, /* content_changed */
3659 NULL /* order_changed */
3660 };
3662 static void
3663 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3664 {
3665 int n_selected = 0;
3666 Inkscape::XML::Node *repr = NULL;
3668 purge_repr_listener( tbl, tbl );
3670 for (GSList const *items = selection->itemList();
3671 items != NULL;
3672 items = items->next)
3673 {
3674 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3675 n_selected++;
3676 repr = SP_OBJECT_REPR((SPItem *) items->data);
3677 }
3678 }
3680 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3682 if (n_selected == 0) {
3683 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3684 } else if (n_selected == 1) {
3685 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3687 if (repr) {
3688 g_object_set_data( tbl, "repr", repr );
3689 Inkscape::GC::anchor(repr);
3690 sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3691 sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3692 }
3693 } else {
3694 // FIXME: implement averaging of all parameters for multiple selected
3695 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3696 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3697 }
3698 }
3701 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3702 {
3703 EgeAdjustmentAction* eact = 0;
3704 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3706 {
3707 EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3708 ege_output_action_set_use_markup( act, TRUE );
3709 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3710 g_object_set_data( holder, "mode_action", act );
3711 }
3713 /* Revolution */
3714 {
3715 gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3716 gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3717 eact = create_adjustment_action( "SpiralRevolutionAction",
3718 _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3719 "/tools/shapes/spiral/revolution", 3.0,
3720 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3721 0.01, 1024.0, 0.1, 1.0,
3722 labels, values, G_N_ELEMENTS(labels),
3723 sp_spl_tb_revolution_value_changed, 1, 2);
3724 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3725 }
3727 /* Expansion */
3728 {
3729 gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3730 gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3731 eact = create_adjustment_action( "SpiralExpansionAction",
3732 _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3733 "/tools/shapes/spiral/expansion", 1.0,
3734 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3735 0.0, 1000.0, 0.01, 1.0,
3736 labels, values, G_N_ELEMENTS(labels),
3737 sp_spl_tb_expansion_value_changed);
3738 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3739 }
3741 /* T0 */
3742 {
3743 gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3744 gdouble values[] = {0, 0.5, 0.9};
3745 eact = create_adjustment_action( "SpiralT0Action",
3746 _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3747 "/tools/shapes/spiral/t0", 0.0,
3748 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3749 0.0, 0.999, 0.01, 1.0,
3750 labels, values, G_N_ELEMENTS(labels),
3751 sp_spl_tb_t0_value_changed);
3752 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3753 }
3755 /* Reset */
3756 {
3757 InkAction* inky = ink_action_new( "SpiralResetAction",
3758 _("Defaults"),
3759 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3760 GTK_STOCK_CLEAR,
3761 secondarySize );
3762 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3763 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3764 }
3767 sigc::connection *connection = new sigc::connection(
3768 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3769 );
3770 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3771 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3772 }
3774 //########################
3775 //## Pen/Pencil ##
3776 //########################
3778 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3779 static Glib::ustring const
3780 freehand_tool_name(GObject *dataKludge)
3781 {
3782 SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3783 return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3784 ? "/tools/freehand/pen"
3785 : "/tools/freehand/pencil" );
3786 }
3788 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3789 {
3790 gint mode = ege_select_one_action_get_active(act);
3792 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3793 prefs->setInt(freehand_tool_name(tbl) + "/freehand-mode", mode);
3795 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3797 // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3798 // preparatory work here
3799 if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3800 SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3801 sp_pen_context_set_polyline_mode(pc);
3802 }
3803 }
3805 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3806 {
3807 /* Freehand mode toggle buttons */
3808 {
3809 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3810 guint freehandMode = prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/freehand-mode" : "/tools/freehand/pen/freehand-mode" ), 0);
3811 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3813 {
3814 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3816 GtkTreeIter iter;
3817 gtk_list_store_append( model, &iter );
3818 gtk_list_store_set( model, &iter,
3819 0, _("Bezier"),
3820 1, _("Create regular Bezier path"),
3821 2, INKSCAPE_ICON_PATH_MODE_BEZIER,
3822 -1 );
3824 gtk_list_store_append( model, &iter );
3825 gtk_list_store_set( model, &iter,
3826 0, _("Spiro"),
3827 1, _("Create Spiro path"),
3828 2, INKSCAPE_ICON_PATH_MODE_SPIRO,
3829 -1 );
3831 if (!tool_is_pencil) {
3832 gtk_list_store_append( model, &iter );
3833 gtk_list_store_set( model, &iter,
3834 0, _("Zigzag"),
3835 1, _("Create a sequence of straight line segments"),
3836 2, INKSCAPE_ICON_PATH_MODE_POLYLINE,
3837 -1 );
3839 gtk_list_store_append( model, &iter );
3840 gtk_list_store_set( model, &iter,
3841 0, _("Paraxial"),
3842 1, _("Create a sequence of paraxial line segments"),
3843 2, INKSCAPE_ICON_PATH_MODE_POLYLINE_PARAXIAL,
3844 -1 );
3845 }
3847 EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3848 "FreehandModeActionPencil" :
3849 "FreehandModeActionPen",
3850 (_("Mode:")), ("Mode"), NULL, GTK_TREE_MODEL(model) );
3851 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3853 ege_select_one_action_set_appearance( act, "full" );
3854 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3855 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3856 ege_select_one_action_set_icon_column( act, 2 );
3857 ege_select_one_action_set_icon_size( act, secondarySize );
3858 ege_select_one_action_set_tooltip_column( act, 1 );
3860 ege_select_one_action_set_active( act, freehandMode);
3861 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3862 }
3863 }
3864 }
3866 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3867 gint shape = ege_select_one_action_get_active( act );
3868 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3869 prefs->setInt(freehand_tool_name(dataKludge) + "/shape", shape);
3870 }
3872 /**
3873 * \brief Generate the list of freehand advanced shape option entries.
3874 */
3875 GList * freehand_shape_dropdown_items_list() {
3876 GList *glist = NULL;
3878 glist = g_list_append (glist, _("None"));
3879 glist = g_list_append (glist, _("Triangle in"));
3880 glist = g_list_append (glist, _("Triangle out"));
3881 glist = g_list_append (glist, _("Ellipse"));
3882 glist = g_list_append (glist, _("From clipboard"));
3884 return glist;
3885 }
3887 static void
3888 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3889 /*advanced shape options */
3890 {
3891 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3892 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3894 GList* items = 0;
3895 gint count = 0;
3896 for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3897 {
3898 GtkTreeIter iter;
3899 gtk_list_store_append( model, &iter );
3900 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3901 count++;
3902 }
3903 g_list_free( items );
3904 items = 0;
3905 EgeSelectOneAction* act1 = ege_select_one_action_new(
3906 tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3907 _("Shape:"), ("Shape"), NULL, GTK_TREE_MODEL(model));
3908 g_object_set( act1, "short_label", _("Shape:"), NULL );
3909 ege_select_one_action_set_appearance( act1, "compact" );
3910 ege_select_one_action_set_active( act1, prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/shape" : "/tools/freehand/pen/shape" ), 0) );
3911 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3912 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3913 g_object_set_data( holder, "shape_action", act1 );
3914 }
3915 }
3917 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3918 {
3919 sp_add_freehand_mode_toggle(mainActions, holder, false);
3920 freehand_add_advanced_shape_options(mainActions, holder, false);
3921 }
3924 static void
3925 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3926 {
3927 GtkWidget *tbl = GTK_WIDGET(obj);
3929 GtkAdjustment *adj;
3931 // fixme: make settable
3932 gdouble tolerance = 4;
3934 adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3935 gtk_adjustment_set_value(adj, tolerance);
3936 gtk_adjustment_value_changed(adj);
3938 spinbutton_defocus(GTK_OBJECT(tbl));
3939 }
3941 static void
3942 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3943 {
3944 // quit if run by the attr_changed listener
3945 if (g_object_get_data( tbl, "freeze" )) {
3946 return;
3947 }
3948 // in turn, prevent listener from responding
3949 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3950 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3951 prefs->setDouble("/tools/freehand/pencil/tolerance", adj->value);
3952 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3953 }
3955 class PencilToleranceObserver : public Inkscape::Preferences::Observer {
3956 public:
3957 PencilToleranceObserver(Glib::ustring const &path, GObject *x) : Observer(path), _obj(x)
3958 {
3959 g_object_set_data(_obj, "prefobserver", this);
3960 }
3961 virtual ~PencilToleranceObserver() {
3962 if (g_object_get_data(_obj, "prefobserver") == this) {
3963 g_object_set_data(_obj, "prefobserver", NULL);
3964 }
3965 }
3966 virtual void notify(Inkscape::Preferences::Entry const &val) {
3967 GObject* tbl = _obj;
3968 if (g_object_get_data( tbl, "freeze" )) {
3969 return;
3970 }
3971 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3973 GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl, "tolerance");
3975 double v = val.getDouble(adj->value);
3976 gtk_adjustment_set_value(adj, v);
3977 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3978 }
3979 private:
3980 GObject *_obj;
3981 };
3984 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3985 {
3986 sp_add_freehand_mode_toggle(mainActions, holder, true);
3988 EgeAdjustmentAction* eact = 0;
3990 /* Tolerance */
3991 {
3992 gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
3993 gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
3994 eact = create_adjustment_action( "PencilToleranceAction",
3995 _("Smoothing:"), _("Smoothing: "),
3996 _("How much smoothing (simplifying) is applied to the line"),
3997 "/tools/freehand/pencil/tolerance",
3998 3.0,
3999 GTK_WIDGET(desktop->canvas), NULL,
4000 holder, TRUE, "altx-pencil",
4001 1, 100.0, 0.5, 0,
4002 labels, values, G_N_ELEMENTS(labels),
4003 sp_pencil_tb_tolerance_value_changed,
4004 1, 2);
4005 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4006 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4008 PencilToleranceObserver *obs =
4009 new PencilToleranceObserver("/tools/freehand/pencil/tolerance", G_OBJECT(holder));
4010 }
4012 /* advanced shape options */
4013 freehand_add_advanced_shape_options(mainActions, holder, true);
4015 /* Reset */
4016 {
4017 InkAction* inky = ink_action_new( "PencilResetAction",
4018 _("Defaults"),
4019 _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
4020 GTK_STOCK_CLEAR,
4021 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4022 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
4023 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4024 }
4026 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4028 }
4031 //########################
4032 //## Tweak ##
4033 //########################
4035 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4036 {
4037 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4038 prefs->setDouble( "/tools/tweak/width", adj->value * 0.01 );
4039 }
4041 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4042 {
4043 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4044 prefs->setDouble( "/tools/tweak/force", adj->value * 0.01 );
4045 }
4047 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4048 {
4049 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4050 prefs->setBool("/tools/tweak/usepressure", gtk_toggle_action_get_active(act));
4051 }
4053 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4054 {
4055 int mode = ege_select_one_action_get_active( act );
4056 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4057 prefs->setInt("/tools/tweak/mode", mode);
4059 GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
4060 GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
4061 GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
4062 GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
4063 GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
4064 GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
4065 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
4066 if (doh) gtk_action_set_sensitive (doh, TRUE);
4067 if (dos) gtk_action_set_sensitive (dos, TRUE);
4068 if (dol) gtk_action_set_sensitive (dol, TRUE);
4069 if (doo) gtk_action_set_sensitive (doo, TRUE);
4070 if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
4071 if (fid) gtk_action_set_sensitive (fid, FALSE);
4072 } else {
4073 if (doh) gtk_action_set_sensitive (doh, FALSE);
4074 if (dos) gtk_action_set_sensitive (dos, FALSE);
4075 if (dol) gtk_action_set_sensitive (dol, FALSE);
4076 if (doo) gtk_action_set_sensitive (doo, FALSE);
4077 if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
4078 if (fid) gtk_action_set_sensitive (fid, TRUE);
4079 }
4080 }
4082 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4083 {
4084 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4085 prefs->setDouble( "/tools/tweak/fidelity", adj->value * 0.01 );
4086 }
4088 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
4089 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4090 prefs->setBool("/tools/tweak/doh", gtk_toggle_action_get_active(act));
4091 }
4092 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
4093 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4094 prefs->setBool("/tools/tweak/dos", gtk_toggle_action_get_active(act));
4095 }
4096 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
4097 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4098 prefs->setBool("/tools/tweak/dol", gtk_toggle_action_get_active(act));
4099 }
4100 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
4101 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4102 prefs->setBool("/tools/tweak/doo", gtk_toggle_action_get_active(act));
4103 }
4105 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4106 {
4107 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
4108 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4110 {
4111 /* Width */
4112 gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
4113 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4114 EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
4115 _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
4116 "/tools/tweak/width", 15,
4117 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
4118 1, 100, 1.0, 0.0,
4119 labels, values, G_N_ELEMENTS(labels),
4120 sp_tweak_width_value_changed, 0.01, 0, 100 );
4121 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4122 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4123 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4124 }
4127 {
4128 /* Force */
4129 gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
4130 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4131 EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
4132 _("Force"), _("Force:"), _("The force of the tweak action"),
4133 "/tools/tweak/force", 20,
4134 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
4135 1, 100, 1.0, 0.0,
4136 labels, values, G_N_ELEMENTS(labels),
4137 sp_tweak_force_value_changed, 0.01, 0, 100 );
4138 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4139 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4140 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4141 }
4143 /* Mode */
4144 {
4145 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4147 GtkTreeIter iter;
4148 gtk_list_store_append( model, &iter );
4149 gtk_list_store_set( model, &iter,
4150 0, _("Move mode"),
4151 1, _("Move objects in any direction"),
4152 2, INKSCAPE_ICON_OBJECT_TWEAK_PUSH,
4153 -1 );
4155 gtk_list_store_append( model, &iter );
4156 gtk_list_store_set( model, &iter,
4157 0, _("Move in/out mode"),
4158 1, _("Move objects towards cursor; with Shift from cursor"),
4159 2, INKSCAPE_ICON_OBJECT_TWEAK_ATTRACT,
4160 -1 );
4162 gtk_list_store_append( model, &iter );
4163 gtk_list_store_set( model, &iter,
4164 0, _("Move jitter mode"),
4165 1, _("Move objects in random directions"),
4166 2, INKSCAPE_ICON_OBJECT_TWEAK_RANDOMIZE,
4167 -1 );
4169 gtk_list_store_append( model, &iter );
4170 gtk_list_store_set( model, &iter,
4171 0, _("Scale mode"),
4172 1, _("Shrink objects, with Shift enlarge"),
4173 2, INKSCAPE_ICON_OBJECT_TWEAK_SHRINK,
4174 -1 );
4176 gtk_list_store_append( model, &iter );
4177 gtk_list_store_set( model, &iter,
4178 0, _("Rotate mode"),
4179 1, _("Rotate objects, with Shift counterclockwise"),
4180 2, INKSCAPE_ICON_OBJECT_TWEAK_ROTATE,
4181 -1 );
4183 gtk_list_store_append( model, &iter );
4184 gtk_list_store_set( model, &iter,
4185 0, _("Duplicate/delete mode"),
4186 1, _("Duplicate objects, with Shift delete"),
4187 2, INKSCAPE_ICON_OBJECT_TWEAK_DUPLICATE,
4188 -1 );
4190 gtk_list_store_append( model, &iter );
4191 gtk_list_store_set( model, &iter,
4192 0, _("Push mode"),
4193 1, _("Push parts of paths in any direction"),
4194 2, INKSCAPE_ICON_PATH_TWEAK_PUSH,
4195 -1 );
4197 gtk_list_store_append( model, &iter );
4198 gtk_list_store_set( model, &iter,
4199 0, _("Shrink/grow mode"),
4200 1, _("Shrink (inset) parts of paths; with Shift grow (outset)"),
4201 2, INKSCAPE_ICON_PATH_TWEAK_SHRINK,
4202 -1 );
4204 gtk_list_store_append( model, &iter );
4205 gtk_list_store_set( model, &iter,
4206 0, _("Attract/repel mode"),
4207 1, _("Attract parts of paths towards cursor; with Shift from cursor"),
4208 2, INKSCAPE_ICON_PATH_TWEAK_ATTRACT,
4209 -1 );
4211 gtk_list_store_append( model, &iter );
4212 gtk_list_store_set( model, &iter,
4213 0, _("Roughen mode"),
4214 1, _("Roughen parts of paths"),
4215 2, INKSCAPE_ICON_PATH_TWEAK_ROUGHEN,
4216 -1 );
4218 gtk_list_store_append( model, &iter );
4219 gtk_list_store_set( model, &iter,
4220 0, _("Color paint mode"),
4221 1, _("Paint the tool's color upon selected objects"),
4222 2, INKSCAPE_ICON_OBJECT_TWEAK_PAINT,
4223 -1 );
4225 gtk_list_store_append( model, &iter );
4226 gtk_list_store_set( model, &iter,
4227 0, _("Color jitter mode"),
4228 1, _("Jitter the colors of selected objects"),
4229 2, INKSCAPE_ICON_OBJECT_TWEAK_JITTER_COLOR,
4230 -1 );
4232 gtk_list_store_append( model, &iter );
4233 gtk_list_store_set( model, &iter,
4234 0, _("Blur mode"),
4235 1, _("Blur selected objects more; with Shift, blur less"),
4236 2, INKSCAPE_ICON_OBJECT_TWEAK_BLUR,
4237 -1 );
4240 EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4241 g_object_set( act, "short_label", _("Mode:"), NULL );
4242 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4243 g_object_set_data( holder, "mode_action", act );
4245 ege_select_one_action_set_appearance( act, "full" );
4246 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4247 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4248 ege_select_one_action_set_icon_column( act, 2 );
4249 ege_select_one_action_set_icon_size( act, secondarySize );
4250 ege_select_one_action_set_tooltip_column( act, 1 );
4252 gint mode = prefs->getInt("/tools/tweak/mode", 0);
4253 ege_select_one_action_set_active( act, mode );
4254 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
4256 g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
4257 }
4259 guint mode = prefs->getInt("/tools/tweak/mode", 0);
4261 {
4262 EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
4263 ege_output_action_set_use_markup( act, TRUE );
4264 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4265 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4266 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4267 g_object_set_data( holder, "tweak_channels_label", act);
4268 }
4270 {
4271 InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
4272 _("Hue"),
4273 _("In color mode, act on objects' hue"),
4274 NULL,
4275 Inkscape::ICON_SIZE_DECORATION );
4276 //TRANSLATORS: "H" here stands for hue
4277 g_object_set( act, "short_label", _("H"), NULL );
4278 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4279 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
4280 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doh", true) );
4281 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4282 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4283 g_object_set_data( holder, "tweak_doh", act);
4284 }
4285 {
4286 InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
4287 _("Saturation"),
4288 _("In color mode, act on objects' saturation"),
4289 NULL,
4290 Inkscape::ICON_SIZE_DECORATION );
4291 //TRANSLATORS: "S" here stands for Saturation
4292 g_object_set( act, "short_label", _("S"), NULL );
4293 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4294 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
4295 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dos", true) );
4296 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4297 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4298 g_object_set_data( holder, "tweak_dos", act );
4299 }
4300 {
4301 InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
4302 _("Lightness"),
4303 _("In color mode, act on objects' lightness"),
4304 NULL,
4305 Inkscape::ICON_SIZE_DECORATION );
4306 //TRANSLATORS: "L" here stands for Lightness
4307 g_object_set( act, "short_label", _("L"), NULL );
4308 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4309 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
4310 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dol", true) );
4311 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4312 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4313 g_object_set_data( holder, "tweak_dol", act );
4314 }
4315 {
4316 InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
4317 _("Opacity"),
4318 _("In color mode, act on objects' opacity"),
4319 NULL,
4320 Inkscape::ICON_SIZE_DECORATION );
4321 //TRANSLATORS: "O" here stands for Opacity
4322 g_object_set( act, "short_label", _("O"), NULL );
4323 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4324 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
4325 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doo", true) );
4326 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4327 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4328 g_object_set_data( holder, "tweak_doo", act );
4329 }
4331 { /* Fidelity */
4332 gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
4333 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4334 EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
4335 _("Fidelity"), _("Fidelity:"),
4336 _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
4337 "/tools/tweak/fidelity", 50,
4338 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
4339 1, 100, 1.0, 10.0,
4340 labels, values, G_N_ELEMENTS(labels),
4341 sp_tweak_fidelity_value_changed, 0.01, 0, 100 );
4342 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4343 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4344 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
4345 gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
4346 g_object_set_data( holder, "tweak_fidelity", eact );
4347 }
4350 /* Use Pressure button */
4351 {
4352 InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
4353 _("Pressure"),
4354 _("Use the pressure of the input device to alter the force of tweak action"),
4355 "use_pressure",
4356 Inkscape::ICON_SIZE_DECORATION );
4357 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4358 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
4359 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/usepressure", true) );
4360 }
4362 }
4365 //########################
4366 //## Calligraphy ##
4367 //########################
4368 static void update_presets_list (GObject *tbl)
4369 {
4370 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4371 if (g_object_get_data(tbl, "presets_blocked"))
4372 return;
4374 EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4375 if (!sel) {
4376 // WTF!? This will cause a segfault if ever reached
4377 //ege_select_one_action_set_active(sel, 0);
4378 return;
4379 }
4381 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4383 int ege_index = 1;
4384 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++ege_index) {
4385 bool match = true;
4387 std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(*i);
4388 for (std::vector<Inkscape::Preferences::Entry>::iterator j = preset.begin(); j != preset.end(); ++j) {
4389 Glib::ustring entry_name = j->getEntryName();
4390 if (entry_name == "id" || entry_name == "name") continue;
4392 void *widget = g_object_get_data(tbl, entry_name.data());
4393 if (widget) {
4394 if (GTK_IS_ADJUSTMENT(widget)) {
4395 double v = j->getDouble();
4396 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4397 //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
4398 if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
4399 match = false;
4400 break;
4401 }
4402 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4403 bool v = j->getBool();
4404 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4405 //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
4406 if ( static_cast<bool>(gtk_toggle_action_get_active(toggle)) != v ) {
4407 match = false;
4408 break;
4409 }
4410 }
4411 }
4412 }
4414 if (match) {
4415 // newly added item is at the same index as the
4416 // save command, so we need to change twice for it to take effect
4417 ege_select_one_action_set_active(sel, 0);
4418 ege_select_one_action_set_active(sel, ege_index); // one-based index
4419 return;
4420 }
4421 }
4423 // no match found
4424 ege_select_one_action_set_active(sel, 0);
4425 }
4427 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
4428 {
4429 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4430 prefs->setDouble( "/tools/calligraphic/mass", adj->value * 0.01 );
4431 update_presets_list(tbl);
4432 }
4434 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
4435 {
4436 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4437 prefs->setDouble( "/tools/calligraphic/wiggle", adj->value * 0.01 );
4438 update_presets_list(tbl);
4439 }
4441 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
4442 {
4443 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4444 prefs->setDouble( "/tools/calligraphic/angle", adj->value );
4445 update_presets_list(tbl);
4446 }
4448 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
4449 {
4450 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4451 prefs->setDouble( "/tools/calligraphic/width", adj->value * 0.01 );
4452 update_presets_list(tbl);
4453 }
4455 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
4456 {
4457 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4458 prefs->setDouble("/tools/calligraphic/thinning", adj->value * 0.01 );
4459 update_presets_list(tbl);
4460 }
4462 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
4463 {
4464 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4465 prefs->setDouble( "/tools/calligraphic/flatness", adj->value * 0.01);
4466 update_presets_list(tbl);
4467 }
4469 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
4470 {
4471 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4472 prefs->setDouble( "/tools/calligraphic/tremor", adj->value * 0.01 );
4473 update_presets_list(tbl);
4474 }
4476 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
4477 {
4478 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4479 prefs->setDouble( "/tools/calligraphic/cap_rounding", adj->value );
4480 update_presets_list(tbl);
4481 }
4483 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject* tbl )
4484 {
4485 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4486 prefs->setBool("/tools/calligraphic/usepressure", gtk_toggle_action_get_active( act ));
4487 update_presets_list(tbl);
4488 }
4490 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject* tbl )
4491 {
4492 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4493 prefs->setBool("/tools/calligraphic/tracebackground", gtk_toggle_action_get_active( act ));
4494 update_presets_list(tbl);
4495 }
4497 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject* tbl )
4498 {
4499 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4500 GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
4501 prefs->setBool("/tools/calligraphic/usetilt", gtk_toggle_action_get_active( act ));
4502 update_presets_list(tbl);
4503 if (calligraphy_angle )
4504 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
4505 }
4508 static gchar const *const widget_names[] = {
4509 "width",
4510 "mass",
4511 "wiggle",
4512 "angle",
4513 "thinning",
4514 "tremor",
4515 "flatness",
4516 "cap_rounding",
4517 "usepressure",
4518 "tracebackground",
4519 "usetilt"
4520 };
4523 static void sp_dcc_build_presets_list(GObject *tbl)
4524 {
4525 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4527 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4528 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4529 gtk_list_store_clear (model);
4531 {
4532 GtkTreeIter iter;
4533 gtk_list_store_append( model, &iter );
4534 gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4535 }
4537 // iterate over all presets to populate the list
4538 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4539 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4540 int ii=1;
4542 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i) {
4543 GtkTreeIter iter;
4544 Glib::ustring preset_name = prefs->getString(*i + "/name");
4545 gtk_list_store_append( model, &iter );
4546 gtk_list_store_set( model, &iter, 0, preset_name.data(), 1, ii++, -1 );
4547 }
4549 {
4550 GtkTreeIter iter;
4551 gtk_list_store_append( model, &iter );
4552 gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4553 g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4554 }
4556 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4558 update_presets_list (tbl);
4559 }
4561 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4562 {
4563 using Inkscape::UI::Dialog::CalligraphicProfileRename;
4564 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4565 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4566 if (! desktop) return;
4568 if (g_object_get_data(tbl, "presets_blocked"))
4569 return;
4571 CalligraphicProfileRename::show(desktop);
4572 if ( !CalligraphicProfileRename::applied()) {
4573 // dialog cancelled
4574 update_presets_list (tbl);
4575 return;
4576 }
4577 Glib::ustring profile_name = CalligraphicProfileRename::getProfileName();
4579 if (profile_name.empty()) {
4580 // empty name entered
4581 update_presets_list (tbl);
4582 return;
4583 }
4585 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4587 // If there's a preset with the given name, find it and set save_path appropriately
4588 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4589 int total_presets = presets.size();
4590 int new_index = -1;
4591 Glib::ustring save_path; // profile pref path without a trailing slash
4593 int temp_index = 0;
4594 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++temp_index) {
4595 Glib::ustring name = prefs->getString(*i + "/name");
4596 if (!name.empty() && profile_name == name) {
4597 new_index = temp_index;
4598 save_path = *i;
4599 break;
4600 }
4601 }
4603 if (new_index == -1) {
4604 // no preset with this name, create
4605 new_index = total_presets + 1;
4606 gchar *profile_id = g_strdup_printf("/dcc%d", new_index);
4607 save_path = Glib::ustring("/tools/calligraphic/preset") + profile_id;
4608 g_free(profile_id);
4609 }
4611 for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4612 gchar const *const widget_name = widget_names[i];
4613 void *widget = g_object_get_data(tbl, widget_name);
4614 if (widget) {
4615 if (GTK_IS_ADJUSTMENT(widget)) {
4616 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4617 prefs->setDouble(save_path + "/" + widget_name, gtk_adjustment_get_value(adj));
4618 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4619 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4620 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4621 prefs->setBool(save_path + "/" + widget_name, gtk_toggle_action_get_active(toggle));
4622 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4623 } else {
4624 g_warning("Unknown widget type for preset: %s\n", widget_name);
4625 }
4626 } else {
4627 g_warning("Bad key when writing preset: %s\n", widget_name);
4628 }
4629 }
4630 prefs->setString(save_path + "/name", profile_name);
4632 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4633 sp_dcc_build_presets_list (tbl);
4634 }
4637 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4639 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4641 gint preset_index = ege_select_one_action_get_active( act );
4642 // This is necessary because EgeSelectOneAction spams us with GObject "changed" signal calls
4643 // even when the preset is not changed. It would be good to replace it with something more
4644 // modern. Index 0 means "No preset", so we don't do anything.
4645 if (preset_index == 0) return;
4647 gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4649 if (preset_index == save_presets_index) {
4650 // this is the Save command
4651 sp_dcc_save_profile(NULL, tbl);
4652 return;
4653 }
4655 if (g_object_get_data(tbl, "presets_blocked"))
4656 return;
4658 // preset_index is one-based so we subtract 1
4659 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4660 Glib::ustring preset_path = presets.at(preset_index - 1);
4662 if (!preset_path.empty()) {
4663 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
4665 std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(preset_path);
4667 // Shouldn't this be std::map?
4668 for (std::vector<Inkscape::Preferences::Entry>::iterator i = preset.begin(); i != preset.end(); ++i) {
4669 Glib::ustring entry_name = i->getEntryName();
4670 if (entry_name == "id" || entry_name == "name") continue;
4671 void *widget = g_object_get_data(tbl, entry_name.data());
4672 if (widget) {
4673 if (GTK_IS_ADJUSTMENT(widget)) {
4674 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4675 gtk_adjustment_set_value(adj, i->getDouble());
4676 //std::cout << "set adj " << attr_name << " to " << v << "\n";
4677 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4678 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4679 gtk_toggle_action_set_active(toggle, i->getBool());
4680 //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4681 } else {
4682 g_warning("Unknown widget type for preset: %s\n", entry_name.data());
4683 }
4684 } else {
4685 g_warning("Bad key found in a preset record: %s\n", entry_name.data());
4686 }
4687 }
4688 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4689 }
4690 }
4693 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4694 {
4695 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4696 {
4697 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4699 EgeAdjustmentAction* calligraphy_angle = 0;
4701 {
4702 /* Width */
4703 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4704 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4705 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4706 _("Pen Width"), _("Width:"),
4707 _("The width of the calligraphic pen (relative to the visible canvas area)"),
4708 "/tools/calligraphic/width", 15,
4709 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4710 1, 100, 1.0, 0.0,
4711 labels, values, G_N_ELEMENTS(labels),
4712 sp_ddc_width_value_changed, 0.01, 0, 100 );
4713 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4714 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4715 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4716 }
4718 {
4719 /* Thinning */
4720 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4721 gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4722 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4723 _("Stroke Thinning"), _("Thinning:"),
4724 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4725 "/tools/calligraphic/thinning", 10,
4726 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4727 -100, 100, 1, 0.1,
4728 labels, values, G_N_ELEMENTS(labels),
4729 sp_ddc_velthin_value_changed, 0.01, 0, 100);
4730 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4731 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4732 }
4734 {
4735 /* Angle */
4736 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4737 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4738 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4739 _("Pen Angle"), _("Angle:"),
4740 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4741 "/tools/calligraphic/angle", 30,
4742 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4743 -90.0, 90.0, 1.0, 10.0,
4744 labels, values, G_N_ELEMENTS(labels),
4745 sp_ddc_angle_value_changed, 1, 0 );
4746 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4747 g_object_set_data( holder, "angle_action", eact );
4748 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4749 calligraphy_angle = eact;
4750 }
4752 {
4753 /* Fixation */
4754 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4755 gdouble values[] = {0, 20, 40, 60, 90, 100};
4756 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4757 _("Fixation"), _("Fixation:"),
4758 _("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"),
4759 "/tools/calligraphic/flatness", 90,
4760 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4761 0.0, 100, 1.0, 10.0,
4762 labels, values, G_N_ELEMENTS(labels),
4763 sp_ddc_flatness_value_changed, 0.01, 0, 100 );
4764 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4765 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4766 }
4768 {
4769 /* Cap Rounding */
4770 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4771 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4772 // TRANSLATORS: "cap" means "end" (both start and finish) here
4773 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4774 _("Cap rounding"), _("Caps:"),
4775 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4776 "/tools/calligraphic/cap_rounding", 0.0,
4777 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4778 0.0, 5.0, 0.01, 0.1,
4779 labels, values, G_N_ELEMENTS(labels),
4780 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4781 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4782 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4783 }
4785 {
4786 /* Tremor */
4787 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4788 gdouble values[] = {0, 10, 20, 40, 60, 100};
4789 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4790 _("Stroke Tremor"), _("Tremor:"),
4791 _("Increase to make strokes rugged and trembling"),
4792 "/tools/calligraphic/tremor", 0.0,
4793 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4794 0.0, 100, 1, 0.0,
4795 labels, values, G_N_ELEMENTS(labels),
4796 sp_ddc_tremor_value_changed, 0.01, 0, 100 );
4798 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4799 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4800 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4801 }
4803 {
4804 /* Wiggle */
4805 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4806 gdouble values[] = {0, 20, 40, 60, 100};
4807 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4808 _("Pen Wiggle"), _("Wiggle:"),
4809 _("Increase to make the pen waver and wiggle"),
4810 "/tools/calligraphic/wiggle", 0.0,
4811 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4812 0.0, 100, 1, 0.0,
4813 labels, values, G_N_ELEMENTS(labels),
4814 sp_ddc_wiggle_value_changed, 0.01, 0, 100 );
4815 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4816 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4817 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4818 }
4820 {
4821 /* Mass */
4822 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4823 gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4824 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4825 _("Pen Mass"), _("Mass:"),
4826 _("Increase to make the pen drag behind, as if slowed by inertia"),
4827 "/tools/calligraphic/mass", 2.0,
4828 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4829 0.0, 100, 1, 0.0,
4830 labels, values, G_N_ELEMENTS(labels),
4831 sp_ddc_mass_value_changed, 0.01, 0, 100 );
4832 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4833 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4834 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4835 }
4838 /* Trace Background button */
4839 {
4840 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4841 _("Trace Background"),
4842 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4843 INKSCAPE_ICON_DRAW_TRACE_BACKGROUND,
4844 Inkscape::ICON_SIZE_DECORATION );
4845 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4846 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4847 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/tracebackground", false) );
4848 g_object_set_data( holder, "tracebackground", act );
4849 }
4851 /* Use Pressure button */
4852 {
4853 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4854 _("Pressure"),
4855 _("Use the pressure of the input device to alter the width of the pen"),
4856 INKSCAPE_ICON_DRAW_USE_PRESSURE,
4857 Inkscape::ICON_SIZE_DECORATION );
4858 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4859 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4860 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usepressure", true) );
4861 g_object_set_data( holder, "usepressure", act );
4862 }
4864 /* Use Tilt button */
4865 {
4866 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4867 _("Tilt"),
4868 _("Use the tilt of the input device to alter the angle of the pen's nib"),
4869 INKSCAPE_ICON_DRAW_USE_TILT,
4870 Inkscape::ICON_SIZE_DECORATION );
4871 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4872 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4873 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs->getBool("/tools/calligraphic/usetilt", true) );
4874 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usetilt", true) );
4875 g_object_set_data( holder, "usetilt", act );
4876 }
4878 /*calligraphic profile */
4879 {
4880 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
4881 EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
4882 ege_select_one_action_set_appearance (act1, "compact");
4883 g_object_set_data (holder, "profile_selector", act1 );
4885 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
4887 sp_dcc_build_presets_list (holder);
4889 g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
4890 gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
4891 }
4892 }
4893 }
4896 //########################
4897 //## Circle / Arc ##
4898 //########################
4900 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4901 {
4902 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4903 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4905 if (v1 == 0 && v2 == 0) {
4906 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4907 gtk_action_set_sensitive( ocb, FALSE );
4908 gtk_action_set_sensitive( make_whole, FALSE );
4909 }
4910 } else {
4911 gtk_action_set_sensitive( ocb, TRUE );
4912 gtk_action_set_sensitive( make_whole, TRUE );
4913 }
4914 }
4916 static void
4917 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4918 {
4919 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4921 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4922 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4923 prefs->setDouble(Glib::ustring("/tools/shapes/arc") + value_name, (adj->value * M_PI)/ 180);
4924 }
4926 // quit if run by the attr_changed listener
4927 if (g_object_get_data( tbl, "freeze" )) {
4928 return;
4929 }
4931 // in turn, prevent listener from responding
4932 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4934 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4936 bool modmade = false;
4937 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4938 items != NULL;
4939 items = items->next)
4940 {
4941 SPItem *item = SP_ITEM(items->data);
4943 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4945 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4946 SPArc *arc = SP_ARC(item);
4948 if (!strcmp(value_name, "start"))
4949 ge->start = (adj->value * M_PI)/ 180;
4950 else
4951 ge->end = (adj->value * M_PI)/ 180;
4953 sp_genericellipse_normalize(ge);
4954 ((SPObject *)arc)->updateRepr();
4955 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4957 modmade = true;
4958 }
4959 }
4961 g_free(namespaced_name);
4963 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4965 sp_arctb_sensitivize( tbl, adj->value, other->value );
4967 if (modmade) {
4968 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4969 _("Arc: Change start/end"));
4970 }
4972 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4973 }
4976 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
4977 {
4978 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
4979 }
4981 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4982 {
4983 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
4984 }
4987 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4988 {
4989 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4990 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4991 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4992 prefs->setBool("/tools/shapes/arc/open", ege_select_one_action_get_active(act) != 0);
4993 }
4995 // quit if run by the attr_changed listener
4996 if (g_object_get_data( tbl, "freeze" )) {
4997 return;
4998 }
5000 // in turn, prevent listener from responding
5001 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5003 bool modmade = false;
5005 if ( ege_select_one_action_get_active(act) != 0 ) {
5006 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5007 items != NULL;
5008 items = items->next)
5009 {
5010 if (SP_IS_ARC((SPItem *) items->data)) {
5011 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5012 repr->setAttribute("sodipodi:open", "true");
5013 SP_OBJECT((SPItem *) items->data)->updateRepr();
5014 modmade = true;
5015 }
5016 }
5017 } else {
5018 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5019 items != NULL;
5020 items = items->next)
5021 {
5022 if (SP_IS_ARC((SPItem *) items->data)) {
5023 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5024 repr->setAttribute("sodipodi:open", NULL);
5025 SP_OBJECT((SPItem *) items->data)->updateRepr();
5026 modmade = true;
5027 }
5028 }
5029 }
5031 if (modmade) {
5032 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
5033 _("Arc: Change open/closed"));
5034 }
5036 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5037 }
5039 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
5040 {
5041 GtkAdjustment *adj;
5042 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
5043 gtk_adjustment_set_value(adj, 0.0);
5044 gtk_adjustment_value_changed(adj);
5046 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
5047 gtk_adjustment_set_value(adj, 0.0);
5048 gtk_adjustment_value_changed(adj);
5050 spinbutton_defocus( GTK_OBJECT(obj) );
5051 }
5053 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
5054 gchar const */*old_value*/, gchar const */*new_value*/,
5055 bool /*is_interactive*/, gpointer data)
5056 {
5057 GObject *tbl = G_OBJECT(data);
5059 // quit if run by the _changed callbacks
5060 if (g_object_get_data( tbl, "freeze" )) {
5061 return;
5062 }
5064 // in turn, prevent callbacks from responding
5065 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5067 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
5068 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
5070 GtkAdjustment *adj1,*adj2;
5071 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
5072 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
5073 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
5074 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
5076 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
5078 char const *openstr = NULL;
5079 openstr = repr->attribute("sodipodi:open");
5080 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
5082 if (openstr) {
5083 ege_select_one_action_set_active( ocb, 1 );
5084 } else {
5085 ege_select_one_action_set_active( ocb, 0 );
5086 }
5088 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5089 }
5091 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
5092 NULL, /* child_added */
5093 NULL, /* child_removed */
5094 arc_tb_event_attr_changed,
5095 NULL, /* content_changed */
5096 NULL /* order_changed */
5097 };
5100 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
5101 {
5102 int n_selected = 0;
5103 Inkscape::XML::Node *repr = NULL;
5105 purge_repr_listener( tbl, tbl );
5107 for (GSList const *items = selection->itemList();
5108 items != NULL;
5109 items = items->next)
5110 {
5111 if (SP_IS_ARC((SPItem *) items->data)) {
5112 n_selected++;
5113 repr = SP_OBJECT_REPR((SPItem *) items->data);
5114 }
5115 }
5117 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
5119 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
5120 if (n_selected == 0) {
5121 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
5122 } else if (n_selected == 1) {
5123 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
5124 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5126 if (repr) {
5127 g_object_set_data( tbl, "repr", repr );
5128 Inkscape::GC::anchor(repr);
5129 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
5130 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
5131 }
5132 } else {
5133 // FIXME: implement averaging of all parameters for multiple selected
5134 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
5135 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5136 sp_arctb_sensitivize( tbl, 1, 0 );
5137 }
5138 }
5141 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5142 {
5143 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5145 EgeAdjustmentAction* eact = 0;
5146 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
5149 {
5150 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
5151 ege_output_action_set_use_markup( act, TRUE );
5152 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5153 g_object_set_data( holder, "mode_action", act );
5154 }
5156 /* Start */
5157 {
5158 eact = create_adjustment_action( "ArcStartAction",
5159 _("Start"), _("Start:"),
5160 _("The angle (in degrees) from the horizontal to the arc's start point"),
5161 "/tools/shapes/arc/start", 0.0,
5162 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
5163 -360.0, 360.0, 1.0, 10.0,
5164 0, 0, 0,
5165 sp_arctb_start_value_changed);
5166 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5167 }
5169 /* End */
5170 {
5171 eact = create_adjustment_action( "ArcEndAction",
5172 _("End"), _("End:"),
5173 _("The angle (in degrees) from the horizontal to the arc's end point"),
5174 "/tools/shapes/arc/end", 0.0,
5175 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
5176 -360.0, 360.0, 1.0, 10.0,
5177 0, 0, 0,
5178 sp_arctb_end_value_changed);
5179 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5180 }
5182 /* Segments / Pie checkbox */
5183 {
5184 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5186 GtkTreeIter iter;
5187 gtk_list_store_append( model, &iter );
5188 gtk_list_store_set( model, &iter,
5189 0, _("Closed arc"),
5190 1, _("Switch to segment (closed shape with two radii)"),
5191 2, INKSCAPE_ICON_DRAW_ELLIPSE_SEGMENT,
5192 -1 );
5194 gtk_list_store_append( model, &iter );
5195 gtk_list_store_set( model, &iter,
5196 0, _("Open Arc"),
5197 1, _("Switch to arc (unclosed shape)"),
5198 2, INKSCAPE_ICON_DRAW_ELLIPSE_ARC,
5199 -1 );
5201 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5202 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5203 g_object_set_data( holder, "open_action", act );
5205 ege_select_one_action_set_appearance( act, "full" );
5206 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5207 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5208 ege_select_one_action_set_icon_column( act, 2 );
5209 ege_select_one_action_set_icon_size( act, secondarySize );
5210 ege_select_one_action_set_tooltip_column( act, 1 );
5212 bool isClosed = !prefs->getBool("/tools/shapes/arc/open", false);
5213 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
5214 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
5215 }
5217 /* Make Whole */
5218 {
5219 InkAction* inky = ink_action_new( "ArcResetAction",
5220 _("Make whole"),
5221 _("Make the shape a whole ellipse, not arc or segment"),
5222 INKSCAPE_ICON_DRAW_ELLIPSE_WHOLE,
5223 secondarySize );
5224 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
5225 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5226 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
5227 g_object_set_data( holder, "make_whole", inky );
5228 }
5230 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
5231 // sensitivize make whole and open checkbox
5232 {
5233 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
5234 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
5235 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
5236 }
5239 sigc::connection *connection = new sigc::connection(
5240 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
5241 );
5242 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
5243 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
5244 }
5249 // toggle button callbacks and updaters
5251 //########################
5252 //## Dropper ##
5253 //########################
5255 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
5256 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5257 prefs->setInt( "/tools/dropper/pick", gtk_toggle_action_get_active( act ) );
5258 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
5259 if ( set_action ) {
5260 if ( gtk_toggle_action_get_active( act ) ) {
5261 gtk_action_set_sensitive( set_action, TRUE );
5262 } else {
5263 gtk_action_set_sensitive( set_action, FALSE );
5264 }
5265 }
5267 spinbutton_defocus(GTK_OBJECT(tbl));
5268 }
5270 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
5271 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5272 prefs->setBool( "/tools/dropper/setalpha", gtk_toggle_action_get_active( act ) );
5273 spinbutton_defocus(GTK_OBJECT(tbl));
5274 }
5277 /**
5278 * Dropper auxiliary toolbar construction and setup.
5279 *
5280 * TODO: Would like to add swatch of current color.
5281 * TODO: Add queue of last 5 or so colors selected with new swatches so that
5282 * can drag and drop places. Will provide a nice mixing palette.
5283 */
5284 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
5285 {
5286 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5287 gint pickAlpha = prefs->getInt( "/tools/dropper/pick", 1 );
5289 {
5290 EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
5291 ege_output_action_set_use_markup( act, TRUE );
5292 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5293 }
5295 {
5296 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
5297 _("Pick opacity"),
5298 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
5299 NULL,
5300 Inkscape::ICON_SIZE_DECORATION );
5301 g_object_set( act, "short_label", _("Pick"), NULL );
5302 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5303 g_object_set_data( holder, "pick_action", act );
5304 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
5305 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
5306 }
5308 {
5309 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
5310 _("Assign opacity"),
5311 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
5312 NULL,
5313 Inkscape::ICON_SIZE_DECORATION );
5314 g_object_set( act, "short_label", _("Assign"), NULL );
5315 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5316 g_object_set_data( holder, "set_action", act );
5317 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/dropper/setalpha", true) );
5318 // make sure it's disabled if we're not picking alpha
5319 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
5320 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
5321 }
5322 }
5325 //########################
5326 //## LPETool ##
5327 //########################
5329 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
5331 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
5332 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
5333 {
5334 using namespace Inkscape::LivePathEffect;
5336 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
5337 SPEventContext *ec = desktop->event_context;
5338 if (!SP_IS_LPETOOL_CONTEXT(ec)) {
5339 return;
5340 }
5342 // only take action if run by the attr_changed listener
5343 if (!g_object_get_data(tbl, "freeze")) {
5344 // in turn, prevent listener from responding
5345 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5347 gint mode = ege_select_one_action_get_active(act);
5348 EffectType type = lpesubtools[mode];
5350 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5351 bool success = lpetool_try_construction(lc, type);
5352 if (success) {
5353 // since the construction was already performed, we set the state back to inactive
5354 ege_select_one_action_set_active(act, 0);
5355 mode = 0;
5356 } else {
5357 // switch to the chosen subtool
5358 SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
5359 }
5361 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5362 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5363 prefs->setInt( "/tools/lpetool/mode", mode );
5364 }
5366 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
5367 }
5368 }
5370 void sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject */*tbl*/)
5371 {
5372 SPEventContext *ec = selection->desktop()->event_context;
5373 if (!SP_IS_LPETOOL_CONTEXT(ec))
5374 return;
5376 lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
5377 }
5379 void
5380 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
5381 {
5382 using namespace Inkscape::LivePathEffect;
5383 SPEventContext *ec = selection->desktop()->event_context;
5384 if (!SP_IS_LPETOOL_CONTEXT(ec))
5385 return;
5386 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
5388 lpetool_delete_measuring_items(lc);
5389 lpetool_create_measuring_items(lc, selection);
5391 // activate line segment combo box if a single item with LPELineSegment is selected
5392 GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
5393 SPItem *item = selection->singleItem();
5394 if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
5395 SPLPEItem *lpeitem = SP_LPE_ITEM(item);
5396 Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
5397 if (lpe && lpe->effectType() == LINE_SEGMENT) {
5398 LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
5399 g_object_set_data(tbl, "currentlpe", lpe);
5400 g_object_set_data(tbl, "currentlpeitem", lpeitem);
5401 gtk_action_set_sensitive(w, TRUE);
5402 ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
5403 } else {
5404 g_object_set_data(tbl, "currentlpe", NULL);
5405 g_object_set_data(tbl, "currentlpeitem", NULL);
5406 gtk_action_set_sensitive(w, FALSE);
5407 }
5408 } else {
5409 g_object_set_data(tbl, "currentlpe", NULL);
5410 g_object_set_data(tbl, "currentlpeitem", NULL);
5411 gtk_action_set_sensitive(w, FALSE);
5412 }
5413 }
5415 static void
5416 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
5417 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5418 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5420 bool show = gtk_toggle_action_get_active( act );
5421 prefs->setBool("/tools/lpetool/show_bbox", show);
5423 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5424 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5425 lpetool_context_reset_limiting_bbox(lc);
5426 }
5427 }
5429 static void
5430 lpetool_toggle_show_measuring_info (GtkToggleAction *act, GObject *tbl) {
5431 SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
5432 if (!tools_isactive(desktop, TOOLS_LPETOOL))
5433 return;
5435 GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
5436 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5437 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5438 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5439 bool show = gtk_toggle_action_get_active( act );
5440 prefs->setBool("/tools/lpetool/show_measuring_info", show);
5441 lpetool_show_measuring_info(lc, show);
5442 gtk_action_set_sensitive(GTK_ACTION(unitact), show);
5443 }
5444 }
5446 static void lpetool_unit_changed(GtkAction* /*act*/, GObject* tbl) {
5447 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
5448 SPUnit const *unit = tracker->getActiveUnit();
5449 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5450 prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5452 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5453 if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
5454 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5455 lpetool_delete_measuring_items(lc);
5456 lpetool_create_measuring_items(lc);
5457 }
5458 }
5460 static void
5461 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
5462 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5463 Inkscape::Selection *selection = desktop->selection;
5465 Geom::OptRect bbox = selection->bounds();
5467 if (bbox) {
5468 Geom::Point A(bbox->min());
5469 Geom::Point B(bbox->max());
5471 A *= desktop->doc2dt();
5472 B *= desktop->doc2dt();
5474 // TODO: should we provide a way to store points in prefs?
5475 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5476 prefs->setDouble("/tools/lpetool/bbox_upperleftx", A[Geom::X]);
5477 prefs->setDouble("/tools/lpetool/bbox_upperlefty", A[Geom::Y]);
5478 prefs->setDouble("/tools/lpetool/bbox_lowerrightx", B[Geom::X]);
5479 prefs->setDouble("/tools/lpetool/bbox_lowerrighty", B[Geom::Y]);
5481 lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
5482 }
5484 gtk_toggle_action_set_active(act, false);
5485 }
5487 static void
5488 sp_line_segment_build_list(GObject *tbl)
5489 {
5490 g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
5492 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
5493 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
5494 gtk_list_store_clear (model);
5496 // TODO: we add the entries of rht combo box manually; later this should be done automatically
5497 {
5498 GtkTreeIter iter;
5499 gtk_list_store_append( model, &iter );
5500 gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
5501 gtk_list_store_append( model, &iter );
5502 gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
5503 gtk_list_store_append( model, &iter );
5504 gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
5505 gtk_list_store_append( model, &iter );
5506 gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
5507 }
5509 g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5510 }
5512 static void
5513 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
5514 using namespace Inkscape::LivePathEffect;
5516 // quit if run by the attr_changed listener
5517 if (g_object_get_data(tbl, "freeze")) {
5518 return;
5519 }
5521 // in turn, prevent listener from responding
5522 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5524 LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
5525 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5526 if (lpeitem) {
5527 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5528 lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
5529 sp_lpe_item_update_patheffect(lpeitem, true, true);
5530 }
5532 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5533 }
5535 static void
5536 lpetool_open_lpe_dialog (GtkToggleAction *act, gpointer data) {
5537 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5539 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5540 sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5541 }
5542 gtk_toggle_action_set_active(act, false);
5543 }
5545 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5546 {
5547 UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5548 tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5549 g_object_set_data(holder, "tracker", tracker);
5550 SPUnit const *unit = tracker->getActiveUnit();
5552 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5553 prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5555 /** Automatically create a list of LPEs that get added to the toolbar **/
5556 {
5557 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5559 GtkTreeIter iter;
5561 // the first toggle button represents the state that no subtool is active (remove this when
5562 // this can be modeled by EgeSelectOneAction or some other action)
5563 gtk_list_store_append( model, &iter );
5564 gtk_list_store_set( model, &iter,
5565 0, _("All inactive"),
5566 1, _("No geometric tool is active"),
5567 2, _("all_inactive"),
5568 -1 );
5570 Inkscape::LivePathEffect::EffectType type;
5571 for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5572 type = lpesubtools[i];
5573 gtk_list_store_append( model, &iter );
5574 gtk_list_store_set( model, &iter,
5575 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5576 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5577 2, Inkscape::LivePathEffect::LPETypeConverter.get_key(type).c_str(),
5578 -1 );
5579 }
5581 EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5582 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5583 g_object_set_data( holder, "lpetool_mode_action", act );
5585 ege_select_one_action_set_appearance( act, "full" );
5586 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5587 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5588 ege_select_one_action_set_icon_column( act, 2 );
5589 ege_select_one_action_set_tooltip_column( act, 1 );
5591 gint lpeToolMode = prefs->getInt("/tools/lpetool/mode", 0);
5592 ege_select_one_action_set_active( act, lpeToolMode );
5593 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5594 }
5596 /* Show limiting bounding box */
5597 {
5598 InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5599 _("Show limiting bounding box"),
5600 _("Show bounding box (used to cut infinite lines)"),
5601 "lpetool_show_bbox",
5602 Inkscape::ICON_SIZE_DECORATION );
5603 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5604 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5605 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_bbox", true ) );
5606 }
5608 /* Set limiting bounding box to bbox of current selection */
5609 {
5610 InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5611 _("Get limiting bounding box from selection"),
5612 _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5613 "lpetool_set_bbox",
5614 Inkscape::ICON_SIZE_DECORATION );
5615 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5616 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5617 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5618 }
5621 /* Combo box to choose line segment type */
5622 {
5623 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5624 EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5625 ege_select_one_action_set_appearance (act, "compact");
5626 g_object_set_data (holder, "lpetool_line_segment_action", act );
5628 g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5630 sp_line_segment_build_list (holder);
5632 g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5633 gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5634 gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5635 }
5637 /* Display measuring info for selected items */
5638 {
5639 InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5640 _("Display measuring info"),
5641 _("Display measuring info for selected items"),
5642 "lpetool_measuring_info",
5643 Inkscape::ICON_SIZE_DECORATION );
5644 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5645 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
5646 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_measuring_info", true ) );
5647 }
5649 // add the units menu
5650 {
5651 GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5652 gtk_action_group_add_action( mainActions, act );
5653 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5654 g_object_set_data(holder, "lpetool_units_action", act);
5655 gtk_action_set_sensitive(act, prefs->getBool("/tools/lpetool/show_measuring_info", true));
5656 }
5658 /* Open LPE dialog (to adapt parameters numerically) */
5659 {
5660 InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
5661 _("Open LPE dialog"),
5662 _("Open LPE dialog (to adapt parameters numerically)"),
5663 "lpetool_open_lpe_dialog",
5664 Inkscape::ICON_SIZE_DECORATION );
5665 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5666 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
5667 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5668 }
5670 //watch selection
5671 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5673 sigc::connection *c_selection_modified =
5674 new sigc::connection (sp_desktop_selection (desktop)->connectModified
5675 (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
5676 pool->add_connection ("selection-modified", c_selection_modified);
5678 sigc::connection *c_selection_changed =
5679 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5680 (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5681 pool->add_connection ("selection-changed", c_selection_changed);
5682 }
5684 //########################
5685 //## Eraser ##
5686 //########################
5688 static void sp_erc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
5689 {
5690 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5691 prefs->setDouble( "/tools/eraser/width", adj->value * 0.01 );
5692 update_presets_list(tbl);
5693 }
5695 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5696 {
5697 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5698 bool eraserMode = ege_select_one_action_get_active( act ) != 0;
5699 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5700 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5701 prefs->setBool( "/tools/eraser/mode", eraserMode );
5702 }
5704 // only take action if run by the attr_changed listener
5705 if (!g_object_get_data( tbl, "freeze" )) {
5706 // in turn, prevent listener from responding
5707 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5709 if ( eraserMode != 0 ) {
5710 } else {
5711 }
5712 // TODO finish implementation
5714 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5715 }
5716 }
5718 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5719 {
5720 {
5721 /* Width */
5722 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5723 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5724 EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5725 _("Pen Width"), _("Width:"),
5726 _("The width of the eraser pen (relative to the visible canvas area)"),
5727 "/tools/eraser/width", 15,
5728 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5729 1, 100, 1.0, 0.0,
5730 labels, values, G_N_ELEMENTS(labels),
5731 sp_erc_width_value_changed, 0.01, 0, 100 );
5732 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5733 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5734 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5735 }
5737 {
5738 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5740 GtkTreeIter iter;
5741 gtk_list_store_append( model, &iter );
5742 gtk_list_store_set( model, &iter,
5743 0, _("Delete"),
5744 1, _("Delete objects touched by the eraser"),
5745 2, INKSCAPE_ICON_DRAW_ERASER_DELETE_OBJECTS,
5746 -1 );
5748 gtk_list_store_append( model, &iter );
5749 gtk_list_store_set( model, &iter,
5750 0, _("Cut"),
5751 1, _("Cut out from objects"),
5752 2, INKSCAPE_ICON_PATH_DIFFERENCE,
5753 -1 );
5755 EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5756 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5757 g_object_set_data( holder, "eraser_mode_action", act );
5759 ege_select_one_action_set_appearance( act, "full" );
5760 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5761 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5762 ege_select_one_action_set_icon_column( act, 2 );
5763 ege_select_one_action_set_tooltip_column( act, 1 );
5765 /// @todo Convert to boolean?
5766 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5767 gint eraserMode = prefs->getBool("/tools/eraser/mode") ? 1 : 0;
5768 ege_select_one_action_set_active( act, eraserMode );
5769 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
5770 }
5772 }
5774 //########################
5775 //## Text Toolbox ##
5776 //########################
5777 /*
5778 static void
5779 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
5780 {
5781 //Call back for letter sizing spinbutton
5782 }
5784 static void
5785 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
5786 {
5787 //Call back for line height spinbutton
5788 }
5790 static void
5791 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5792 {
5793 //Call back for horizontal kerning spinbutton
5794 }
5796 static void
5797 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5798 {
5799 //Call back for vertical kerning spinbutton
5800 }
5802 static void
5803 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
5804 {
5805 //Call back for letter rotation spinbutton
5806 }*/
5808 namespace {
5810 bool popdown_visible = false;
5811 bool popdown_hasfocus = false;
5813 void
5814 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
5815 {
5816 SPStyle *query =
5817 sp_style_new (SP_ACTIVE_DOCUMENT);
5819 // int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5821 int result_family =
5822 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5824 int result_style =
5825 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5827 int result_numbers =
5828 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5830 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5832 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5833 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
5834 // there are no texts in selection, read from prefs
5836 sp_style_read_from_prefs(query, "/tools/text");
5838 if (g_object_get_data(tbl, "text_style_from_prefs")) {
5839 // do not reset the toolbar style from prefs if we already did it last time
5840 sp_style_unref(query);
5841 return;
5842 }
5843 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
5844 } else {
5845 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
5846 }
5848 if (query->text)
5849 {
5850 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
5851 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5852 gtk_entry_set_text (GTK_ENTRY (entry), "");
5854 } else if (query->text->font_specification.value || query->text->font_family.value) {
5856 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5858 // Get the font that corresponds
5859 Glib::ustring familyName;
5861 font_instance * font = font_factory::Default()->FaceFromStyle(query);
5862 if (font) {
5863 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
5864 font->Unref();
5865 font = NULL;
5866 }
5868 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
5870 Gtk::TreePath path;
5871 try {
5872 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
5873 } catch (...) {
5874 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
5875 sp_style_unref(query);
5876 return;
5877 }
5879 GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5880 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5882 g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
5884 gtk_tree_selection_select_path (tselection, path.gobj());
5885 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5887 g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
5888 }
5890 //Size
5891 {
5892 GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
5893 gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
5894 g_object_set_data(tbl, "size-block", gpointer(1));
5895 gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
5896 g_object_set_data(tbl, "size-block", gpointer(0));
5897 g_free(str);
5898 }
5900 //Anchor
5901 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
5902 {
5903 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
5904 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5905 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5906 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5907 }
5908 else
5909 {
5910 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
5911 {
5912 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
5913 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5914 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5915 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5916 }
5917 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
5918 {
5919 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
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_END)
5925 {
5926 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
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 }
5933 //Style
5934 {
5935 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
5937 gboolean active = gtk_toggle_button_get_active (button);
5938 gboolean check = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
5940 if (active != check)
5941 {
5942 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5943 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5944 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5945 }
5946 }
5948 {
5949 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
5951 gboolean active = gtk_toggle_button_get_active (button);
5952 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
5954 if (active != check)
5955 {
5956 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5957 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5958 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5959 }
5960 }
5962 //Orientation
5963 //locking both buttons, changing one affect all group (both)
5964 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
5965 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5967 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
5968 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
5970 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
5971 {
5972 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5973 }
5974 else
5975 {
5976 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
5977 }
5978 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5979 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
5980 }
5982 sp_style_unref(query);
5983 }
5985 void
5986 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
5987 {
5988 sp_text_toolbox_selection_changed (selection, tbl);
5989 }
5991 void
5992 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
5993 {
5994 sp_text_toolbox_selection_changed (NULL, tbl);
5995 }
5997 void
5998 sp_text_toolbox_family_changed (GtkTreeSelection *selection,
5999 GObject *tbl)
6000 {
6001 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6002 GtkTreeModel *model = 0;
6003 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6004 GtkTreeIter iter;
6005 char *family = 0;
6007 gdk_pointer_ungrab (GDK_CURRENT_TIME);
6008 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
6010 if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
6011 return;
6012 }
6014 gtk_tree_model_get (model, &iter, 0, &family, -1);
6016 if (g_object_get_data (G_OBJECT (selection), "block"))
6017 {
6018 gtk_entry_set_text (GTK_ENTRY (entry), family);
6019 return;
6020 }
6022 gtk_entry_set_text (GTK_ENTRY (entry), family);
6024 SPStyle *query =
6025 sp_style_new (SP_ACTIVE_DOCUMENT);
6027 int result_fontspec =
6028 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6030 //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6032 SPCSSAttr *css = sp_repr_css_attr_new ();
6035 // First try to get the font spec from the stored value
6036 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
6038 if (fontSpec.empty()) {
6039 // Construct a new font specification if it does not yet exist
6040 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6041 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6042 fontFromStyle->Unref();
6043 }
6045 if (!fontSpec.empty()) {
6046 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
6047 if (!newFontSpec.empty() && fontSpec != newFontSpec) {
6048 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
6049 if (font) {
6050 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6052 // Set all the these just in case they were altered when finding the best
6053 // match for the new family and old style...
6055 gchar c[256];
6057 font->Family(c, 256);
6058 sp_repr_css_set_property (css, "font-family", c);
6060 font->Attribute( "weight", c, 256);
6061 sp_repr_css_set_property (css, "font-weight", c);
6063 font->Attribute("style", c, 256);
6064 sp_repr_css_set_property (css, "font-style", c);
6066 font->Attribute("stretch", c, 256);
6067 sp_repr_css_set_property (css, "font-stretch", c);
6069 font->Attribute("variant", c, 256);
6070 sp_repr_css_set_property (css, "font-variant", c);
6072 font->Unref();
6073 }
6074 }
6075 }
6077 // If querying returned nothing, set the default style of the tool (for new texts)
6078 if (result_fontspec == QUERY_STYLE_NOTHING)
6079 {
6080 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6081 prefs->setStyle("/tools/text/style", css);
6082 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
6083 }
6084 else
6085 {
6086 sp_desktop_set_style (desktop, css, true, true);
6087 }
6089 sp_style_unref(query);
6091 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6092 _("Text: Change font family"));
6093 sp_repr_css_attr_unref (css);
6094 g_free(family);
6095 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6097 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6098 }
6100 /* This is where execution comes when the contents of the font family box have been completed
6101 by the press of the return key */
6102 void
6103 sp_text_toolbox_family_entry_activate (GtkEntry *entry,
6104 GObject *tbl)
6105 {
6106 const char *family = gtk_entry_get_text (entry); // Fetch the requested font family
6108 // Try to match that to a known font. If not, then leave current font alone and remain focused on text box
6109 try {
6110 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
6111 GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
6112 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
6113 gtk_tree_selection_select_path (selection, path.gobj());
6114 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
6115 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6116 } catch (...) {
6117 if (family && strlen (family))
6118 {
6119 gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6120 }
6121 }
6122 }
6124 void
6125 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
6126 gpointer data)
6127 {
6128 if (g_object_get_data (G_OBJECT (button), "block")) return;
6129 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
6130 int prop = GPOINTER_TO_INT(data);
6132 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6133 SPCSSAttr *css = sp_repr_css_attr_new ();
6135 switch (prop)
6136 {
6137 case 0:
6138 {
6139 sp_repr_css_set_property (css, "text-anchor", "start");
6140 sp_repr_css_set_property (css, "text-align", "start");
6141 break;
6142 }
6143 case 1:
6144 {
6145 sp_repr_css_set_property (css, "text-anchor", "middle");
6146 sp_repr_css_set_property (css, "text-align", "center");
6147 break;
6148 }
6150 case 2:
6151 {
6152 sp_repr_css_set_property (css, "text-anchor", "end");
6153 sp_repr_css_set_property (css, "text-align", "end");
6154 break;
6155 }
6157 case 3:
6158 {
6159 sp_repr_css_set_property (css, "text-anchor", "start");
6160 sp_repr_css_set_property (css, "text-align", "justify");
6161 break;
6162 }
6163 }
6165 SPStyle *query =
6166 sp_style_new (SP_ACTIVE_DOCUMENT);
6167 int result_numbers =
6168 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6170 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6171 if (result_numbers == QUERY_STYLE_NOTHING)
6172 {
6173 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6174 prefs->setStyle("/tools/text/style", css);
6175 }
6177 sp_style_unref(query);
6179 sp_desktop_set_style (desktop, css, true, true);
6180 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6181 _("Text: Change alignment"));
6182 sp_repr_css_attr_unref (css);
6184 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6185 }
6187 void
6188 sp_text_toolbox_style_toggled (GtkToggleButton *button,
6189 gpointer data)
6190 {
6191 if (g_object_get_data (G_OBJECT (button), "block")) return;
6193 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6194 SPCSSAttr *css = sp_repr_css_attr_new ();
6195 int prop = GPOINTER_TO_INT(data);
6196 bool active = gtk_toggle_button_get_active (button);
6198 SPStyle *query =
6199 sp_style_new (SP_ACTIVE_DOCUMENT);
6201 int result_fontspec =
6202 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6204 //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6205 //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6206 //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6208 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
6209 Glib::ustring newFontSpec = "";
6211 if (fontSpec.empty()) {
6212 // Construct a new font specification if it does not yet exist
6213 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6214 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6215 fontFromStyle->Unref();
6216 }
6218 switch (prop)
6219 {
6220 case 0:
6221 {
6222 if (!fontSpec.empty()) {
6223 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
6224 }
6225 if (fontSpec != newFontSpec) {
6226 // Don't even set the bold if the font didn't exist on the system
6227 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
6228 }
6229 break;
6230 }
6232 case 1:
6233 {
6234 if (!fontSpec.empty()) {
6235 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
6236 }
6237 if (fontSpec != newFontSpec) {
6238 // Don't even set the italic if the font didn't exist on the system
6239 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
6240 }
6241 break;
6242 }
6243 }
6245 if (!newFontSpec.empty()) {
6246 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6247 }
6249 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6250 if (result_fontspec == QUERY_STYLE_NOTHING)
6251 {
6252 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6253 prefs->setStyle("/tools/text/style", css);
6254 }
6256 sp_style_unref(query);
6258 sp_desktop_set_style (desktop, css, true, true);
6259 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6260 _("Text: Change font style"));
6261 sp_repr_css_attr_unref (css);
6263 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6264 }
6266 void
6267 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
6268 gpointer data)
6269 {
6270 if (g_object_get_data (G_OBJECT (button), "block")) {
6271 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6272 return;
6273 }
6275 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6276 SPCSSAttr *css = sp_repr_css_attr_new ();
6277 int prop = GPOINTER_TO_INT(data);
6279 switch (prop)
6280 {
6281 case 0:
6282 {
6283 sp_repr_css_set_property (css, "writing-mode", "lr");
6284 break;
6285 }
6287 case 1:
6288 {
6289 sp_repr_css_set_property (css, "writing-mode", "tb");
6290 break;
6291 }
6292 }
6294 SPStyle *query =
6295 sp_style_new (SP_ACTIVE_DOCUMENT);
6296 int result_numbers =
6297 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6299 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6300 if (result_numbers == QUERY_STYLE_NOTHING)
6301 {
6302 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6303 prefs->setStyle("/tools/text/style", css);
6304 }
6306 sp_desktop_set_style (desktop, css, true, true);
6307 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6308 _("Text: Change orientation"));
6309 sp_repr_css_attr_unref (css);
6311 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6312 }
6314 gboolean
6315 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6316 {
6317 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6318 if (!desktop) return FALSE;
6320 switch (get_group0_keyval (event)) {
6321 case GDK_Escape: // defocus
6322 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6323 sp_text_toolbox_selection_changed (NULL, tbl); // update
6324 return TRUE; // I consumed the event
6325 break;
6326 }
6327 return FALSE;
6328 }
6330 gboolean
6331 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
6332 {
6333 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6334 if (!desktop) return FALSE;
6336 switch (get_group0_keyval (event)) {
6337 case GDK_KP_Enter:
6338 case GDK_Return:
6339 case GDK_Escape: // defocus
6340 gtk_widget_hide (w);
6341 popdown_visible = false;
6342 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6343 return TRUE; // I consumed the event
6344 break;
6345 case GDK_w:
6346 case GDK_W:
6347 if (event->state & GDK_CONTROL_MASK) {
6348 gtk_widget_hide (w);
6349 popdown_visible = false;
6350 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6351 return TRUE; // I consumed the event
6352 }
6353 break;
6354 }
6355 return FALSE;
6356 }
6359 void
6360 sp_text_toolbox_size_changed (GtkComboBox *cbox,
6361 GObject *tbl)
6362 {
6363 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6365 if (g_object_get_data (tbl, "size-block")) return;
6367 // If this is not from selecting a size in the list (in which case get_active will give the
6368 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
6369 // process this event. This fixes GTK's stupid insistence on sending an activate change every
6370 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
6371 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
6372 return;
6374 gdouble value = -1;
6375 {
6376 gchar *endptr;
6377 gchar *const text = gtk_combo_box_get_active_text(cbox);
6378 if (text) {
6379 value = g_strtod(text, &endptr);
6380 if (endptr == text) { // Conversion failed, non-numeric input.
6381 value = -1;
6382 }
6383 g_free(text);
6384 }
6385 }
6386 if (value <= 0) {
6387 return; // could not parse value
6388 }
6390 SPCSSAttr *css = sp_repr_css_attr_new ();
6391 Inkscape::CSSOStringStream osfs;
6392 osfs << value;
6393 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
6395 SPStyle *query =
6396 sp_style_new (SP_ACTIVE_DOCUMENT);
6397 int result_numbers =
6398 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6400 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6401 if (result_numbers == QUERY_STYLE_NOTHING)
6402 {
6403 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6404 prefs->setStyle("/tools/text/style", css);
6405 }
6407 sp_style_unref(query);
6409 sp_desktop_set_style (desktop, css, true, true);
6410 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
6411 _("Text: Change font size"));
6412 sp_repr_css_attr_unref (css);
6414 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6415 }
6417 gboolean
6418 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
6419 {
6420 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6421 if (!desktop) return FALSE;
6423 if (!g_object_get_data (tbl, "esc-pressed")) {
6424 g_object_set_data (tbl, "enter-pressed", gpointer(1));
6425 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6426 sp_text_toolbox_size_changed (cbox, tbl);
6427 g_object_set_data (tbl, "enter-pressed", gpointer(0));
6428 }
6429 return FALSE; // I consumed the event
6430 }
6433 gboolean
6434 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6435 {
6436 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6437 if (!desktop) return FALSE;
6439 switch (get_group0_keyval (event)) {
6440 case GDK_Escape: // defocus
6441 g_object_set_data (tbl, "esc-pressed", gpointer(1));
6442 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6443 g_object_set_data (tbl, "esc-pressed", gpointer(0));
6444 return TRUE; // I consumed the event
6445 break;
6446 case GDK_Return: // defocus
6447 case GDK_KP_Enter:
6448 g_object_set_data (tbl, "enter-pressed", gpointer(1));
6449 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6450 sp_text_toolbox_size_changed (cbox, tbl);
6451 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6452 g_object_set_data (tbl, "enter-pressed", gpointer(0));
6453 return TRUE; // I consumed the event
6454 break;
6455 }
6456 return FALSE;
6457 }
6459 void
6460 sp_text_toolbox_text_popdown_clicked (GtkButton */*button*/,
6461 GObject *tbl)
6462 {
6463 GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
6464 GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6465 int x, y;
6467 if (!popdown_visible)
6468 {
6469 gdk_window_get_origin (widget->window, &x, &y);
6470 gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
6471 gtk_widget_show_all (popdown);
6472 //sp_transientize (popdown);
6474 gdk_pointer_grab (widget->window, TRUE,
6475 GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
6476 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
6477 GDK_POINTER_MOTION_MASK),
6478 NULL, NULL, GDK_CURRENT_TIME);
6480 gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
6482 popdown_visible = true;
6483 }
6484 else
6485 {
6486 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6487 gdk_pointer_ungrab (GDK_CURRENT_TIME);
6488 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
6489 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6490 gtk_widget_hide (popdown);
6491 popdown_visible = false;
6492 }
6493 }
6495 gboolean
6496 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
6497 GdkEventFocus */*event*/,
6498 GObject */*tbl*/)
6499 {
6500 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
6501 return FALSE;
6502 }
6504 gboolean
6505 sp_text_toolbox_popdown_focus_out (GtkWidget *popdown,
6506 GdkEventFocus */*event*/,
6507 GObject */*tbl*/)
6508 {
6509 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6511 if (popdown_hasfocus) {
6512 gtk_widget_hide (popdown);
6513 popdown_hasfocus = false;
6514 popdown_visible = false;
6515 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6516 return TRUE;
6517 }
6518 return FALSE;
6519 }
6521 gboolean
6522 sp_text_toolbox_popdown_focus_in (GtkWidget */*popdown*/,
6523 GdkEventFocus */*event*/,
6524 GObject */*tbl*/)
6525 {
6526 popdown_hasfocus = true;
6527 return TRUE;
6528 }
6531 void
6532 cell_data_func (GtkTreeViewColumn */*column*/,
6533 GtkCellRenderer *cell,
6534 GtkTreeModel *tree_model,
6535 GtkTreeIter *iter,
6536 gpointer /*data*/)
6537 {
6538 gchar *family;
6539 gtk_tree_model_get(tree_model, iter, 0, &family, -1);
6540 gchar *const family_escaped = g_markup_escape_text(family, -1);
6542 static char const *const sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
6543 gchar *const sample_escaped = g_markup_escape_text(sample, -1);
6545 std::stringstream markup;
6546 markup << family_escaped << " <span foreground='darkgray' font_family='"
6547 << family_escaped << "'>" << sample_escaped << "</span>";
6548 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6550 g_free(family);
6551 g_free(family_escaped);
6552 g_free(sample_escaped);
6553 }
6555 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
6556 GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
6557 if (completion) {
6558 gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
6559 g_object_unref (completion);
6560 }
6561 }
6563 GtkWidget*
6564 sp_text_toolbox_new (SPDesktop *desktop)
6565 {
6566 GtkToolbar *tbl = GTK_TOOLBAR(gtk_toolbar_new());
6567 GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("/toolbox/secondary", 1));
6569 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
6570 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
6572 GtkTooltips *tt = gtk_tooltips_new();
6573 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
6575 ////////////Family
6576 //Window
6577 GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
6578 gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
6580 //Entry
6581 GtkWidget *entry = gtk_entry_new ();
6582 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
6583 GtkEntryCompletion *completion = gtk_entry_completion_new ();
6584 gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
6585 gtk_entry_completion_set_text_column (completion, 0);
6586 gtk_entry_completion_set_minimum_key_length (completion, 1);
6587 g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
6588 gtk_entry_set_completion (GTK_ENTRY(entry), completion);
6589 gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
6590 gtk_toolbar_append_widget( tbl, entry, "", "" );
6591 g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
6593 //Button
6594 GtkWidget *button = gtk_button_new ();
6595 gtk_container_add (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
6596 gtk_toolbar_append_widget( tbl, button, "", "");
6598 //Popdown
6599 GtkWidget *sw = gtk_scrolled_window_new (NULL, NULL);
6600 GtkWidget *treeview = gtk_tree_view_new ();
6602 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
6603 GtkTreeViewColumn *column = gtk_tree_view_column_new ();
6604 gtk_tree_view_column_pack_start (column, cell, FALSE);
6605 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
6606 gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
6607 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
6609 gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
6610 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
6611 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
6613 //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
6615 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
6616 gtk_container_add (GTK_CONTAINER (sw), treeview);
6618 gtk_container_add (GTK_CONTAINER (window), sw);
6619 gtk_widget_set_size_request (window, 300, 450);
6621 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
6622 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
6623 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
6625 g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
6627 g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
6628 g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
6629 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
6631 GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
6632 g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6634 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
6635 g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
6636 g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
6637 g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
6638 g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
6640 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
6641 GtkWidget *box = gtk_event_box_new ();
6642 gtk_container_add (GTK_CONTAINER (box), image);
6643 gtk_toolbar_append_widget( tbl, box, "", "");
6644 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
6645 GtkTooltips *tooltips = gtk_tooltips_new ();
6646 gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
6647 gtk_widget_hide (GTK_WIDGET (box));
6648 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
6650 ////////////Size
6651 gchar const *const sizes[] = {
6652 "4", "6", "8", "9", "10", "11", "12", "13", "14",
6653 "16", "18", "20", "22", "24", "28",
6654 "32", "36", "40", "48", "56", "64", "72", "144"
6655 };
6657 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
6658 for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
6659 gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
6660 }
6661 gtk_widget_set_size_request (cbox, 80, -1);
6662 gtk_toolbar_append_widget( tbl, cbox, "", "");
6663 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
6664 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
6665 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
6666 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
6668 ////////////Text anchor
6669 GtkWidget *group = gtk_radio_button_new (NULL);
6670 GtkWidget *row = gtk_hbox_new (FALSE, 4);
6671 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
6673 // left
6674 GtkWidget *rbutton = group;
6675 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6676 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
6677 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6679 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6680 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
6681 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
6682 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
6684 // center
6685 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6686 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6687 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
6688 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6690 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6691 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
6692 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
6693 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
6695 // right
6696 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6697 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6698 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
6699 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6701 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6702 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
6703 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
6704 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
6706 // fill
6707 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6708 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6709 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
6710 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6712 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6713 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
6714 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
6715 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
6717 gtk_toolbar_append_widget( tbl, row, "", "");
6719 //spacer
6720 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6722 ////////////Text style
6723 row = gtk_hbox_new (FALSE, 4);
6725 // bold
6726 rbutton = gtk_toggle_button_new ();
6727 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6728 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
6729 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6730 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
6732 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6733 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
6734 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
6736 // italic
6737 rbutton = gtk_toggle_button_new ();
6738 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6739 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
6740 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6741 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
6743 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6744 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
6745 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
6747 gtk_toolbar_append_widget( tbl, row, "", "");
6749 //spacer
6750 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6752 // Text orientation
6753 group = gtk_radio_button_new (NULL);
6754 row = gtk_hbox_new (FALSE, 4);
6755 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
6757 // horizontal
6758 rbutton = group;
6759 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6760 gtk_container_add (GTK_CONTAINER (rbutton),
6761 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_HORIZONTAL));
6762 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6763 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
6765 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6766 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
6767 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
6769 // vertical
6770 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6771 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6772 gtk_container_add (GTK_CONTAINER (rbutton),
6773 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_VERTICAL));
6774 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6775 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
6777 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6778 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
6779 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
6780 gtk_toolbar_append_widget( tbl, row, "", "" );
6783 //watch selection
6784 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
6786 sigc::connection *c_selection_changed =
6787 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
6788 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
6789 pool->add_connection ("selection-changed", c_selection_changed);
6791 sigc::connection *c_selection_modified =
6792 new sigc::connection (sp_desktop_selection (desktop)->connectModified
6793 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
6794 pool->add_connection ("selection-modified", c_selection_modified);
6796 sigc::connection *c_subselection_changed =
6797 new sigc::connection (desktop->connectToolSubselectionChanged
6798 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
6799 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
6801 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
6804 gtk_widget_show_all( GTK_WIDGET(tbl) );
6806 return GTK_WIDGET(tbl);
6807 } // end of sp_text_toolbox_new()
6809 }//<unnamed> namespace
6812 //#########################
6813 //## Connector ##
6814 //#########################
6816 static void sp_connector_path_set_avoid(void)
6817 {
6818 cc_selection_set_avoid(true);
6819 }
6822 static void sp_connector_path_set_ignore(void)
6823 {
6824 cc_selection_set_avoid(false);
6825 }
6829 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
6830 {
6831 // quit if run by the _changed callbacks
6832 if (g_object_get_data( tbl, "freeze" )) {
6833 return;
6834 }
6836 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
6837 SPDocument *doc = sp_desktop_document(desktop);
6839 if (!sp_document_get_undo_sensitive(doc))
6840 {
6841 return;
6842 }
6844 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6846 if ( repr->attribute("inkscape:connector-spacing") ) {
6847 gdouble priorValue = gtk_adjustment_get_value(adj);
6848 sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
6849 if ( priorValue == gtk_adjustment_get_value(adj) ) {
6850 return;
6851 }
6852 } else if ( adj->value == defaultConnSpacing ) {
6853 return;
6854 }
6856 // in turn, prevent callbacks from responding
6857 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6859 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
6860 SP_OBJECT(desktop->namedview)->updateRepr();
6862 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
6863 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
6864 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
6865 Geom::Matrix m = Geom::identity();
6866 avoid_item_move(&m, item);
6867 }
6869 if (items) {
6870 g_slist_free(items);
6871 }
6873 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
6874 _("Change connector spacing"));
6876 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6878 spinbutton_defocus(GTK_OBJECT(tbl));
6879 }
6881 static void sp_connector_graph_layout(void)
6882 {
6883 if (!SP_ACTIVE_DESKTOP) return;
6884 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6886 // hack for clones, see comment in align-and-distribute.cpp
6887 int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
6888 prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
6890 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
6892 prefs->setInt("/options/clonecompensation/value", saved_compensation);
6894 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
6895 }
6897 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6898 {
6899 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6900 prefs->setBool("/tools/connector/directedlayout",
6901 gtk_toggle_action_get_active( act ));
6902 }
6904 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6905 {
6906 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6907 prefs->setBool("/tools/connector/avoidoverlaplayout",
6908 gtk_toggle_action_get_active( act ));
6909 }
6912 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
6913 {
6914 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6915 prefs->setDouble("/tools/connector/length", adj->value);
6916 spinbutton_defocus(GTK_OBJECT(tbl));
6917 }
6919 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
6920 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
6921 bool /*is_interactive*/, gpointer data)
6922 {
6923 GtkWidget *tbl = GTK_WIDGET(data);
6925 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6926 return;
6927 }
6928 if (strcmp(name, "inkscape:connector-spacing") != 0) {
6929 return;
6930 }
6932 GtkAdjustment *adj = (GtkAdjustment*)
6933 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
6934 gdouble spacing = defaultConnSpacing;
6935 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
6937 gtk_adjustment_set_value(adj, spacing);
6938 }
6941 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
6942 NULL, /* child_added */
6943 NULL, /* child_removed */
6944 connector_tb_event_attr_changed,
6945 NULL, /* content_changed */
6946 NULL /* order_changed */
6947 };
6950 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
6951 {
6952 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6953 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
6955 {
6956 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
6957 _("Avoid"),
6958 _("Make connectors avoid selected objects"),
6959 INKSCAPE_ICON_CONNECTOR_AVOID,
6960 secondarySize );
6961 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
6962 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6963 }
6965 {
6966 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
6967 _("Ignore"),
6968 _("Make connectors ignore selected objects"),
6969 INKSCAPE_ICON_CONNECTOR_IGNORE,
6970 secondarySize );
6971 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
6972 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6973 }
6975 EgeAdjustmentAction* eact = 0;
6977 // Spacing spinbox
6978 eact = create_adjustment_action( "ConnectorSpacingAction",
6979 _("Connector Spacing"), _("Spacing:"),
6980 _("The amount of space left around objects by auto-routing connectors"),
6981 "/tools/connector/spacing", defaultConnSpacing,
6982 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
6983 0, 100, 1.0, 10.0,
6984 0, 0, 0,
6985 connector_spacing_changed, 1, 0 );
6986 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6988 // Graph (connector network) layout
6989 {
6990 InkAction* inky = ink_action_new( "ConnectorGraphAction",
6991 _("Graph"),
6992 _("Nicely arrange selected connector network"),
6993 INKSCAPE_ICON_DISTRIBUTE_GRAPH,
6994 secondarySize );
6995 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
6996 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6997 }
6999 // Default connector length spinbox
7000 eact = create_adjustment_action( "ConnectorLengthAction",
7001 _("Connector Length"), _("Length:"),
7002 _("Ideal length for connectors when layout is applied"),
7003 "/tools/connector/length", 100,
7004 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
7005 10, 1000, 10.0, 100.0,
7006 0, 0, 0,
7007 connector_length_changed, 1, 0 );
7008 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7011 // Directed edges toggle button
7012 {
7013 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
7014 _("Downwards"),
7015 _("Make connectors with end-markers (arrows) point downwards"),
7016 INKSCAPE_ICON_DISTRIBUTE_GRAPH_DIRECTED,
7017 Inkscape::ICON_SIZE_DECORATION );
7018 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7020 bool tbuttonstate = prefs->getBool("/tools/connector/directedlayout");
7021 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7023 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
7024 }
7026 // Avoid overlaps toggle button
7027 {
7028 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
7029 _("Remove overlaps"),
7030 _("Do not allow overlapping shapes"),
7031 INKSCAPE_ICON_DISTRIBUTE_REMOVE_OVERLAPS,
7032 Inkscape::ICON_SIZE_DECORATION );
7033 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7035 bool tbuttonstate = prefs->getBool("/tools/connector/avoidoverlaplayout");
7036 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), (tbuttonstate ? TRUE : FALSE ));
7038 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
7039 }
7041 // Code to watch for changes to the connector-spacing attribute in
7042 // the XML.
7043 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7044 g_assert(repr != NULL);
7046 purge_repr_listener( holder, holder );
7048 if (repr) {
7049 g_object_set_data( holder, "repr", repr );
7050 Inkscape::GC::anchor(repr);
7051 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
7052 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
7053 }
7054 } // end of sp_connector_toolbox_prep()
7057 //#########################
7058 //## Paintbucket ##
7059 //#########################
7061 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
7062 {
7063 gint channels = ege_select_one_action_get_active( act );
7064 flood_channels_set_channels( channels );
7065 }
7067 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
7068 {
7069 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7070 prefs->setInt("/tools/paintbucket/threshold", (gint)adj->value);
7071 }
7073 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
7074 {
7075 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7076 prefs->setBool("/tools/paintbucket/autogap", ege_select_one_action_get_active( act ));
7077 }
7079 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
7080 {
7081 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
7082 SPUnit const *unit = tracker->getActiveUnit();
7083 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7085 prefs->setDouble("/tools/paintbucket/offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
7086 prefs->setString("/tools/paintbucket/offsetunits", sp_unit_get_abbreviation(unit));
7087 }
7089 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
7090 {
7091 // FIXME: make defaults settable via Inkscape Options
7092 struct KeyValue {
7093 char const *key;
7094 double value;
7095 } const key_values[] = {
7096 {"threshold", 15},
7097 {"offset", 0.0}
7098 };
7100 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
7101 KeyValue const &kv = key_values[i];
7102 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
7103 if ( adj ) {
7104 gtk_adjustment_set_value(adj, kv.value);
7105 }
7106 }
7108 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
7109 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
7110 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
7111 ege_select_one_action_set_active( autogap_action, 0 );
7112 }
7114 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
7115 {
7116 EgeAdjustmentAction* eact = 0;
7117 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7119 {
7120 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7122 GList* items = 0;
7123 gint count = 0;
7124 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
7125 {
7126 GtkTreeIter iter;
7127 gtk_list_store_append( model, &iter );
7128 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7129 count++;
7130 }
7131 g_list_free( items );
7132 items = 0;
7133 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
7134 g_object_set( act1, "short_label", _("Fill by:"), NULL );
7135 ege_select_one_action_set_appearance( act1, "compact" );
7136 ege_select_one_action_set_active( act1, prefs->getInt("/tools/paintbucket/channels", 0) );
7137 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
7138 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
7139 g_object_set_data( holder, "channels_action", act1 );
7140 }
7142 // Spacing spinbox
7143 {
7144 eact = create_adjustment_action(
7145 "ThresholdAction",
7146 _("Fill Threshold"), _("Threshold:"),
7147 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
7148 "/tools/paintbucket/threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
7149 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
7150 0, 0, 0,
7151 paintbucket_threshold_changed, 1, 0 );
7153 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
7154 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7155 }
7157 // Create the units menu.
7158 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
7159 Glib::ustring stored_unit = prefs->getString("/tools/paintbucket/offsetunits");
7160 if (!stored_unit.empty())
7161 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit.data()));
7162 g_object_set_data( holder, "tracker", tracker );
7163 {
7164 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
7165 gtk_action_group_add_action( mainActions, act );
7166 }
7168 // Offset spinbox
7169 {
7170 eact = create_adjustment_action(
7171 "OffsetAction",
7172 _("Grow/shrink by"), _("Grow/shrink by:"),
7173 _("The amount to grow (positive) or shrink (negative) the created fill path"),
7174 "/tools/paintbucket/offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
7175 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
7176 0, 0, 0,
7177 paintbucket_offset_changed, 1, 2);
7178 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
7180 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7181 }
7183 /* Auto Gap */
7184 {
7185 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7187 GList* items = 0;
7188 gint count = 0;
7189 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
7190 {
7191 GtkTreeIter iter;
7192 gtk_list_store_append( model, &iter );
7193 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7194 count++;
7195 }
7196 g_list_free( items );
7197 items = 0;
7198 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
7199 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
7200 ege_select_one_action_set_appearance( act2, "compact" );
7201 ege_select_one_action_set_active( act2, prefs->getBool("/tools/paintbucket/autogap") );
7202 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
7203 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
7204 g_object_set_data( holder, "autogap_action", act2 );
7205 }
7207 /* Reset */
7208 {
7209 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
7210 _("Defaults"),
7211 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
7212 GTK_STOCK_CLEAR );
7213 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
7214 gtk_action_group_add_action( mainActions, act );
7215 gtk_action_set_sensitive( act, TRUE );
7216 }
7218 }
7220 /*
7221 Local Variables:
7222 mode:c++
7223 c-file-style:"stroustrup"
7224 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
7225 indent-tabs-mode:nil
7226 fill-column:99
7227 End:
7228 */
7229 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :