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 "calligraphic-profile-rename.h"
40 #include "../conn-avoid-ref.h"
41 #include "../connection-pool.h"
42 #include "../connector-context.h"
43 #include "../desktop.h"
44 #include "../desktop-handles.h"
45 #include "../desktop-style.h"
46 #include "../dialogs/dialog-events.h"
47 #include "../dialogs/text-edit.h"
48 #include "../document-private.h"
49 #include "../ege-adjustment-action.h"
50 #include "../ege-output-action.h"
51 #include "../ege-select-one-action.h"
52 #include "../flood-context.h"
53 #include "gradient-toolbar.h"
54 #include "../graphlayout/graphlayout.h"
55 #include "../helper/unit-menu.h"
56 #include "../helper/units.h"
57 #include "../helper/unit-tracker.h"
58 #include "icon.h"
59 #include "../ink-action.h"
60 #include "../inkscape.h"
61 #include "../inkscape-stock.h"
62 #include "../interface.h"
63 #include "../libnrtype/font-instance.h"
64 #include "../libnrtype/font-lister.h"
65 #include "../live_effects/effect.h"
66 #include "../live_effects/lpe-angle_bisector.h"
67 #include "../live_effects/lpe-line_segment.h"
68 #include "../lpe-tool-context.h"
69 #include "../mod360.h"
70 #include "../node-context.h"
71 #include "../pen-context.h"
72 #include "../preferences.h"
73 #include "../selection-chemistry.h"
74 #include "../selection.h"
75 #include "select-toolbar.h"
76 #include "../shape-editor.h"
77 #include "../shortcuts.h"
78 #include "../sp-clippath.h"
79 #include "../sp-ellipse.h"
80 #include "../sp-flowtext.h"
81 #include "../sp-mask.h"
82 #include "../sp-namedview.h"
83 #include "../sp-rect.h"
84 #include "../sp-spiral.h"
85 #include "../sp-star.h"
86 #include "../sp-text.h"
87 #include "../style.h"
88 #include "../svg/css-ostringstream.h"
89 #include "../tools-switch.h"
90 #include "../tweak-context.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 "node_insert",
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 "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 "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 "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 "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 "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 "node_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 "node_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 "node_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 "node_auto",
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 "node_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 "node_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 "nodes_show_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 "nodes_show_helperpath",
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 "edit_next_parameter",
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 "nodeedit-clippath",
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 "nodeedit-mask",
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='ToggleSnapToFromBBoxMidpoints' />"
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='ToggleSnapToFromObjectMidpoints' />"
2008 " <toolitem action='ToggleSnapToFromCenter' />"
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", // "name"
2021 _("Snap"), // "label"
2022 _("Enable snapping"), // "tooltip"
2023 "toggle_snap_global", // "iconId"
2024 secondarySize,
2025 SP_ATTR_INKSCAPE_SNAP_GLOBAL);
2027 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2028 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2029 }
2031 {
2032 InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromBBoxCorner",// "name"
2033 _("Bounding box"), // "label"
2034 _("Snap bounding box corners"), // "tooltip"
2035 "toggle_snap_bbox", // "iconId"
2036 secondarySize,
2037 SP_ATTR_INKSCAPE_SNAP_BBOX);
2039 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2040 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2041 }
2043 {
2044 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxPath", // "name"
2045 _("Bounding box edges"), // "label"
2046 _("Snap to edges of a bounding box"), // "tooltip"
2047 "toggle_snap_to_bbox_path", // "iconId"
2048 secondarySize,
2049 SP_ATTR_INKSCAPE_BBOX_PATHS);
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("ToggleSnapToBBoxNode", // "name"
2057 _("Bounding box corners"), // "label"
2058 _("Snap to bounding box corners"), // "tooltip"
2059 "toggle_snap_to_bbox_node", // "iconId"
2060 secondarySize,
2061 SP_ATTR_INKSCAPE_BBOX_NODES);
2063 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2064 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2065 }
2067 {
2068 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxEdgeMidpoints", // "name"
2069 _("BBox Edge Midpoints"), // "label"
2070 _("Snap from and to midpoints of bounding box edges"), // "tooltip"
2071 "toggle_snap_to_bbox_edge_midpoints", // "iconId"
2072 secondarySize,
2073 SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS);
2075 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2076 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2077 }
2079 {
2080 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxMidpoints", // "name"
2081 _("BBox Midpoints"), // "label"
2082 _("Snapping from and to midpoints of bounding boxes"), // "tooltip"
2083 "toggle_snap_to_bbox_midpoints", // "iconId"
2084 secondarySize,
2085 SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS);
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("ToggleSnapFromNode", // "name"
2093 _("Nodes"), // "label"
2094 _("Snap nodes"), // "tooltip"
2095 "toggle_snap_nodes", // "iconId"
2096 secondarySize,
2097 SP_ATTR_INKSCAPE_SNAP_NODES);
2099 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2100 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2101 }
2103 {
2104 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemPath", // "name"
2105 _("Paths"), // "label"
2106 _("Snap to paths"), // "tooltip"
2107 "toggle_snap_to_paths", // "iconId"
2108 secondarySize,
2109 SP_ATTR_INKSCAPE_OBJECT_PATHS);
2111 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2112 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2113 }
2115 {
2116 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPathIntersections", // "name"
2117 _("Path intersections"), // "label"
2118 _("Snap to path intersections"), // "tooltip"
2119 "toggle_snap_to_path_intersections", // "iconId"
2120 secondarySize,
2121 SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS);
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("ToggleSnapToItemNode", // "name"
2129 _("To nodes"), // "label"
2130 _("Snap to cusp nodes"), // "tooltip"
2131 "toggle_snap_to_nodes", // "iconId"
2132 secondarySize,
2133 SP_ATTR_INKSCAPE_OBJECT_NODES);
2135 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2136 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2137 }
2139 {
2140 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToSmoothNodes", // "name"
2141 _("Smooth nodes"), // "label"
2142 _("Snap to smooth nodes"),// "tooltip"
2143 "toggle_snap_to_smooth_nodes", // "iconId"
2144 secondarySize,
2145 SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES);
2147 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2148 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2149 }
2151 {
2152 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromLineMidpoints", // "name"
2153 _("Line Midpoints"), // "label"
2154 _("Snap from and to midpoints of line segments"), // "tooltip"
2155 "toggle_snap_to_midpoints", // "iconId"
2156 secondarySize,
2157 SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS);
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("ToggleSnapToFromObjectMidpoints", // "name"
2165 _("Object Midpoints"), // "label"
2166 _("Snap from and to midpoints of objects"), // "tooltip"
2167 "toggle_snap_to_object_midpoints", // "iconId"
2168 secondarySize,
2169 SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS);
2171 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2172 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2173 }
2175 {
2176 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromCenter",// "name"
2177 _("Center"), // "label"
2178 _("Snap from and to an item's rotation center"), // "tooltip"
2179 "toggle_snap_center", // "iconId"
2180 secondarySize,
2181 SP_ATTR_INKSCAPE_SNAP_CENTER);
2183 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2184 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2185 }
2187 {
2188 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPageBorder", // "name"
2189 _("Page border"), // "label"
2190 _("Snap to the page border"), // "tooltip"
2191 "toggle_snap_page_border", // "iconId"
2192 secondarySize,
2193 SP_ATTR_INKSCAPE_SNAP_PAGE);
2195 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2196 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2197 }
2199 {
2200 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGrids", // "name"
2201 _("Grids"), // "label"
2202 _("Snap to grids"), // "tooltip"
2203 "grid_xy", // "iconId"
2204 secondarySize,
2205 SP_ATTR_INKSCAPE_SNAP_GRIDS);
2207 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2208 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2209 }
2211 {
2212 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGuides", // "name"
2213 _("Guides"), // "label"
2214 _("Snap to guides"), // "tooltip"
2215 "guide", // "iconId"
2216 secondarySize,
2217 SP_ATTR_INKSCAPE_SNAP_TO_GUIDES);
2219 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2220 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2221 }
2223 /*{
2224 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGridGuideIntersections", // "name"
2225 _("Grid/guide intersections"), // "label"
2226 _("Snap to intersections of a grid with a guide"), // "tooltip"
2227 "toggle_snap_grid_guide_intersections", // "iconId"
2228 secondarySize,
2229 SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE);
2231 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2232 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2233 }*/
2235 GtkUIManager* mgr = gtk_ui_manager_new();
2236 GError* errVal = 0;
2238 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
2239 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
2241 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/SnapToolbar" );
2242 if ( prefs->getBool("/toolbox/icononly", true) ) {
2243 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
2244 }
2246 Inkscape::IconSize toolboxSize = prefToSize("/toolbox/secondary");
2247 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
2249 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
2250 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
2252 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
2254 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
2255 if ( child ) {
2256 gtk_container_remove( GTK_CONTAINER(toolbox), child );
2257 }
2259 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
2261 }
2263 void update_snap_toolbox(SPDesktop *desktop, SPEventContext */*eventcontext*/, GtkWidget *toolbox)
2264 {
2265 g_assert(desktop != NULL);
2266 g_assert(toolbox != NULL);
2268 SPNamedView *nv = sp_desktop_namedview(desktop);
2269 if (nv == NULL) {
2270 g_warning("Namedview cannot be retrieved (in update_snap_toolbox)!");
2271 return;
2272 }
2274 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2276 Glib::RefPtr<Gtk::Action> act1 = mainActions->get_action("ToggleSnapGlobal");
2277 Glib::RefPtr<Gtk::Action> act2 = mainActions->get_action("ToggleSnapFromBBoxCorner");
2278 Glib::RefPtr<Gtk::Action> act3 = mainActions->get_action("ToggleSnapToBBoxPath");
2279 Glib::RefPtr<Gtk::Action> act4 = mainActions->get_action("ToggleSnapToBBoxNode");
2280 Glib::RefPtr<Gtk::Action> act4b = mainActions->get_action("ToggleSnapToFromBBoxEdgeMidpoints");
2281 Glib::RefPtr<Gtk::Action> act4c = mainActions->get_action("ToggleSnapToFromBBoxMidpoints");
2282 Glib::RefPtr<Gtk::Action> act5 = mainActions->get_action("ToggleSnapFromNode");
2283 Glib::RefPtr<Gtk::Action> act6 = mainActions->get_action("ToggleSnapToItemPath");
2284 Glib::RefPtr<Gtk::Action> act6b = mainActions->get_action("ToggleSnapToPathIntersections");
2285 Glib::RefPtr<Gtk::Action> act7 = mainActions->get_action("ToggleSnapToItemNode");
2286 Glib::RefPtr<Gtk::Action> act8 = mainActions->get_action("ToggleSnapToSmoothNodes");
2287 Glib::RefPtr<Gtk::Action> act9 = mainActions->get_action("ToggleSnapToFromLineMidpoints");
2288 Glib::RefPtr<Gtk::Action> act10 = mainActions->get_action("ToggleSnapToFromObjectMidpoints");
2289 Glib::RefPtr<Gtk::Action> act11 = mainActions->get_action("ToggleSnapToFromCenter");
2290 Glib::RefPtr<Gtk::Action> act12 = mainActions->get_action("ToggleSnapToPageBorder");
2291 //Glib::RefPtr<Gtk::Action> act13 = mainActions->get_action("ToggleSnapToGridGuideIntersections");
2292 Glib::RefPtr<Gtk::Action> act14 = mainActions->get_action("ToggleSnapToGrids");
2293 Glib::RefPtr<Gtk::Action> act15 = mainActions->get_action("ToggleSnapToGuides");
2296 if (!act1) {
2297 return; // The snap actions haven't been defined yet (might be the case during startup)
2298 }
2300 // The ..._set_active calls below will toggle the buttons, but this shouldn't lead to
2301 // changes in our document because we're only updating the UI;
2302 // Setting the "freeze" parameter to true will block the code in toggle_snap_callback()
2303 g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(TRUE));
2305 bool const c1 = nv->snap_manager.snapprefs.getSnapEnabledGlobally();
2306 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act1->gobj()), c1);
2308 bool const c2 = nv->snap_manager.snapprefs.getSnapModeBBox();
2309 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act2->gobj()), c2);
2310 gtk_action_set_sensitive(GTK_ACTION(act2->gobj()), c1);
2312 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act3->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxPath());
2313 gtk_action_set_sensitive(GTK_ACTION(act3->gobj()), c1 && c2);
2314 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxNode());
2315 gtk_action_set_sensitive(GTK_ACTION(act4->gobj()), c1 && c2);
2316 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4b->gobj()), nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints());
2317 gtk_action_set_sensitive(GTK_ACTION(act4b->gobj()), c1 && c2);
2318 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4c->gobj()), nv->snap_manager.snapprefs.getSnapBBoxMidpoints());
2319 gtk_action_set_sensitive(GTK_ACTION(act4c->gobj()), c1 && c2);
2321 bool const c3 = nv->snap_manager.snapprefs.getSnapModeNode();
2322 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act5->gobj()), c3);
2323 gtk_action_set_sensitive(GTK_ACTION(act5->gobj()), c1);
2325 bool const c4 = nv->snap_manager.snapprefs.getSnapToItemPath();
2326 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6->gobj()), c4);
2327 gtk_action_set_sensitive(GTK_ACTION(act6->gobj()), c1 && c3);
2328 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6b->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionCS());
2329 gtk_action_set_sensitive(GTK_ACTION(act6b->gobj()), c1 && c3 && c4);
2330 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act7->gobj()), nv->snap_manager.snapprefs.getSnapToItemNode());
2331 gtk_action_set_sensitive(GTK_ACTION(act7->gobj()), c1 && c3);
2332 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act8->gobj()), nv->snap_manager.snapprefs.getSnapSmoothNodes());
2333 gtk_action_set_sensitive(GTK_ACTION(act8->gobj()), c1 && c3);
2334 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act9->gobj()), nv->snap_manager.snapprefs.getSnapLineMidpoints());
2335 gtk_action_set_sensitive(GTK_ACTION(act9->gobj()), c1 && c3);
2336 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act10->gobj()), nv->snap_manager.snapprefs.getSnapObjectMidpoints());
2337 gtk_action_set_sensitive(GTK_ACTION(act10->gobj()), c1 && c3);
2338 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act11->gobj()), nv->snap_manager.snapprefs.getIncludeItemCenter());
2339 gtk_action_set_sensitive(GTK_ACTION(act11->gobj()), c1 && c3);
2341 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act12->gobj()), nv->snap_manager.snapprefs.getSnapToPageBorder());
2342 gtk_action_set_sensitive(GTK_ACTION(act12->gobj()), c1);
2343 //gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act13->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionGG());
2344 //gtk_action_set_sensitive(GTK_ACTION(act13->gobj()), c1);
2346 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act14->gobj()), nv->snap_manager.snapprefs.getSnapToGrids());
2347 gtk_action_set_sensitive(GTK_ACTION(act14->gobj()), c1);
2348 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act15->gobj()), nv->snap_manager.snapprefs.getSnapToGuides());
2349 gtk_action_set_sensitive(GTK_ACTION(act15->gobj()), c1);
2352 g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(FALSE)); // unfreeze (see above)
2353 }
2355 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
2356 {
2357 gtk_widget_show(toolbox_toplevel);
2358 GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
2360 GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
2361 if (!shown_toolbox) {
2362 return;
2363 }
2364 gtk_widget_show(toolbox);
2366 gtk_widget_show_all(shown_toolbox);
2367 }
2369 static GtkWidget *
2370 sp_empty_toolbox_new(SPDesktop *desktop)
2371 {
2372 GtkWidget *tbl = gtk_toolbar_new();
2373 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
2374 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
2376 gtk_widget_show_all(tbl);
2377 sp_set_font_size_smaller (tbl);
2379 return tbl;
2380 }
2382 #define MODE_LABEL_WIDTH 70
2384 //########################
2385 //## Star ##
2386 //########################
2388 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2389 {
2390 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2392 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2393 // do not remember prefs if this call is initiated by an undo change, because undoing object
2394 // creation sets bogus values to its attributes before it is deleted
2395 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2396 prefs->setInt("/tools/shapes/star/magnitude", (gint)adj->value);
2397 }
2399 // quit if run by the attr_changed listener
2400 if (g_object_get_data( dataKludge, "freeze" )) {
2401 return;
2402 }
2404 // in turn, prevent listener from responding
2405 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2407 bool modmade = false;
2409 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2410 GSList const *items = selection->itemList();
2411 for (; items != NULL; items = items->next) {
2412 if (SP_IS_STAR((SPItem *) items->data)) {
2413 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2414 sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
2415 sp_repr_set_svg_double(repr, "sodipodi:arg2",
2416 (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
2417 + M_PI / (gint)adj->value));
2418 SP_OBJECT((SPItem *) items->data)->updateRepr();
2419 modmade = true;
2420 }
2421 }
2422 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2423 _("Star: Change number of corners"));
2425 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2426 }
2428 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2429 {
2430 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2432 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2433 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2434 prefs->setDouble("/tools/shapes/star/proportion", adj->value);
2435 }
2437 // quit if run by the attr_changed listener
2438 if (g_object_get_data( dataKludge, "freeze" )) {
2439 return;
2440 }
2442 // in turn, prevent listener from responding
2443 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2445 bool modmade = false;
2446 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2447 GSList const *items = selection->itemList();
2448 for (; items != NULL; items = items->next) {
2449 if (SP_IS_STAR((SPItem *) items->data)) {
2450 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2452 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2453 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2454 if (r2 < r1) {
2455 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
2456 } else {
2457 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
2458 }
2460 SP_OBJECT((SPItem *) items->data)->updateRepr();
2461 modmade = true;
2462 }
2463 }
2465 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2466 _("Star: Change spoke ratio"));
2468 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2469 }
2471 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
2472 {
2473 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2474 bool flat = ege_select_one_action_get_active( act ) == 0;
2476 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2477 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2478 prefs->setBool( "/tools/shapes/star/isflatsided", flat);
2479 }
2481 // quit if run by the attr_changed listener
2482 if (g_object_get_data( dataKludge, "freeze" )) {
2483 return;
2484 }
2486 // in turn, prevent listener from responding
2487 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2489 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2490 GSList const *items = selection->itemList();
2491 GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2492 bool modmade = false;
2494 if ( prop_action ) {
2495 gtk_action_set_sensitive( prop_action, !flat );
2496 }
2498 for (; items != NULL; items = items->next) {
2499 if (SP_IS_STAR((SPItem *) items->data)) {
2500 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2501 repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
2502 SP_OBJECT((SPItem *) items->data)->updateRepr();
2503 modmade = true;
2504 }
2505 }
2507 if (modmade) {
2508 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2509 flat ? _("Make polygon") : _("Make star"));
2510 }
2512 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2513 }
2515 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2516 {
2517 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2519 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2520 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2521 prefs->setDouble("/tools/shapes/star/rounded", (gdouble) adj->value);
2522 }
2524 // quit if run by the attr_changed listener
2525 if (g_object_get_data( dataKludge, "freeze" )) {
2526 return;
2527 }
2529 // in turn, prevent listener from responding
2530 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2532 bool modmade = false;
2534 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2535 GSList const *items = selection->itemList();
2536 for (; items != NULL; items = items->next) {
2537 if (SP_IS_STAR((SPItem *) items->data)) {
2538 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2539 sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2540 SP_OBJECT(items->data)->updateRepr();
2541 modmade = true;
2542 }
2543 }
2544 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2545 _("Star: Change rounding"));
2547 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2548 }
2550 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2551 {
2552 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2554 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2555 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2556 prefs->setDouble("/tools/shapes/star/randomized", (gdouble) adj->value);
2557 }
2559 // quit if run by the attr_changed listener
2560 if (g_object_get_data( dataKludge, "freeze" )) {
2561 return;
2562 }
2564 // in turn, prevent listener from responding
2565 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2567 bool modmade = false;
2569 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2570 GSList const *items = selection->itemList();
2571 for (; items != NULL; items = items->next) {
2572 if (SP_IS_STAR((SPItem *) items->data)) {
2573 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2574 sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2575 SP_OBJECT(items->data)->updateRepr();
2576 modmade = true;
2577 }
2578 }
2579 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2580 _("Star: Change randomization"));
2582 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2583 }
2586 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2587 gchar const */*old_value*/, gchar const */*new_value*/,
2588 bool /*is_interactive*/, gpointer data)
2589 {
2590 GtkWidget *tbl = GTK_WIDGET(data);
2592 // quit if run by the _changed callbacks
2593 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2594 return;
2595 }
2597 // in turn, prevent callbacks from responding
2598 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2600 GtkAdjustment *adj = 0;
2602 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2603 bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2605 if (!strcmp(name, "inkscape:randomized")) {
2606 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2607 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2608 } else if (!strcmp(name, "inkscape:rounded")) {
2609 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2610 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2611 } else if (!strcmp(name, "inkscape:flatsided")) {
2612 GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2613 char const *flatsides = repr->attribute("inkscape:flatsided");
2614 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2615 if ( flatsides && !strcmp(flatsides,"false") ) {
2616 ege_select_one_action_set_active( flat_action, 1 );
2617 gtk_action_set_sensitive( prop_action, TRUE );
2618 } else {
2619 ege_select_one_action_set_active( flat_action, 0 );
2620 gtk_action_set_sensitive( prop_action, FALSE );
2621 }
2622 } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2623 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2624 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2625 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2626 if (r2 < r1) {
2627 gtk_adjustment_set_value(adj, r2/r1);
2628 } else {
2629 gtk_adjustment_set_value(adj, r1/r2);
2630 }
2631 } else if (!strcmp(name, "sodipodi:sides")) {
2632 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2633 gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2634 }
2636 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2637 }
2640 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2641 {
2642 NULL, /* child_added */
2643 NULL, /* child_removed */
2644 star_tb_event_attr_changed,
2645 NULL, /* content_changed */
2646 NULL /* order_changed */
2647 };
2650 /**
2651 * \param selection Should not be NULL.
2652 */
2653 static void
2654 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2655 {
2656 int n_selected = 0;
2657 Inkscape::XML::Node *repr = NULL;
2659 purge_repr_listener( tbl, tbl );
2661 for (GSList const *items = selection->itemList();
2662 items != NULL;
2663 items = items->next)
2664 {
2665 if (SP_IS_STAR((SPItem *) items->data)) {
2666 n_selected++;
2667 repr = SP_OBJECT_REPR((SPItem *) items->data);
2668 }
2669 }
2671 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2673 if (n_selected == 0) {
2674 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2675 } else if (n_selected == 1) {
2676 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2678 if (repr) {
2679 g_object_set_data( tbl, "repr", repr );
2680 Inkscape::GC::anchor(repr);
2681 sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2682 sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2683 }
2684 } else {
2685 // FIXME: implement averaging of all parameters for multiple selected stars
2686 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2687 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2688 }
2689 }
2692 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2693 {
2694 // FIXME: in this and all other _default functions, set some flag telling the value_changed
2695 // callbacks to lump all the changes for all selected objects in one undo step
2697 GtkAdjustment *adj = 0;
2699 // fixme: make settable in prefs!
2700 gint mag = 5;
2701 gdouble prop = 0.5;
2702 gboolean flat = FALSE;
2703 gdouble randomized = 0;
2704 gdouble rounded = 0;
2706 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2707 ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2709 GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2710 gtk_action_set_sensitive( sb2, !flat );
2712 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2713 gtk_adjustment_set_value(adj, mag);
2714 gtk_adjustment_value_changed(adj);
2716 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2717 gtk_adjustment_set_value(adj, prop);
2718 gtk_adjustment_value_changed(adj);
2720 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2721 gtk_adjustment_set_value(adj, rounded);
2722 gtk_adjustment_value_changed(adj);
2724 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2725 gtk_adjustment_set_value(adj, randomized);
2726 gtk_adjustment_value_changed(adj);
2727 }
2730 void
2731 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2732 {
2733 GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2734 if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2735 GtkWidget *l = gtk_label_new(NULL);
2736 gtk_label_set_markup(GTK_LABEL(l), title);
2737 gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2738 if ( GTK_IS_TOOLBAR(tbl) ) {
2739 gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2740 } else {
2741 gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2742 }
2743 gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2744 }
2747 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2748 {
2749 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
2751 {
2752 EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2753 ege_output_action_set_use_markup( act, TRUE );
2754 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2755 g_object_set_data( holder, "mode_action", act );
2756 }
2758 {
2759 EgeAdjustmentAction* eact = 0;
2760 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2761 bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2763 /* Flatsided checkbox */
2764 {
2765 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2767 GtkTreeIter iter;
2768 gtk_list_store_append( model, &iter );
2769 gtk_list_store_set( model, &iter,
2770 0, _("Polygon"),
2771 1, _("Regular polygon (with one handle) instead of a star"),
2772 2, "star_flat",
2773 -1 );
2775 gtk_list_store_append( model, &iter );
2776 gtk_list_store_set( model, &iter,
2777 0, _("Star"),
2778 1, _("Star instead of a regular polygon (with one handle)"),
2779 2, "star_angled",
2780 -1 );
2782 EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2783 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2784 g_object_set_data( holder, "flat_action", act );
2786 ege_select_one_action_set_appearance( act, "full" );
2787 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2788 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2789 ege_select_one_action_set_icon_column( act, 2 );
2790 ege_select_one_action_set_icon_size( act, secondarySize );
2791 ege_select_one_action_set_tooltip_column( act, 1 );
2793 ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2794 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2795 }
2797 /* Magnitude */
2798 {
2799 gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2800 gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2801 eact = create_adjustment_action( "MagnitudeAction",
2802 _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2803 "/tools/shapes/star/magnitude", 3,
2804 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2805 3, 1024, 1, 5,
2806 labels, values, G_N_ELEMENTS(labels),
2807 sp_stb_magnitude_value_changed,
2808 1.0, 0 );
2809 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2810 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2811 }
2813 /* Spoke ratio */
2814 {
2815 gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2816 gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2817 eact = create_adjustment_action( "SpokeAction",
2818 _("Spoke ratio"), _("Spoke ratio:"),
2819 // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2820 // Base radius is the same for the closest handle.
2821 _("Base radius to tip radius ratio"),
2822 "/tools/shapes/star/proportion", 0.5,
2823 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2824 0.01, 1.0, 0.01, 0.1,
2825 labels, values, G_N_ELEMENTS(labels),
2826 sp_stb_proportion_value_changed );
2827 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2828 g_object_set_data( holder, "prop_action", eact );
2829 }
2831 if ( !isFlatSided ) {
2832 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2833 } else {
2834 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2835 }
2837 /* Roundedness */
2838 {
2839 gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2840 gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2841 eact = create_adjustment_action( "RoundednessAction",
2842 _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2843 "/tools/shapes/star/rounded", 0.0,
2844 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2845 -10.0, 10.0, 0.01, 0.1,
2846 labels, values, G_N_ELEMENTS(labels),
2847 sp_stb_rounded_value_changed );
2848 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2849 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2850 }
2852 /* Randomization */
2853 {
2854 gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2855 gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2856 eact = create_adjustment_action( "RandomizationAction",
2857 _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2858 "/tools/shapes/star/randomized", 0.0,
2859 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2860 -10.0, 10.0, 0.001, 0.01,
2861 labels, values, G_N_ELEMENTS(labels),
2862 sp_stb_randomized_value_changed, 0.1, 3 );
2863 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2864 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2865 }
2866 }
2868 {
2869 /* Reset */
2870 {
2871 GtkAction* act = gtk_action_new( "StarResetAction",
2872 _("Defaults"),
2873 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2874 GTK_STOCK_CLEAR );
2875 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2876 gtk_action_group_add_action( mainActions, act );
2877 gtk_action_set_sensitive( act, TRUE );
2878 }
2879 }
2881 sigc::connection *connection = new sigc::connection(
2882 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2883 );
2884 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2885 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2886 }
2889 //########################
2890 //## Rect ##
2891 //########################
2893 static void sp_rtb_sensitivize( GObject *tbl )
2894 {
2895 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2896 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2897 GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2899 if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2900 gtk_action_set_sensitive( not_rounded, FALSE );
2901 } else {
2902 gtk_action_set_sensitive( not_rounded, TRUE );
2903 }
2904 }
2907 static void
2908 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2909 void (*setter)(SPRect *, gdouble))
2910 {
2911 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2913 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2914 SPUnit const *unit = tracker->getActiveUnit();
2916 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2917 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2918 prefs->setDouble(Glib::ustring("/tools/shapes/rect/") + value_name, sp_units_get_pixels(adj->value, *unit));
2919 }
2921 // quit if run by the attr_changed listener
2922 if (g_object_get_data( tbl, "freeze" )) {
2923 return;
2924 }
2926 // in turn, prevent listener from responding
2927 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2929 bool modmade = false;
2930 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2931 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2932 if (SP_IS_RECT(items->data)) {
2933 if (adj->value != 0) {
2934 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2935 } else {
2936 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2937 }
2938 modmade = true;
2939 }
2940 }
2942 sp_rtb_sensitivize( tbl );
2944 if (modmade) {
2945 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2946 _("Change rectangle"));
2947 }
2949 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2950 }
2952 static void
2953 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2954 {
2955 sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2956 }
2958 static void
2959 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2960 {
2961 sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2962 }
2964 static void
2965 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2966 {
2967 sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2968 }
2970 static void
2971 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2972 {
2973 sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2974 }
2978 static void
2979 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2980 {
2981 GtkAdjustment *adj = 0;
2983 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2984 gtk_adjustment_set_value(adj, 0.0);
2985 // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2986 gtk_adjustment_value_changed(adj);
2988 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2989 gtk_adjustment_set_value(adj, 0.0);
2990 gtk_adjustment_value_changed(adj);
2992 sp_rtb_sensitivize( obj );
2993 }
2995 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2996 gchar const */*old_value*/, gchar const */*new_value*/,
2997 bool /*is_interactive*/, gpointer data)
2998 {
2999 GObject *tbl = G_OBJECT(data);
3001 // quit if run by the _changed callbacks
3002 if (g_object_get_data( tbl, "freeze" )) {
3003 return;
3004 }
3006 // in turn, prevent callbacks from responding
3007 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3009 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
3010 SPUnit const *unit = tracker->getActiveUnit();
3012 gpointer item = g_object_get_data( tbl, "item" );
3013 if (item && SP_IS_RECT(item)) {
3014 {
3015 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
3016 gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
3017 gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
3018 }
3020 {
3021 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
3022 gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
3023 gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
3024 }
3026 {
3027 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
3028 gdouble width = sp_rect_get_visible_width (SP_RECT(item));
3029 gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
3030 }
3032 {
3033 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
3034 gdouble height = sp_rect_get_visible_height (SP_RECT(item));
3035 gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
3036 }
3037 }
3039 sp_rtb_sensitivize( tbl );
3041 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3042 }
3045 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
3046 NULL, /* child_added */
3047 NULL, /* child_removed */
3048 rect_tb_event_attr_changed,
3049 NULL, /* content_changed */
3050 NULL /* order_changed */
3051 };
3053 /**
3054 * \param selection should not be NULL.
3055 */
3056 static void
3057 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3058 {
3059 int n_selected = 0;
3060 Inkscape::XML::Node *repr = NULL;
3061 SPItem *item = NULL;
3063 if ( g_object_get_data( tbl, "repr" ) ) {
3064 g_object_set_data( tbl, "item", NULL );
3065 }
3066 purge_repr_listener( tbl, tbl );
3068 for (GSList const *items = selection->itemList();
3069 items != NULL;
3070 items = items->next) {
3071 if (SP_IS_RECT((SPItem *) items->data)) {
3072 n_selected++;
3073 item = (SPItem *) items->data;
3074 repr = SP_OBJECT_REPR(item);
3075 }
3076 }
3078 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3080 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3082 if (n_selected == 0) {
3083 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3085 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3086 gtk_action_set_sensitive(w, FALSE);
3087 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3088 gtk_action_set_sensitive(h, FALSE);
3090 } else if (n_selected == 1) {
3091 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3092 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3094 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3095 gtk_action_set_sensitive(w, TRUE);
3096 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3097 gtk_action_set_sensitive(h, TRUE);
3099 if (repr) {
3100 g_object_set_data( tbl, "repr", repr );
3101 g_object_set_data( tbl, "item", item );
3102 Inkscape::GC::anchor(repr);
3103 sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
3104 sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
3105 }
3106 } else {
3107 // FIXME: implement averaging of all parameters for multiple selected
3108 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3109 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3110 sp_rtb_sensitivize( tbl );
3111 }
3112 }
3115 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3116 {
3117 EgeAdjustmentAction* eact = 0;
3118 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3120 {
3121 EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
3122 ege_output_action_set_use_markup( act, TRUE );
3123 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3124 g_object_set_data( holder, "mode_action", act );
3125 }
3127 // rx/ry units menu: create
3128 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
3129 //tracker->addUnit( SP_UNIT_PERCENT, 0 );
3130 // fixme: add % meaning per cent of the width/height
3131 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
3132 g_object_set_data( holder, "tracker", tracker );
3134 /* W */
3135 {
3136 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3137 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3138 eact = create_adjustment_action( "RectWidthAction",
3139 _("Width"), _("W:"), _("Width of rectangle"),
3140 "/tools/shapes/rect/width", 0,
3141 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
3142 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3143 labels, values, G_N_ELEMENTS(labels),
3144 sp_rtb_width_value_changed );
3145 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3146 g_object_set_data( holder, "width_action", eact );
3147 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3148 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3149 }
3151 /* H */
3152 {
3153 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3154 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3155 eact = create_adjustment_action( "RectHeightAction",
3156 _("Height"), _("H:"), _("Height of rectangle"),
3157 "/tools/shapes/rect/height", 0,
3158 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3159 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3160 labels, values, G_N_ELEMENTS(labels),
3161 sp_rtb_height_value_changed );
3162 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3163 g_object_set_data( holder, "height_action", eact );
3164 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3165 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3166 }
3168 /* rx */
3169 {
3170 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3171 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3172 eact = create_adjustment_action( "RadiusXAction",
3173 _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
3174 "/tools/shapes/rect/rx", 0,
3175 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3176 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3177 labels, values, G_N_ELEMENTS(labels),
3178 sp_rtb_rx_value_changed);
3179 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3180 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3181 }
3183 /* ry */
3184 {
3185 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3186 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3187 eact = create_adjustment_action( "RadiusYAction",
3188 _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
3189 "/tools/shapes/rect/ry", 0,
3190 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3191 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3192 labels, values, G_N_ELEMENTS(labels),
3193 sp_rtb_ry_value_changed);
3194 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3195 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3196 }
3198 // add the units menu
3199 {
3200 GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
3201 gtk_action_group_add_action( mainActions, act );
3202 }
3204 /* Reset */
3205 {
3206 InkAction* inky = ink_action_new( "RectResetAction",
3207 _("Not rounded"),
3208 _("Make corners sharp"),
3209 "squared_corner",
3210 secondarySize );
3211 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
3212 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3213 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3214 g_object_set_data( holder, "not_rounded", inky );
3215 }
3217 g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
3218 sp_rtb_sensitivize( holder );
3220 sigc::connection *connection = new sigc::connection(
3221 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
3222 );
3223 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3224 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3225 }
3227 //########################
3228 //## 3D Box ##
3229 //########################
3231 // normalize angle so that it lies in the interval [0,360]
3232 static double box3d_normalize_angle (double a) {
3233 double angle = a + ((int) (a/360.0))*360;
3234 if (angle < 0) {
3235 angle += 360.0;
3236 }
3237 return angle;
3238 }
3240 static void
3241 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
3242 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
3243 // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
3244 // have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
3245 // are reset).
3246 bool is_infinite = !persp3d_VP_is_finite(persp, axis);
3248 if (is_infinite) {
3249 gtk_toggle_action_set_active(tact, TRUE);
3250 gtk_action_set_sensitive(act, TRUE);
3252 double angle = persp3d_get_infinite_angle(persp, axis);
3253 if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
3254 gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
3255 }
3256 } else {
3257 gtk_toggle_action_set_active(tact, FALSE);
3258 gtk_action_set_sensitive(act, FALSE);
3259 }
3260 }
3262 static void
3263 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
3264 if (!persp_repr) {
3265 g_print ("No perspective given to box3d_resync_toolbar().\n");
3266 return;
3267 }
3269 GtkWidget *tbl = GTK_WIDGET(data);
3270 GtkAdjustment *adj = 0;
3271 GtkAction *act = 0;
3272 GtkToggleAction *tact = 0;
3273 Persp3D *persp = persp3d_get_from_repr(persp_repr);
3274 {
3275 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
3276 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
3277 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
3279 box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
3280 }
3281 {
3282 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
3283 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
3284 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
3286 box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
3287 }
3288 {
3289 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
3290 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
3291 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
3293 box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
3294 }
3295 }
3297 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3298 gchar const */*old_value*/, gchar const */*new_value*/,
3299 bool /*is_interactive*/, gpointer data)
3300 {
3301 GtkWidget *tbl = GTK_WIDGET(data);
3303 // quit if run by the attr_changed or selection changed listener
3304 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3305 return;
3306 }
3308 // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
3309 // sp_document_maybe_done() when the document is undo insensitive)
3310 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3312 // TODO: Only update the appropriate part of the toolbar
3313 // if (!strcmp(name, "inkscape:vp_z")) {
3314 box3d_resync_toolbar(repr, G_OBJECT(tbl));
3315 // }
3317 Persp3D *persp = persp3d_get_from_repr(repr);
3318 persp3d_update_box_reprs(persp);
3320 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3321 }
3323 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
3324 {
3325 NULL, /* child_added */
3326 NULL, /* child_removed */
3327 box3d_persp_tb_event_attr_changed,
3328 NULL, /* content_changed */
3329 NULL /* order_changed */
3330 };
3332 /**
3333 * \param selection Should not be NULL.
3334 */
3335 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
3336 // Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
3337 static void
3338 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3339 {
3340 // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
3341 // disable the angle entry fields for this direction (otherwise entering a value in them should only
3342 // update the perspectives with infinite VPs and leave the other ones untouched).
3344 Inkscape::XML::Node *persp_repr = NULL;
3345 purge_repr_listener(tbl, tbl);
3347 SPItem *item = selection->singleItem();
3348 if (item && SP_IS_BOX3D(item)) {
3349 // FIXME: Also deal with multiple selected boxes
3350 SPBox3D *box = SP_BOX3D(item);
3351 Persp3D *persp = box3d_get_perspective(box);
3352 persp_repr = SP_OBJECT_REPR(persp);
3353 if (persp_repr) {
3354 g_object_set_data(tbl, "repr", persp_repr);
3355 Inkscape::GC::anchor(persp_repr);
3356 sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
3357 sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
3358 }
3360 inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
3361 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3362 prefs->setString("/tools/shapes/3dbox/persp", persp_repr->attribute("id"));
3364 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
3365 box3d_resync_toolbar(persp_repr, tbl);
3366 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
3367 }
3368 }
3370 static void
3371 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
3372 {
3373 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3374 SPDocument *document = sp_desktop_document(desktop);
3376 // quit if run by the attr_changed or selection changed listener
3377 if (g_object_get_data( dataKludge, "freeze" )) {
3378 return;
3379 }
3381 // in turn, prevent listener from responding
3382 g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
3384 //Persp3D *persp = document->current_persp3d;
3385 std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
3386 if (sel_persps.empty()) {
3387 // this can happen when the document is created; we silently ignore it
3388 return;
3389 }
3390 Persp3D *persp = sel_persps.front();
3392 persp->tmat.set_infinite_direction (axis, adj->value);
3393 SP_OBJECT(persp)->updateRepr();
3395 // TODO: use the correct axis here, too
3396 sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
3398 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
3399 }
3402 static void
3403 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3404 {
3405 box3d_angle_value_changed(adj, dataKludge, Proj::X);
3406 }
3408 static void
3409 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3410 {
3411 box3d_angle_value_changed(adj, dataKludge, Proj::Y);
3412 }
3414 static void
3415 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3416 {
3417 box3d_angle_value_changed(adj, dataKludge, Proj::Z);
3418 }
3421 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
3422 {
3423 // TODO: Take all selected perspectives into account
3424 std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
3425 if (sel_persps.empty()) {
3426 // this can happen when the document is created; we silently ignore it
3427 return;
3428 }
3429 Persp3D *persp = sel_persps.front();
3431 bool set_infinite = gtk_toggle_action_get_active(act);
3432 persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
3433 }
3435 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3436 {
3437 box3d_vp_state_changed(act, box3d_angle, Proj::X);
3438 }
3440 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3441 {
3442 box3d_vp_state_changed(act, box3d_angle, Proj::Y);
3443 }
3445 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3446 {
3447 box3d_vp_state_changed(act, box3d_angle, Proj::Z);
3448 }
3450 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3451 {
3452 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3453 EgeAdjustmentAction* eact = 0;
3454 SPDocument *document = sp_desktop_document (desktop);
3455 Persp3D *persp = document->current_persp3d;
3457 EgeAdjustmentAction* box3d_angle_x = 0;
3458 EgeAdjustmentAction* box3d_angle_y = 0;
3459 EgeAdjustmentAction* box3d_angle_z = 0;
3461 /* Angle X */
3462 {
3463 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3464 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3465 eact = create_adjustment_action( "3DBoxAngleXAction",
3466 _("Angle in X direction"), _("Angle X:"),
3467 // Translators: PL is short for 'perspective line'
3468 _("Angle of PLs in X direction"),
3469 "/tools/shapes/3dbox/box3d_angle_x", 30,
3470 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
3471 -360.0, 360.0, 1.0, 10.0,
3472 labels, values, G_N_ELEMENTS(labels),
3473 box3d_angle_x_value_changed );
3474 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3475 g_object_set_data( holder, "box3d_angle_x_action", eact );
3476 box3d_angle_x = eact;
3477 }
3479 if (!persp3d_VP_is_finite(persp, Proj::X)) {
3480 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3481 } else {
3482 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3483 }
3486 /* VP X state */
3487 {
3488 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
3489 // Translators: VP is short for 'vanishing point'
3490 _("State of VP in X direction"),
3491 _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
3492 "toggle_vp_x",
3493 Inkscape::ICON_SIZE_DECORATION );
3494 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3495 g_object_set_data( holder, "box3d_vp_x_state_action", act );
3496 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
3497 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3498 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3499 }
3501 /* Angle Y */
3502 {
3503 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3504 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3505 eact = create_adjustment_action( "3DBoxAngleYAction",
3506 _("Angle in Y direction"), _("Angle Y:"),
3507 // Translators: PL is short for 'perspective line'
3508 _("Angle of PLs in Y direction"),
3509 "/tools/shapes/3dbox/box3d_angle_y", 30,
3510 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3511 -360.0, 360.0, 1.0, 10.0,
3512 labels, values, G_N_ELEMENTS(labels),
3513 box3d_angle_y_value_changed );
3514 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3515 g_object_set_data( holder, "box3d_angle_y_action", eact );
3516 box3d_angle_y = eact;
3517 }
3519 if (!persp3d_VP_is_finite(persp, Proj::Y)) {
3520 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3521 } else {
3522 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3523 }
3525 /* VP Y state */
3526 {
3527 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
3528 // Translators: VP is short for 'vanishing point'
3529 _("State of VP in Y direction"),
3530 _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
3531 "toggle_vp_y",
3532 Inkscape::ICON_SIZE_DECORATION );
3533 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3534 g_object_set_data( holder, "box3d_vp_y_state_action", act );
3535 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
3536 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3537 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3538 }
3540 /* Angle Z */
3541 {
3542 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3543 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3544 eact = create_adjustment_action( "3DBoxAngleZAction",
3545 _("Angle in Z direction"), _("Angle Z:"),
3546 // Translators: PL is short for 'perspective line'
3547 _("Angle of PLs in Z direction"),
3548 "/tools/shapes/3dbox/box3d_angle_z", 30,
3549 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3550 -360.0, 360.0, 1.0, 10.0,
3551 labels, values, G_N_ELEMENTS(labels),
3552 box3d_angle_z_value_changed );
3553 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3554 g_object_set_data( holder, "box3d_angle_z_action", eact );
3555 box3d_angle_z = eact;
3556 }
3558 if (!persp3d_VP_is_finite(persp, Proj::Z)) {
3559 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3560 } else {
3561 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3562 }
3564 /* VP Z state */
3565 {
3566 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3567 // Translators: VP is short for 'vanishing point'
3568 _("State of VP in Z direction"),
3569 _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3570 "toggle_vp_z",
3571 Inkscape::ICON_SIZE_DECORATION );
3572 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3573 g_object_set_data( holder, "box3d_vp_z_state_action", act );
3574 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3575 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3576 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3577 }
3579 sigc::connection *connection = new sigc::connection(
3580 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3581 );
3582 g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3583 g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3584 }
3586 //########################
3587 //## Spiral ##
3588 //########################
3590 static void
3591 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, Glib::ustring const &value_name)
3592 {
3593 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3595 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3596 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3597 prefs->setDouble("/tools/shapes/spiral/" + value_name, adj->value);
3598 }
3600 // quit if run by the attr_changed listener
3601 if (g_object_get_data( tbl, "freeze" )) {
3602 return;
3603 }
3605 // in turn, prevent listener from responding
3606 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3608 gchar* namespaced_name = g_strconcat("sodipodi:", value_name.data(), NULL);
3610 bool modmade = false;
3611 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3612 items != NULL;
3613 items = items->next)
3614 {
3615 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3616 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3617 sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3618 SP_OBJECT((SPItem *) items->data)->updateRepr();
3619 modmade = true;
3620 }
3621 }
3623 g_free(namespaced_name);
3625 if (modmade) {
3626 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3627 _("Change spiral"));
3628 }
3630 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3631 }
3633 static void
3634 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3635 {
3636 sp_spl_tb_value_changed(adj, tbl, "revolution");
3637 }
3639 static void
3640 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3641 {
3642 sp_spl_tb_value_changed(adj, tbl, "expansion");
3643 }
3645 static void
3646 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3647 {
3648 sp_spl_tb_value_changed(adj, tbl, "t0");
3649 }
3651 static void
3652 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3653 {
3654 GtkWidget *tbl = GTK_WIDGET(obj);
3656 GtkAdjustment *adj;
3658 // fixme: make settable
3659 gdouble rev = 5;
3660 gdouble exp = 1.0;
3661 gdouble t0 = 0.0;
3663 adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3664 gtk_adjustment_set_value(adj, rev);
3665 gtk_adjustment_value_changed(adj);
3667 adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3668 gtk_adjustment_set_value(adj, exp);
3669 gtk_adjustment_value_changed(adj);
3671 adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3672 gtk_adjustment_set_value(adj, t0);
3673 gtk_adjustment_value_changed(adj);
3675 spinbutton_defocus(GTK_OBJECT(tbl));
3676 }
3679 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3680 gchar const */*old_value*/, gchar const */*new_value*/,
3681 bool /*is_interactive*/, gpointer data)
3682 {
3683 GtkWidget *tbl = GTK_WIDGET(data);
3685 // quit if run by the _changed callbacks
3686 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3687 return;
3688 }
3690 // in turn, prevent callbacks from responding
3691 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3693 GtkAdjustment *adj;
3694 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3695 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3697 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3698 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3700 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3701 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3703 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3704 }
3707 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3708 NULL, /* child_added */
3709 NULL, /* child_removed */
3710 spiral_tb_event_attr_changed,
3711 NULL, /* content_changed */
3712 NULL /* order_changed */
3713 };
3715 static void
3716 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3717 {
3718 int n_selected = 0;
3719 Inkscape::XML::Node *repr = NULL;
3721 purge_repr_listener( tbl, tbl );
3723 for (GSList const *items = selection->itemList();
3724 items != NULL;
3725 items = items->next)
3726 {
3727 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3728 n_selected++;
3729 repr = SP_OBJECT_REPR((SPItem *) items->data);
3730 }
3731 }
3733 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3735 if (n_selected == 0) {
3736 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3737 } else if (n_selected == 1) {
3738 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3740 if (repr) {
3741 g_object_set_data( tbl, "repr", repr );
3742 Inkscape::GC::anchor(repr);
3743 sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3744 sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3745 }
3746 } else {
3747 // FIXME: implement averaging of all parameters for multiple selected
3748 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3749 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3750 }
3751 }
3754 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3755 {
3756 EgeAdjustmentAction* eact = 0;
3757 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3759 {
3760 EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3761 ege_output_action_set_use_markup( act, TRUE );
3762 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3763 g_object_set_data( holder, "mode_action", act );
3764 }
3766 /* Revolution */
3767 {
3768 gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3769 gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3770 eact = create_adjustment_action( "SpiralRevolutionAction",
3771 _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3772 "/tools/shapes/spiral/revolution", 3.0,
3773 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3774 0.01, 1024.0, 0.1, 1.0,
3775 labels, values, G_N_ELEMENTS(labels),
3776 sp_spl_tb_revolution_value_changed, 1, 2);
3777 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3778 }
3780 /* Expansion */
3781 {
3782 gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3783 gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3784 eact = create_adjustment_action( "SpiralExpansionAction",
3785 _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3786 "/tools/shapes/spiral/expansion", 1.0,
3787 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3788 0.0, 1000.0, 0.01, 1.0,
3789 labels, values, G_N_ELEMENTS(labels),
3790 sp_spl_tb_expansion_value_changed);
3791 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3792 }
3794 /* T0 */
3795 {
3796 gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3797 gdouble values[] = {0, 0.5, 0.9};
3798 eact = create_adjustment_action( "SpiralT0Action",
3799 _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3800 "/tools/shapes/spiral/t0", 0.0,
3801 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3802 0.0, 0.999, 0.01, 1.0,
3803 labels, values, G_N_ELEMENTS(labels),
3804 sp_spl_tb_t0_value_changed);
3805 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3806 }
3808 /* Reset */
3809 {
3810 InkAction* inky = ink_action_new( "SpiralResetAction",
3811 _("Defaults"),
3812 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3813 GTK_STOCK_CLEAR,
3814 secondarySize );
3815 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3816 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3817 }
3820 sigc::connection *connection = new sigc::connection(
3821 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3822 );
3823 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3824 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3825 }
3827 //########################
3828 //## Pen/Pencil ##
3829 //########################
3831 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3832 static Glib::ustring const
3833 freehand_tool_name(GObject *dataKludge)
3834 {
3835 SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3836 return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3837 ? "/tools/freehand/pen"
3838 : "/tools/freehand/pencil" );
3839 }
3841 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3842 {
3843 gint mode = ege_select_one_action_get_active(act);
3845 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3846 prefs->setInt(freehand_tool_name(tbl) + "/freehand-mode", mode);
3848 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3850 // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3851 // preparatory work here
3852 if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3853 SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3854 sp_pen_context_set_polyline_mode(pc);
3855 }
3856 }
3858 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3859 {
3860 /* Freehand mode toggle buttons */
3861 {
3862 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3863 guint freehandMode = prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/freehand-mode" : "/tools/freehand/pen/freehand-mode" ), 0);
3864 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3866 {
3867 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3869 GtkTreeIter iter;
3870 gtk_list_store_append( model, &iter );
3871 gtk_list_store_set( model, &iter,
3872 0, _("Bezier"),
3873 1, _("Create regular Bezier path"),
3874 2, "bezier_mode",
3875 -1 );
3877 gtk_list_store_append( model, &iter );
3878 gtk_list_store_set( model, &iter,
3879 0, _("Spiro"),
3880 1, _("Create Spiro path"),
3881 2, "spiro_splines_mode",
3882 -1 );
3884 if (!tool_is_pencil) {
3885 gtk_list_store_append( model, &iter );
3886 gtk_list_store_set( model, &iter,
3887 0, _("Zigzag"),
3888 1, _("Create a sequence of straight line segments"),
3889 2, "polylines_mode",
3890 -1 );
3892 gtk_list_store_append( model, &iter );
3893 gtk_list_store_set( model, &iter,
3894 0, _("Paraxial"),
3895 1, _("Create a sequence of paraxial line segments"),
3896 2, "paraxial_lines_mode",
3897 -1 );
3898 }
3900 EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3901 "FreehandModeActionPencil" :
3902 "FreehandModeActionPen",
3903 (_("Mode:")), ("Mode"), NULL, GTK_TREE_MODEL(model) );
3904 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3906 ege_select_one_action_set_appearance( act, "full" );
3907 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3908 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3909 ege_select_one_action_set_icon_column( act, 2 );
3910 ege_select_one_action_set_icon_size( act, secondarySize );
3911 ege_select_one_action_set_tooltip_column( act, 1 );
3913 ege_select_one_action_set_active( act, freehandMode);
3914 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3915 }
3916 }
3917 }
3919 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3920 gint shape = ege_select_one_action_get_active( act );
3921 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3922 prefs->setInt(freehand_tool_name(dataKludge) + "/shape", shape);
3923 }
3925 /**
3926 * \brief Generate the list of freehand advanced shape option entries.
3927 */
3928 GList * freehand_shape_dropdown_items_list() {
3929 GList *glist = NULL;
3931 glist = g_list_append (glist, _("None"));
3932 glist = g_list_append (glist, _("Triangle in"));
3933 glist = g_list_append (glist, _("Triangle out"));
3934 glist = g_list_append (glist, _("Ellipse"));
3935 glist = g_list_append (glist, _("From clipboard"));
3937 return glist;
3938 }
3940 static void
3941 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3942 /*advanced shape options */
3943 {
3944 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3945 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3947 GList* items = 0;
3948 gint count = 0;
3949 for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3950 {
3951 GtkTreeIter iter;
3952 gtk_list_store_append( model, &iter );
3953 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3954 count++;
3955 }
3956 g_list_free( items );
3957 items = 0;
3958 EgeSelectOneAction* act1 = ege_select_one_action_new(
3959 tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3960 _("Shape:"), ("Shape"), NULL, GTK_TREE_MODEL(model));
3961 g_object_set( act1, "short_label", _("Shape:"), NULL );
3962 ege_select_one_action_set_appearance( act1, "compact" );
3963 ege_select_one_action_set_active( act1, prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/shape" : "/tools/freehand/pen/shape" ), 0) );
3964 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3965 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3966 g_object_set_data( holder, "shape_action", act1 );
3967 }
3968 }
3970 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3971 {
3972 sp_add_freehand_mode_toggle(mainActions, holder, false);
3973 freehand_add_advanced_shape_options(mainActions, holder, false);
3974 }
3977 static void
3978 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3979 {
3980 GtkWidget *tbl = GTK_WIDGET(obj);
3982 GtkAdjustment *adj;
3984 // fixme: make settable
3985 gdouble tolerance = 4;
3987 adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3988 gtk_adjustment_set_value(adj, tolerance);
3989 gtk_adjustment_value_changed(adj);
3991 spinbutton_defocus(GTK_OBJECT(tbl));
3992 }
3994 static void
3995 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3996 {
3997 // quit if run by the attr_changed listener
3998 if (g_object_get_data( tbl, "freeze" )) {
3999 return;
4000 }
4001 // in turn, prevent listener from responding
4002 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4003 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4004 prefs->setDouble("/tools/freehand/pencil/tolerance", adj->value);
4005 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4006 }
4008 class PencilToleranceObserver : public Inkscape::Preferences::Observer {
4009 public:
4010 PencilToleranceObserver(Glib::ustring const &path, GObject *x) : Observer(path), _obj(x)
4011 {
4012 g_object_set_data(_obj, "prefobserver", this);
4013 }
4014 virtual ~PencilToleranceObserver() {
4015 if (g_object_get_data(_obj, "prefobserver") == this) {
4016 g_object_set_data(_obj, "prefobserver", NULL);
4017 }
4018 }
4019 virtual void notify(Inkscape::Preferences::Entry const &val) {
4020 GObject* tbl = _obj;
4021 if (g_object_get_data( tbl, "freeze" )) {
4022 return;
4023 }
4024 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4026 GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl, "tolerance");
4028 double v = val.getDouble(adj->value);
4029 gtk_adjustment_set_value(adj, v);
4030 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4031 }
4032 private:
4033 GObject *_obj;
4034 };
4037 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4038 {
4039 sp_add_freehand_mode_toggle(mainActions, holder, true);
4041 EgeAdjustmentAction* eact = 0;
4043 /* Tolerance */
4044 {
4045 gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
4046 gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
4047 eact = create_adjustment_action( "PencilToleranceAction",
4048 _("Smoothing:"), _("Smoothing: "),
4049 _("How much smoothing (simplifying) is applied to the line"),
4050 "/tools/freehand/pencil/tolerance",
4051 3.0,
4052 GTK_WIDGET(desktop->canvas), NULL,
4053 holder, TRUE, "altx-pencil",
4054 1, 100.0, 0.5, 0,
4055 labels, values, G_N_ELEMENTS(labels),
4056 sp_pencil_tb_tolerance_value_changed,
4057 1, 2);
4058 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4059 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4061 PencilToleranceObserver *obs =
4062 new PencilToleranceObserver("/tools/freehand/pencil/tolerance", G_OBJECT(holder));
4063 }
4065 /* advanced shape options */
4066 freehand_add_advanced_shape_options(mainActions, holder, true);
4068 /* Reset */
4069 {
4070 InkAction* inky = ink_action_new( "PencilResetAction",
4071 _("Defaults"),
4072 _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
4073 GTK_STOCK_CLEAR,
4074 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4075 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
4076 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4077 }
4079 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4081 }
4084 //########################
4085 //## Tweak ##
4086 //########################
4088 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4089 {
4090 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4091 prefs->setDouble( "/tools/tweak/width", adj->value * 0.01 );
4092 }
4094 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4095 {
4096 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4097 prefs->setDouble( "/tools/tweak/force", adj->value * 0.01 );
4098 }
4100 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4101 {
4102 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4103 prefs->setBool("/tools/tweak/usepressure", gtk_toggle_action_get_active(act));
4104 }
4106 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4107 {
4108 int mode = ege_select_one_action_get_active( act );
4109 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4110 prefs->setInt("/tools/tweak/mode", mode);
4112 GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
4113 GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
4114 GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
4115 GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
4116 GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
4117 GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
4118 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
4119 if (doh) gtk_action_set_sensitive (doh, TRUE);
4120 if (dos) gtk_action_set_sensitive (dos, TRUE);
4121 if (dol) gtk_action_set_sensitive (dol, TRUE);
4122 if (doo) gtk_action_set_sensitive (doo, TRUE);
4123 if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
4124 if (fid) gtk_action_set_sensitive (fid, FALSE);
4125 } else {
4126 if (doh) gtk_action_set_sensitive (doh, FALSE);
4127 if (dos) gtk_action_set_sensitive (dos, FALSE);
4128 if (dol) gtk_action_set_sensitive (dol, FALSE);
4129 if (doo) gtk_action_set_sensitive (doo, FALSE);
4130 if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
4131 if (fid) gtk_action_set_sensitive (fid, TRUE);
4132 }
4133 }
4135 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4136 {
4137 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4138 prefs->setDouble( "/tools/tweak/fidelity", adj->value * 0.01 );
4139 }
4141 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
4142 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4143 prefs->setBool("/tools/tweak/doh", gtk_toggle_action_get_active(act));
4144 }
4145 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
4146 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4147 prefs->setBool("/tools/tweak/dos", gtk_toggle_action_get_active(act));
4148 }
4149 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
4150 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4151 prefs->setBool("/tools/tweak/dol", gtk_toggle_action_get_active(act));
4152 }
4153 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
4154 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4155 prefs->setBool("/tools/tweak/doo", gtk_toggle_action_get_active(act));
4156 }
4158 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4159 {
4160 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
4161 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4163 {
4164 /* Width */
4165 gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
4166 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4167 EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
4168 _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
4169 "/tools/tweak/width", 15,
4170 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
4171 1, 100, 1.0, 0.0,
4172 labels, values, G_N_ELEMENTS(labels),
4173 sp_tweak_width_value_changed, 0.01, 0, 100 );
4174 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4175 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4176 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4177 }
4180 {
4181 /* Force */
4182 gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
4183 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4184 EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
4185 _("Force"), _("Force:"), _("The force of the tweak action"),
4186 "/tools/tweak/force", 20,
4187 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
4188 1, 100, 1.0, 0.0,
4189 labels, values, G_N_ELEMENTS(labels),
4190 sp_tweak_force_value_changed, 0.01, 0, 100 );
4191 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4192 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4193 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4194 }
4196 /* Mode */
4197 {
4198 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4200 GtkTreeIter iter;
4201 gtk_list_store_append( model, &iter );
4202 gtk_list_store_set( model, &iter,
4203 0, _("Move mode"),
4204 1, _("Move objects in any direction"),
4205 2, "tweak_move_mode",
4206 -1 );
4208 gtk_list_store_append( model, &iter );
4209 gtk_list_store_set( model, &iter,
4210 0, _("Move in/out mode"),
4211 1, _("Move objects towards cursor; with Shift from cursor"),
4212 2, "tweak_move_mode_inout",
4213 -1 );
4215 gtk_list_store_append( model, &iter );
4216 gtk_list_store_set( model, &iter,
4217 0, _("Move jitter mode"),
4218 1, _("Move objects in random directions"),
4219 2, "tweak_move_mode_jitter",
4220 -1 );
4222 gtk_list_store_append( model, &iter );
4223 gtk_list_store_set( model, &iter,
4224 0, _("Scale mode"),
4225 1, _("Scale objects, with Shift scale up"),
4226 2, "tweak_scale_mode",
4227 -1 );
4229 gtk_list_store_append( model, &iter );
4230 gtk_list_store_set( model, &iter,
4231 0, _("Rotate mode"),
4232 1, _("Rotate objects, with Shift counterclockwise"),
4233 2, "tweak_rotate_mode",
4234 -1 );
4236 gtk_list_store_append( model, &iter );
4237 gtk_list_store_set( model, &iter,
4238 0, _("Duplicate/delete mode"),
4239 1, _("Duplicate objects, with Shift delete"),
4240 2, "tweak_moreless_mode",
4241 -1 );
4243 gtk_list_store_append( model, &iter );
4244 gtk_list_store_set( model, &iter,
4245 0, _("Push mode"),
4246 1, _("Push parts of paths in any direction"),
4247 2, "tweak_push_mode",
4248 -1 );
4250 gtk_list_store_append( model, &iter );
4251 gtk_list_store_set( model, &iter,
4252 0, _("Shrink/grow mode"),
4253 1, _("Shrink (inset) parts of paths; with Shift grow (outset)"),
4254 2, "tweak_shrink_mode",
4255 -1 );
4257 gtk_list_store_append( model, &iter );
4258 gtk_list_store_set( model, &iter,
4259 0, _("Attract/repel mode"),
4260 1, _("Attract parts of paths towards cursor; with Shift from cursor"),
4261 2, "tweak_attract_mode",
4262 -1 );
4264 gtk_list_store_append( model, &iter );
4265 gtk_list_store_set( model, &iter,
4266 0, _("Roughen mode"),
4267 1, _("Roughen parts of paths"),
4268 2, "tweak_roughen_mode",
4269 -1 );
4271 gtk_list_store_append( model, &iter );
4272 gtk_list_store_set( model, &iter,
4273 0, _("Color paint mode"),
4274 1, _("Paint the tool's color upon selected objects"),
4275 2, "tweak_colorpaint_mode",
4276 -1 );
4278 gtk_list_store_append( model, &iter );
4279 gtk_list_store_set( model, &iter,
4280 0, _("Color jitter mode"),
4281 1, _("Jitter the colors of selected objects"),
4282 2, "tweak_colorjitter_mode",
4283 -1 );
4285 gtk_list_store_append( model, &iter );
4286 gtk_list_store_set( model, &iter,
4287 0, _("Blur mode"),
4288 1, _("Blur selected objects more; with Shift, blur less"),
4289 2, "tweak_blur_mode",
4290 -1 );
4293 EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4294 g_object_set( act, "short_label", _("Mode:"), NULL );
4295 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4296 g_object_set_data( holder, "mode_action", act );
4298 ege_select_one_action_set_appearance( act, "full" );
4299 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4300 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4301 ege_select_one_action_set_icon_column( act, 2 );
4302 ege_select_one_action_set_icon_size( act, secondarySize );
4303 ege_select_one_action_set_tooltip_column( act, 1 );
4305 gint mode = prefs->getInt("/tools/tweak/mode", 0);
4306 ege_select_one_action_set_active( act, mode );
4307 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
4309 g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
4310 }
4312 guint mode = prefs->getInt("/tools/tweak/mode", 0);
4314 {
4315 EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
4316 ege_output_action_set_use_markup( act, TRUE );
4317 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4318 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4319 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4320 g_object_set_data( holder, "tweak_channels_label", act);
4321 }
4323 {
4324 InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
4325 _("Hue"),
4326 _("In color mode, act on objects' hue"),
4327 NULL,
4328 Inkscape::ICON_SIZE_DECORATION );
4329 //TRANSLATORS: "H" here stands for hue
4330 g_object_set( act, "short_label", _("H"), NULL );
4331 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4332 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
4333 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doh", true) );
4334 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4335 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4336 g_object_set_data( holder, "tweak_doh", act);
4337 }
4338 {
4339 InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
4340 _("Saturation"),
4341 _("In color mode, act on objects' saturation"),
4342 NULL,
4343 Inkscape::ICON_SIZE_DECORATION );
4344 //TRANSLATORS: "S" here stands for Saturation
4345 g_object_set( act, "short_label", _("S"), NULL );
4346 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4347 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
4348 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dos", true) );
4349 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4350 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4351 g_object_set_data( holder, "tweak_dos", act );
4352 }
4353 {
4354 InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
4355 _("Lightness"),
4356 _("In color mode, act on objects' lightness"),
4357 NULL,
4358 Inkscape::ICON_SIZE_DECORATION );
4359 //TRANSLATORS: "L" here stands for Lightness
4360 g_object_set( act, "short_label", _("L"), NULL );
4361 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4362 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
4363 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dol", true) );
4364 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4365 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4366 g_object_set_data( holder, "tweak_dol", act );
4367 }
4368 {
4369 InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
4370 _("Opacity"),
4371 _("In color mode, act on objects' opacity"),
4372 NULL,
4373 Inkscape::ICON_SIZE_DECORATION );
4374 //TRANSLATORS: "O" here stands for Opacity
4375 g_object_set( act, "short_label", _("O"), NULL );
4376 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4377 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
4378 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doo", true) );
4379 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4380 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4381 g_object_set_data( holder, "tweak_doo", act );
4382 }
4384 { /* Fidelity */
4385 gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
4386 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4387 EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
4388 _("Fidelity"), _("Fidelity:"),
4389 _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
4390 "/tools/tweak/fidelity", 50,
4391 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
4392 1, 100, 1.0, 10.0,
4393 labels, values, G_N_ELEMENTS(labels),
4394 sp_tweak_fidelity_value_changed, 0.01, 0, 100 );
4395 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4396 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4397 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
4398 gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
4399 g_object_set_data( holder, "tweak_fidelity", eact );
4400 }
4403 /* Use Pressure button */
4404 {
4405 InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
4406 _("Pressure"),
4407 _("Use the pressure of the input device to alter the force of tweak action"),
4408 "use_pressure",
4409 Inkscape::ICON_SIZE_DECORATION );
4410 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4411 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
4412 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/usepressure", true) );
4413 }
4415 }
4418 //########################
4419 //## Calligraphy ##
4420 //########################
4421 static void update_presets_list (GObject *tbl)
4422 {
4423 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4424 if (g_object_get_data(tbl, "presets_blocked"))
4425 return;
4427 EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4428 if (!sel) {
4429 // WTF!? This will cause a segfault if ever reached
4430 //ege_select_one_action_set_active(sel, 0);
4431 return;
4432 }
4434 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4436 int ege_index = 1;
4437 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++ege_index) {
4438 bool match = true;
4440 std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(*i);
4441 for (std::vector<Inkscape::Preferences::Entry>::iterator j = preset.begin(); j != preset.end(); ++j) {
4442 Glib::ustring entry_name = j->getEntryName();
4443 if (entry_name == "id" || entry_name == "name") continue;
4445 void *widget = g_object_get_data(tbl, entry_name.data());
4446 if (widget) {
4447 if (GTK_IS_ADJUSTMENT(widget)) {
4448 double v = j->getDouble();
4449 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4450 //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
4451 if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
4452 match = false;
4453 break;
4454 }
4455 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4456 bool v = j->getBool();
4457 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4458 //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
4459 if ( static_cast<bool>(gtk_toggle_action_get_active(toggle)) != v ) {
4460 match = false;
4461 break;
4462 }
4463 }
4464 }
4465 }
4467 if (match) {
4468 // newly added item is at the same index as the
4469 // save command, so we need to change twice for it to take effect
4470 ege_select_one_action_set_active(sel, 0);
4471 ege_select_one_action_set_active(sel, ege_index); // one-based index
4472 return;
4473 }
4474 }
4476 // no match found
4477 ege_select_one_action_set_active(sel, 0);
4478 }
4480 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
4481 {
4482 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4483 prefs->setDouble( "/tools/calligraphic/mass", adj->value * 0.01 );
4484 update_presets_list(tbl);
4485 }
4487 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
4488 {
4489 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4490 prefs->setDouble( "/tools/calligraphic/wiggle", adj->value * 0.01 );
4491 update_presets_list(tbl);
4492 }
4494 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
4495 {
4496 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4497 prefs->setDouble( "/tools/calligraphic/angle", adj->value );
4498 update_presets_list(tbl);
4499 }
4501 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
4502 {
4503 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4504 prefs->setDouble( "/tools/calligraphic/width", adj->value * 0.01 );
4505 update_presets_list(tbl);
4506 }
4508 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
4509 {
4510 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4511 prefs->setDouble("/tools/calligraphic/thinning", adj->value * 0.01 );
4512 update_presets_list(tbl);
4513 }
4515 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
4516 {
4517 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4518 prefs->setDouble( "/tools/calligraphic/flatness", adj->value * 0.01);
4519 update_presets_list(tbl);
4520 }
4522 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
4523 {
4524 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4525 prefs->setDouble( "/tools/calligraphic/tremor", adj->value * 0.01 );
4526 update_presets_list(tbl);
4527 }
4529 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
4530 {
4531 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4532 prefs->setDouble( "/tools/calligraphic/cap_rounding", adj->value );
4533 update_presets_list(tbl);
4534 }
4536 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject* tbl )
4537 {
4538 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4539 prefs->setBool("/tools/calligraphic/usepressure", gtk_toggle_action_get_active( act ));
4540 update_presets_list(tbl);
4541 }
4543 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject* tbl )
4544 {
4545 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4546 prefs->setBool("/tools/calligraphic/tracebackground", gtk_toggle_action_get_active( act ));
4547 update_presets_list(tbl);
4548 }
4550 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject* tbl )
4551 {
4552 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4553 GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
4554 prefs->setBool("/tools/calligraphic/usetilt", gtk_toggle_action_get_active( act ));
4555 update_presets_list(tbl);
4556 if (calligraphy_angle )
4557 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
4558 }
4561 static gchar const *const widget_names[] = {
4562 "width",
4563 "mass",
4564 "wiggle",
4565 "angle",
4566 "thinning",
4567 "tremor",
4568 "flatness",
4569 "cap_rounding",
4570 "usepressure",
4571 "tracebackground",
4572 "usetilt"
4573 };
4576 static void sp_dcc_build_presets_list(GObject *tbl)
4577 {
4578 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4580 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4581 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4582 gtk_list_store_clear (model);
4584 {
4585 GtkTreeIter iter;
4586 gtk_list_store_append( model, &iter );
4587 gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4588 }
4590 // iterate over all presets to populate the list
4591 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4592 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4593 int ii=1;
4595 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i) {
4596 GtkTreeIter iter;
4597 Glib::ustring preset_name = prefs->getString(*i + "/name");
4598 gtk_list_store_append( model, &iter );
4599 gtk_list_store_set( model, &iter, 0, preset_name.data(), 1, ii++, -1 );
4600 }
4602 {
4603 GtkTreeIter iter;
4604 gtk_list_store_append( model, &iter );
4605 gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4606 g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4607 }
4609 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4611 update_presets_list (tbl);
4612 }
4614 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4615 {
4616 using Inkscape::UI::Dialog::CalligraphicProfileRename;
4617 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4618 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4619 if (! desktop) return;
4621 if (g_object_get_data(tbl, "presets_blocked"))
4622 return;
4624 CalligraphicProfileRename::show(desktop);
4625 if ( !CalligraphicProfileRename::applied()) {
4626 // dialog cancelled
4627 update_presets_list (tbl);
4628 return;
4629 }
4630 Glib::ustring profile_name = CalligraphicProfileRename::getProfileName();
4632 if (profile_name.empty()) {
4633 // empty name entered
4634 update_presets_list (tbl);
4635 return;
4636 }
4638 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4640 // If there's a preset with the given name, find it and set save_path appropriately
4641 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4642 int total_presets = presets.size();
4643 int new_index = -1;
4644 Glib::ustring save_path; // profile pref path without a trailing slash
4646 int temp_index = 0;
4647 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++temp_index) {
4648 Glib::ustring name = prefs->getString(*i + "/name");
4649 if (!name.empty() && profile_name == name) {
4650 new_index = temp_index;
4651 save_path = *i;
4652 break;
4653 }
4654 }
4656 if (new_index == -1) {
4657 // no preset with this name, create
4658 new_index = total_presets + 1;
4659 gchar *profile_id = g_strdup_printf("/dcc%d", new_index);
4660 save_path = Glib::ustring("/tools/calligraphic/preset") + profile_id;
4661 g_free(profile_id);
4662 }
4664 for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4665 gchar const *const widget_name = widget_names[i];
4666 void *widget = g_object_get_data(tbl, widget_name);
4667 if (widget) {
4668 if (GTK_IS_ADJUSTMENT(widget)) {
4669 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4670 prefs->setDouble(save_path + "/" + widget_name, gtk_adjustment_get_value(adj));
4671 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4672 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4673 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4674 prefs->setBool(save_path + "/" + widget_name, gtk_toggle_action_get_active(toggle));
4675 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4676 } else {
4677 g_warning("Unknown widget type for preset: %s\n", widget_name);
4678 }
4679 } else {
4680 g_warning("Bad key when writing preset: %s\n", widget_name);
4681 }
4682 }
4683 prefs->setString(save_path + "/name", profile_name);
4685 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4686 sp_dcc_build_presets_list (tbl);
4687 }
4690 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4692 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4694 gint preset_index = ege_select_one_action_get_active( act );
4695 // This is necessary because EgeSelectOneAction spams us with GObject "changed" signal calls
4696 // even when the preset is not changed. It would be good to replace it with something more
4697 // modern. Index 0 means "No preset", so we don't do anything.
4698 if (preset_index == 0) return;
4700 gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4702 if (preset_index == save_presets_index) {
4703 // this is the Save command
4704 sp_dcc_save_profile(NULL, tbl);
4705 return;
4706 }
4708 if (g_object_get_data(tbl, "presets_blocked"))
4709 return;
4711 // preset_index is one-based so we subtract 1
4712 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4713 Glib::ustring preset_path = presets.at(preset_index - 1);
4715 if (!preset_path.empty()) {
4716 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
4718 std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(preset_path);
4720 // Shouldn't this be std::map?
4721 for (std::vector<Inkscape::Preferences::Entry>::iterator i = preset.begin(); i != preset.end(); ++i) {
4722 Glib::ustring entry_name = i->getEntryName();
4723 if (entry_name == "id" || entry_name == "name") continue;
4724 void *widget = g_object_get_data(tbl, entry_name.data());
4725 if (widget) {
4726 if (GTK_IS_ADJUSTMENT(widget)) {
4727 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4728 gtk_adjustment_set_value(adj, i->getDouble());
4729 //std::cout << "set adj " << attr_name << " to " << v << "\n";
4730 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4731 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4732 gtk_toggle_action_set_active(toggle, i->getBool());
4733 //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4734 } else {
4735 g_warning("Unknown widget type for preset: %s\n", entry_name.data());
4736 }
4737 } else {
4738 g_warning("Bad key found in a preset record: %s\n", entry_name.data());
4739 }
4740 }
4741 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4742 }
4743 }
4746 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4747 {
4748 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4749 {
4750 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4752 EgeAdjustmentAction* calligraphy_angle = 0;
4754 {
4755 /* Width */
4756 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4757 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4758 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4759 _("Pen Width"), _("Width:"),
4760 _("The width of the calligraphic pen (relative to the visible canvas area)"),
4761 "/tools/calligraphic/width", 15,
4762 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4763 1, 100, 1.0, 0.0,
4764 labels, values, G_N_ELEMENTS(labels),
4765 sp_ddc_width_value_changed, 0.01, 0, 100 );
4766 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4767 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4768 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4769 }
4771 {
4772 /* Thinning */
4773 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4774 gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4775 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4776 _("Stroke Thinning"), _("Thinning:"),
4777 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4778 "/tools/calligraphic/thinning", 10,
4779 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4780 -100, 100, 1, 0.1,
4781 labels, values, G_N_ELEMENTS(labels),
4782 sp_ddc_velthin_value_changed, 0.01, 0, 100);
4783 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4784 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4785 }
4787 {
4788 /* Angle */
4789 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4790 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4791 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4792 _("Pen Angle"), _("Angle:"),
4793 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4794 "/tools/calligraphic/angle", 30,
4795 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4796 -90.0, 90.0, 1.0, 10.0,
4797 labels, values, G_N_ELEMENTS(labels),
4798 sp_ddc_angle_value_changed, 1, 0 );
4799 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4800 g_object_set_data( holder, "angle_action", eact );
4801 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4802 calligraphy_angle = eact;
4803 }
4805 {
4806 /* Fixation */
4807 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4808 gdouble values[] = {0, 20, 40, 60, 90, 100};
4809 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4810 _("Fixation"), _("Fixation:"),
4811 _("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"),
4812 "/tools/calligraphic/flatness", 90,
4813 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4814 0.0, 100, 1.0, 10.0,
4815 labels, values, G_N_ELEMENTS(labels),
4816 sp_ddc_flatness_value_changed, 0.01, 0, 100 );
4817 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4818 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4819 }
4821 {
4822 /* Cap Rounding */
4823 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4824 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4825 // TRANSLATORS: "cap" means "end" (both start and finish) here
4826 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4827 _("Cap rounding"), _("Caps:"),
4828 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4829 "/tools/calligraphic/cap_rounding", 0.0,
4830 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4831 0.0, 5.0, 0.01, 0.1,
4832 labels, values, G_N_ELEMENTS(labels),
4833 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4834 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4835 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4836 }
4838 {
4839 /* Tremor */
4840 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4841 gdouble values[] = {0, 10, 20, 40, 60, 100};
4842 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4843 _("Stroke Tremor"), _("Tremor:"),
4844 _("Increase to make strokes rugged and trembling"),
4845 "/tools/calligraphic/tremor", 0.0,
4846 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4847 0.0, 100, 1, 0.0,
4848 labels, values, G_N_ELEMENTS(labels),
4849 sp_ddc_tremor_value_changed, 0.01, 0, 100 );
4851 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4852 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4853 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4854 }
4856 {
4857 /* Wiggle */
4858 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4859 gdouble values[] = {0, 20, 40, 60, 100};
4860 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4861 _("Pen Wiggle"), _("Wiggle:"),
4862 _("Increase to make the pen waver and wiggle"),
4863 "/tools/calligraphic/wiggle", 0.0,
4864 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4865 0.0, 100, 1, 0.0,
4866 labels, values, G_N_ELEMENTS(labels),
4867 sp_ddc_wiggle_value_changed, 0.01, 0, 100 );
4868 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4869 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4870 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4871 }
4873 {
4874 /* Mass */
4875 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4876 gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4877 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4878 _("Pen Mass"), _("Mass:"),
4879 _("Increase to make the pen drag behind, as if slowed by inertia"),
4880 "/tools/calligraphic/mass", 2.0,
4881 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4882 0.0, 100, 1, 0.0,
4883 labels, values, G_N_ELEMENTS(labels),
4884 sp_ddc_mass_value_changed, 0.01, 0, 100 );
4885 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4886 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4887 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4888 }
4891 /* Trace Background button */
4892 {
4893 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4894 _("Trace Background"),
4895 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4896 "trace_background",
4897 Inkscape::ICON_SIZE_DECORATION );
4898 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4899 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4900 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/tracebackground", false) );
4901 g_object_set_data( holder, "tracebackground", act );
4902 }
4904 /* Use Pressure button */
4905 {
4906 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4907 _("Pressure"),
4908 _("Use the pressure of the input device to alter the width of the pen"),
4909 "use_pressure",
4910 Inkscape::ICON_SIZE_DECORATION );
4911 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4912 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4913 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usepressure", true) );
4914 g_object_set_data( holder, "usepressure", act );
4915 }
4917 /* Use Tilt button */
4918 {
4919 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4920 _("Tilt"),
4921 _("Use the tilt of the input device to alter the angle of the pen's nib"),
4922 "use_tilt",
4923 Inkscape::ICON_SIZE_DECORATION );
4924 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4925 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4926 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs->getBool("/tools/calligraphic/usetilt", true) );
4927 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usetilt", true) );
4928 g_object_set_data( holder, "usetilt", act );
4929 }
4931 /*calligraphic profile */
4932 {
4933 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
4934 EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
4935 ege_select_one_action_set_appearance (act1, "compact");
4936 g_object_set_data (holder, "profile_selector", act1 );
4938 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
4940 sp_dcc_build_presets_list (holder);
4942 g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
4943 gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
4944 }
4945 }
4946 }
4949 //########################
4950 //## Circle / Arc ##
4951 //########################
4953 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4954 {
4955 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4956 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4958 if (v1 == 0 && v2 == 0) {
4959 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4960 gtk_action_set_sensitive( ocb, FALSE );
4961 gtk_action_set_sensitive( make_whole, FALSE );
4962 }
4963 } else {
4964 gtk_action_set_sensitive( ocb, TRUE );
4965 gtk_action_set_sensitive( make_whole, TRUE );
4966 }
4967 }
4969 static void
4970 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4971 {
4972 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4974 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4975 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4976 prefs->setDouble(Glib::ustring("/tools/shapes/arc") + value_name, (adj->value * M_PI)/ 180);
4977 }
4979 // quit if run by the attr_changed listener
4980 if (g_object_get_data( tbl, "freeze" )) {
4981 return;
4982 }
4984 // in turn, prevent listener from responding
4985 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4987 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4989 bool modmade = false;
4990 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4991 items != NULL;
4992 items = items->next)
4993 {
4994 SPItem *item = SP_ITEM(items->data);
4996 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4998 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4999 SPArc *arc = SP_ARC(item);
5001 if (!strcmp(value_name, "start"))
5002 ge->start = (adj->value * M_PI)/ 180;
5003 else
5004 ge->end = (adj->value * M_PI)/ 180;
5006 sp_genericellipse_normalize(ge);
5007 ((SPObject *)arc)->updateRepr();
5008 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
5010 modmade = true;
5011 }
5012 }
5014 g_free(namespaced_name);
5016 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
5018 sp_arctb_sensitivize( tbl, adj->value, other->value );
5020 if (modmade) {
5021 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
5022 _("Arc: Change start/end"));
5023 }
5025 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5026 }
5029 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
5030 {
5031 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
5032 }
5034 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
5035 {
5036 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
5037 }
5040 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
5041 {
5042 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5043 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5044 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5045 prefs->setBool("/tools/shapes/arc/open", ege_select_one_action_get_active(act) != 0);
5046 }
5048 // quit if run by the attr_changed listener
5049 if (g_object_get_data( tbl, "freeze" )) {
5050 return;
5051 }
5053 // in turn, prevent listener from responding
5054 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5056 bool modmade = false;
5058 if ( ege_select_one_action_get_active(act) != 0 ) {
5059 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5060 items != NULL;
5061 items = items->next)
5062 {
5063 if (SP_IS_ARC((SPItem *) items->data)) {
5064 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5065 repr->setAttribute("sodipodi:open", "true");
5066 SP_OBJECT((SPItem *) items->data)->updateRepr();
5067 modmade = true;
5068 }
5069 }
5070 } else {
5071 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5072 items != NULL;
5073 items = items->next)
5074 {
5075 if (SP_IS_ARC((SPItem *) items->data)) {
5076 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5077 repr->setAttribute("sodipodi:open", NULL);
5078 SP_OBJECT((SPItem *) items->data)->updateRepr();
5079 modmade = true;
5080 }
5081 }
5082 }
5084 if (modmade) {
5085 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
5086 _("Arc: Change open/closed"));
5087 }
5089 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5090 }
5092 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
5093 {
5094 GtkAdjustment *adj;
5095 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
5096 gtk_adjustment_set_value(adj, 0.0);
5097 gtk_adjustment_value_changed(adj);
5099 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
5100 gtk_adjustment_set_value(adj, 0.0);
5101 gtk_adjustment_value_changed(adj);
5103 spinbutton_defocus( GTK_OBJECT(obj) );
5104 }
5106 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
5107 gchar const */*old_value*/, gchar const */*new_value*/,
5108 bool /*is_interactive*/, gpointer data)
5109 {
5110 GObject *tbl = G_OBJECT(data);
5112 // quit if run by the _changed callbacks
5113 if (g_object_get_data( tbl, "freeze" )) {
5114 return;
5115 }
5117 // in turn, prevent callbacks from responding
5118 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5120 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
5121 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
5123 GtkAdjustment *adj1,*adj2;
5124 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
5125 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
5126 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
5127 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
5129 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
5131 char const *openstr = NULL;
5132 openstr = repr->attribute("sodipodi:open");
5133 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
5135 if (openstr) {
5136 ege_select_one_action_set_active( ocb, 1 );
5137 } else {
5138 ege_select_one_action_set_active( ocb, 0 );
5139 }
5141 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5142 }
5144 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
5145 NULL, /* child_added */
5146 NULL, /* child_removed */
5147 arc_tb_event_attr_changed,
5148 NULL, /* content_changed */
5149 NULL /* order_changed */
5150 };
5153 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
5154 {
5155 int n_selected = 0;
5156 Inkscape::XML::Node *repr = NULL;
5158 purge_repr_listener( tbl, tbl );
5160 for (GSList const *items = selection->itemList();
5161 items != NULL;
5162 items = items->next)
5163 {
5164 if (SP_IS_ARC((SPItem *) items->data)) {
5165 n_selected++;
5166 repr = SP_OBJECT_REPR((SPItem *) items->data);
5167 }
5168 }
5170 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
5172 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
5173 if (n_selected == 0) {
5174 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
5175 } else if (n_selected == 1) {
5176 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
5177 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5179 if (repr) {
5180 g_object_set_data( tbl, "repr", repr );
5181 Inkscape::GC::anchor(repr);
5182 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
5183 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
5184 }
5185 } else {
5186 // FIXME: implement averaging of all parameters for multiple selected
5187 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
5188 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5189 sp_arctb_sensitivize( tbl, 1, 0 );
5190 }
5191 }
5194 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5195 {
5196 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5198 EgeAdjustmentAction* eact = 0;
5199 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
5202 {
5203 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
5204 ege_output_action_set_use_markup( act, TRUE );
5205 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5206 g_object_set_data( holder, "mode_action", act );
5207 }
5209 /* Start */
5210 {
5211 eact = create_adjustment_action( "ArcStartAction",
5212 _("Start"), _("Start:"),
5213 _("The angle (in degrees) from the horizontal to the arc's start point"),
5214 "/tools/shapes/arc/start", 0.0,
5215 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
5216 -360.0, 360.0, 1.0, 10.0,
5217 0, 0, 0,
5218 sp_arctb_start_value_changed);
5219 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5220 }
5222 /* End */
5223 {
5224 eact = create_adjustment_action( "ArcEndAction",
5225 _("End"), _("End:"),
5226 _("The angle (in degrees) from the horizontal to the arc's end point"),
5227 "/tools/shapes/arc/end", 0.0,
5228 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
5229 -360.0, 360.0, 1.0, 10.0,
5230 0, 0, 0,
5231 sp_arctb_end_value_changed);
5232 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5233 }
5235 /* Segments / Pie checkbox */
5236 {
5237 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5239 GtkTreeIter iter;
5240 gtk_list_store_append( model, &iter );
5241 gtk_list_store_set( model, &iter,
5242 0, _("Closed arc"),
5243 1, _("Switch to segment (closed shape with two radii)"),
5244 2, "circle_closed_arc",
5245 -1 );
5247 gtk_list_store_append( model, &iter );
5248 gtk_list_store_set( model, &iter,
5249 0, _("Open Arc"),
5250 1, _("Switch to arc (unclosed shape)"),
5251 2, "circle_open_arc",
5252 -1 );
5254 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5255 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5256 g_object_set_data( holder, "open_action", act );
5258 ege_select_one_action_set_appearance( act, "full" );
5259 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5260 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5261 ege_select_one_action_set_icon_column( act, 2 );
5262 ege_select_one_action_set_icon_size( act, secondarySize );
5263 ege_select_one_action_set_tooltip_column( act, 1 );
5265 bool isClosed = !prefs->getBool("/tools/shapes/arc/open", false);
5266 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
5267 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
5268 }
5270 /* Make Whole */
5271 {
5272 InkAction* inky = ink_action_new( "ArcResetAction",
5273 _("Make whole"),
5274 _("Make the shape a whole ellipse, not arc or segment"),
5275 "reset_circle",
5276 secondarySize );
5277 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
5278 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5279 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
5280 g_object_set_data( holder, "make_whole", inky );
5281 }
5283 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
5284 // sensitivize make whole and open checkbox
5285 {
5286 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
5287 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
5288 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
5289 }
5292 sigc::connection *connection = new sigc::connection(
5293 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
5294 );
5295 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
5296 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
5297 }
5302 // toggle button callbacks and updaters
5304 //########################
5305 //## Dropper ##
5306 //########################
5308 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
5309 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5310 prefs->setInt( "/tools/dropper/pick", gtk_toggle_action_get_active( act ) );
5311 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
5312 if ( set_action ) {
5313 if ( gtk_toggle_action_get_active( act ) ) {
5314 gtk_action_set_sensitive( set_action, TRUE );
5315 } else {
5316 gtk_action_set_sensitive( set_action, FALSE );
5317 }
5318 }
5320 spinbutton_defocus(GTK_OBJECT(tbl));
5321 }
5323 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
5324 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5325 prefs->setBool( "/tools/dropper/setalpha", gtk_toggle_action_get_active( act ) );
5326 spinbutton_defocus(GTK_OBJECT(tbl));
5327 }
5330 /**
5331 * Dropper auxiliary toolbar construction and setup.
5332 *
5333 * TODO: Would like to add swatch of current color.
5334 * TODO: Add queue of last 5 or so colors selected with new swatches so that
5335 * can drag and drop places. Will provide a nice mixing palette.
5336 */
5337 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
5338 {
5339 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5340 gint pickAlpha = prefs->getInt( "/tools/dropper/pick", 1 );
5342 {
5343 EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
5344 ege_output_action_set_use_markup( act, TRUE );
5345 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5346 }
5348 {
5349 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
5350 _("Pick opacity"),
5351 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
5352 NULL,
5353 Inkscape::ICON_SIZE_DECORATION );
5354 g_object_set( act, "short_label", _("Pick"), NULL );
5355 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5356 g_object_set_data( holder, "pick_action", act );
5357 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
5358 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
5359 }
5361 {
5362 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
5363 _("Assign opacity"),
5364 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
5365 NULL,
5366 Inkscape::ICON_SIZE_DECORATION );
5367 g_object_set( act, "short_label", _("Assign"), NULL );
5368 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5369 g_object_set_data( holder, "set_action", act );
5370 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/dropper/setalpha", true) );
5371 // make sure it's disabled if we're not picking alpha
5372 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
5373 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
5374 }
5375 }
5378 //########################
5379 //## LPETool ##
5380 //########################
5382 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
5384 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
5385 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
5386 {
5387 using namespace Inkscape::LivePathEffect;
5389 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
5390 SPEventContext *ec = desktop->event_context;
5391 if (!SP_IS_LPETOOL_CONTEXT(ec)) {
5392 return;
5393 }
5395 // only take action if run by the attr_changed listener
5396 if (!g_object_get_data(tbl, "freeze")) {
5397 // in turn, prevent listener from responding
5398 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5400 gint mode = ege_select_one_action_get_active(act);
5401 EffectType type = lpesubtools[mode];
5403 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5404 bool success = lpetool_try_construction(lc, type);
5405 if (success) {
5406 // since the construction was already performed, we set the state back to inactive
5407 ege_select_one_action_set_active(act, 0);
5408 mode = 0;
5409 } else {
5410 // switch to the chosen subtool
5411 SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
5412 }
5414 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5415 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5416 prefs->setInt( "/tools/lpetool/mode", mode );
5417 }
5419 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
5420 }
5421 }
5423 void sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject */*tbl*/)
5424 {
5425 SPEventContext *ec = selection->desktop()->event_context;
5426 if (!SP_IS_LPETOOL_CONTEXT(ec))
5427 return;
5429 lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
5430 }
5432 void
5433 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
5434 {
5435 using namespace Inkscape::LivePathEffect;
5436 SPEventContext *ec = selection->desktop()->event_context;
5437 if (!SP_IS_LPETOOL_CONTEXT(ec))
5438 return;
5439 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
5441 lpetool_delete_measuring_items(lc);
5442 lpetool_create_measuring_items(lc, selection);
5444 // activate line segment combo box if a single item with LPELineSegment is selected
5445 GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
5446 SPItem *item = selection->singleItem();
5447 if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
5448 SPLPEItem *lpeitem = SP_LPE_ITEM(item);
5449 Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
5450 if (lpe && lpe->effectType() == LINE_SEGMENT) {
5451 LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
5452 g_object_set_data(tbl, "currentlpe", lpe);
5453 g_object_set_data(tbl, "currentlpeitem", lpeitem);
5454 gtk_action_set_sensitive(w, TRUE);
5455 ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
5456 } else {
5457 g_object_set_data(tbl, "currentlpe", NULL);
5458 g_object_set_data(tbl, "currentlpeitem", NULL);
5459 gtk_action_set_sensitive(w, FALSE);
5460 }
5461 } else {
5462 g_object_set_data(tbl, "currentlpe", NULL);
5463 g_object_set_data(tbl, "currentlpeitem", NULL);
5464 gtk_action_set_sensitive(w, FALSE);
5465 }
5466 }
5468 static void
5469 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
5470 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5471 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5473 bool show = gtk_toggle_action_get_active( act );
5474 prefs->setBool("/tools/lpetool/show_bbox", show);
5476 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5477 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5478 lpetool_context_reset_limiting_bbox(lc);
5479 }
5480 }
5482 static void
5483 lpetool_toggle_show_measuring_info (GtkToggleAction *act, GObject *tbl) {
5484 SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
5485 if (!tools_isactive(desktop, TOOLS_LPETOOL))
5486 return;
5488 GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
5489 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5490 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5491 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5492 bool show = gtk_toggle_action_get_active( act );
5493 prefs->setBool("/tools/lpetool/show_measuring_info", show);
5494 lpetool_show_measuring_info(lc, show);
5495 gtk_action_set_sensitive(GTK_ACTION(unitact), show);
5496 }
5497 }
5499 static void lpetool_unit_changed(GtkAction* /*act*/, GObject* tbl) {
5500 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
5501 SPUnit const *unit = tracker->getActiveUnit();
5502 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5503 prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5505 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5506 if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
5507 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5508 lpetool_delete_measuring_items(lc);
5509 lpetool_create_measuring_items(lc);
5510 }
5511 }
5513 static void
5514 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
5515 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5516 Inkscape::Selection *selection = desktop->selection;
5518 Geom::OptRect bbox = selection->bounds();
5520 if (bbox) {
5521 Geom::Point A(bbox->min());
5522 Geom::Point B(bbox->max());
5524 A *= desktop->doc2dt();
5525 B *= desktop->doc2dt();
5527 // TODO: should we provide a way to store points in prefs?
5528 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5529 prefs->setDouble("/tools/lpetool/bbox_upperleftx", A[Geom::X]);
5530 prefs->setDouble("/tools/lpetool/bbox_upperlefty", A[Geom::Y]);
5531 prefs->setDouble("/tools/lpetool/bbox_lowerrightx", B[Geom::X]);
5532 prefs->setDouble("/tools/lpetool/bbox_lowerrighty", B[Geom::Y]);
5534 lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
5535 }
5537 gtk_toggle_action_set_active(act, false);
5538 }
5540 static void
5541 sp_line_segment_build_list(GObject *tbl)
5542 {
5543 g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
5545 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
5546 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
5547 gtk_list_store_clear (model);
5549 // TODO: we add the entries of rht combo box manually; later this should be done automatically
5550 {
5551 GtkTreeIter iter;
5552 gtk_list_store_append( model, &iter );
5553 gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
5554 gtk_list_store_append( model, &iter );
5555 gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
5556 gtk_list_store_append( model, &iter );
5557 gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
5558 gtk_list_store_append( model, &iter );
5559 gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
5560 }
5562 g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5563 }
5565 static void
5566 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
5567 using namespace Inkscape::LivePathEffect;
5569 // quit if run by the attr_changed listener
5570 if (g_object_get_data(tbl, "freeze")) {
5571 return;
5572 }
5574 // in turn, prevent listener from responding
5575 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5577 LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
5578 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5579 if (lpeitem) {
5580 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5581 lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
5582 sp_lpe_item_update_patheffect(lpeitem, true, true);
5583 }
5585 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5586 }
5588 static void
5589 lpetool_open_lpe_dialog (GtkToggleAction *act, gpointer data) {
5590 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5592 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5593 sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5594 }
5595 gtk_toggle_action_set_active(act, false);
5596 }
5598 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5599 {
5600 UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5601 tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5602 g_object_set_data(holder, "tracker", tracker);
5603 SPUnit const *unit = tracker->getActiveUnit();
5605 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5606 prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5608 /** Automatically create a list of LPEs that get added to the toolbar **/
5609 {
5610 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5612 GtkTreeIter iter;
5614 // the first toggle button represents the state that no subtool is active (remove this when
5615 // this can be modeled by EgeSelectOneAction or some other action)
5616 gtk_list_store_append( model, &iter );
5617 gtk_list_store_set( model, &iter,
5618 0, _("All inactive"),
5619 1, _("No geometric tool is active"),
5620 2, _("all_inactive"),
5621 -1 );
5623 Inkscape::LivePathEffect::EffectType type;
5624 for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5625 type = lpesubtools[i];
5626 gtk_list_store_append( model, &iter );
5627 gtk_list_store_set( model, &iter,
5628 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5629 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5630 2, Inkscape::LivePathEffect::LPETypeConverter.get_key(type).c_str(),
5631 -1 );
5632 }
5634 EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5635 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5636 g_object_set_data( holder, "lpetool_mode_action", act );
5638 ege_select_one_action_set_appearance( act, "full" );
5639 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5640 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5641 ege_select_one_action_set_icon_column( act, 2 );
5642 ege_select_one_action_set_tooltip_column( act, 1 );
5644 gint lpeToolMode = prefs->getInt("/tools/lpetool/mode", 0);
5645 ege_select_one_action_set_active( act, lpeToolMode );
5646 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5647 }
5649 /* Show limiting bounding box */
5650 {
5651 InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5652 _("Show limiting bounding box"),
5653 _("Show bounding box (used to cut infinite lines)"),
5654 "lpetool_show_bbox",
5655 Inkscape::ICON_SIZE_DECORATION );
5656 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5657 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5658 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_bbox", true ) );
5659 }
5661 /* Set limiting bounding box to bbox of current selection */
5662 {
5663 InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5664 _("Get limiting bounding box from selection"),
5665 _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5666 "lpetool_set_bbox",
5667 Inkscape::ICON_SIZE_DECORATION );
5668 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5669 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5670 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5671 }
5674 /* Combo box to choose line segment type */
5675 {
5676 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5677 EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5678 ege_select_one_action_set_appearance (act, "compact");
5679 g_object_set_data (holder, "lpetool_line_segment_action", act );
5681 g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5683 sp_line_segment_build_list (holder);
5685 g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5686 gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5687 gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5688 }
5690 /* Display measuring info for selected items */
5691 {
5692 InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5693 _("Display measuring info"),
5694 _("Display measuring info for selected items"),
5695 "lpetool_measuring_info",
5696 Inkscape::ICON_SIZE_DECORATION );
5697 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5698 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
5699 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_measuring_info", true ) );
5700 }
5702 // add the units menu
5703 {
5704 GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5705 gtk_action_group_add_action( mainActions, act );
5706 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5707 g_object_set_data(holder, "lpetool_units_action", act);
5708 gtk_action_set_sensitive(act, prefs->getBool("/tools/lpetool/show_measuring_info", true));
5709 }
5711 /* Open LPE dialog (to adapt parameters numerically) */
5712 {
5713 InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
5714 _("Open LPE dialog"),
5715 _("Open LPE dialog (to adapt parameters numerically)"),
5716 "lpetool_open_lpe_dialog",
5717 Inkscape::ICON_SIZE_DECORATION );
5718 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5719 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
5720 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5721 }
5723 //watch selection
5724 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5726 sigc::connection *c_selection_modified =
5727 new sigc::connection (sp_desktop_selection (desktop)->connectModified
5728 (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
5729 pool->add_connection ("selection-modified", c_selection_modified);
5731 sigc::connection *c_selection_changed =
5732 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5733 (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5734 pool->add_connection ("selection-changed", c_selection_changed);
5735 }
5737 //########################
5738 //## Eraser ##
5739 //########################
5741 static void sp_erc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
5742 {
5743 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5744 prefs->setDouble( "/tools/eraser/width", adj->value * 0.01 );
5745 update_presets_list(tbl);
5746 }
5748 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5749 {
5750 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5751 bool eraserMode = ege_select_one_action_get_active( act ) != 0;
5752 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5753 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5754 prefs->setBool( "/tools/eraser/mode", eraserMode );
5755 }
5757 // only take action if run by the attr_changed listener
5758 if (!g_object_get_data( tbl, "freeze" )) {
5759 // in turn, prevent listener from responding
5760 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5762 if ( eraserMode != 0 ) {
5763 } else {
5764 }
5765 // TODO finish implementation
5767 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5768 }
5769 }
5771 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5772 {
5773 {
5774 /* Width */
5775 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5776 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5777 EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5778 _("Pen Width"), _("Width:"),
5779 _("The width of the eraser pen (relative to the visible canvas area)"),
5780 "/tools/eraser/width", 15,
5781 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5782 1, 100, 1.0, 0.0,
5783 labels, values, G_N_ELEMENTS(labels),
5784 sp_erc_width_value_changed, 0.01, 0, 100 );
5785 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5786 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5787 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5788 }
5790 {
5791 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5793 GtkTreeIter iter;
5794 gtk_list_store_append( model, &iter );
5795 gtk_list_store_set( model, &iter,
5796 0, _("Delete"),
5797 1, _("Delete objects touched by the eraser"),
5798 2, "delete_object",
5799 -1 );
5801 gtk_list_store_append( model, &iter );
5802 gtk_list_store_set( model, &iter,
5803 0, _("Cut"),
5804 1, _("Cut out from objects"),
5805 2, "difference",
5806 -1 );
5808 EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5809 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5810 g_object_set_data( holder, "eraser_mode_action", act );
5812 ege_select_one_action_set_appearance( act, "full" );
5813 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5814 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5815 ege_select_one_action_set_icon_column( act, 2 );
5816 ege_select_one_action_set_tooltip_column( act, 1 );
5818 /// @todo Convert to boolean?
5819 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5820 gint eraserMode = prefs->getBool("/tools/eraser/mode") ? 1 : 0;
5821 ege_select_one_action_set_active( act, eraserMode );
5822 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
5823 }
5825 }
5827 //########################
5828 //## Text Toolbox ##
5829 //########################
5830 /*
5831 static void
5832 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
5833 {
5834 //Call back for letter sizing spinbutton
5835 }
5837 static void
5838 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
5839 {
5840 //Call back for line height spinbutton
5841 }
5843 static void
5844 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5845 {
5846 //Call back for horizontal kerning spinbutton
5847 }
5849 static void
5850 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5851 {
5852 //Call back for vertical kerning spinbutton
5853 }
5855 static void
5856 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
5857 {
5858 //Call back for letter rotation spinbutton
5859 }*/
5861 namespace {
5863 bool popdown_visible = false;
5864 bool popdown_hasfocus = false;
5866 void
5867 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
5868 {
5869 SPStyle *query =
5870 sp_style_new (SP_ACTIVE_DOCUMENT);
5872 // int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5874 int result_family =
5875 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5877 int result_style =
5878 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5880 int result_numbers =
5881 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5883 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5885 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5886 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
5887 // there are no texts in selection, read from prefs
5889 sp_style_read_from_prefs(query, "/tools/text");
5891 if (g_object_get_data(tbl, "text_style_from_prefs")) {
5892 // do not reset the toolbar style from prefs if we already did it last time
5893 sp_style_unref(query);
5894 return;
5895 }
5896 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
5897 } else {
5898 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
5899 }
5901 if (query->text)
5902 {
5903 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
5904 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5905 gtk_entry_set_text (GTK_ENTRY (entry), "");
5907 } else if (query->text->font_specification.value || query->text->font_family.value) {
5909 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5911 // Get the font that corresponds
5912 Glib::ustring familyName;
5914 font_instance * font = font_factory::Default()->FaceFromStyle(query);
5915 if (font) {
5916 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
5917 font->Unref();
5918 font = NULL;
5919 }
5921 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
5923 Gtk::TreePath path;
5924 try {
5925 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
5926 } catch (...) {
5927 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
5928 sp_style_unref(query);
5929 return;
5930 }
5932 GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5933 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5935 g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
5937 gtk_tree_selection_select_path (tselection, path.gobj());
5938 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5940 g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
5941 }
5943 //Size
5944 {
5945 GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
5946 gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
5947 g_object_set_data(tbl, "size-block", gpointer(1));
5948 gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
5949 g_object_set_data(tbl, "size-block", gpointer(0));
5950 g_free(str);
5951 }
5953 //Anchor
5954 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
5955 {
5956 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
5957 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5958 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5959 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5960 }
5961 else
5962 {
5963 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
5964 {
5965 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
5966 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5967 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5968 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5969 }
5970 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
5971 {
5972 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
5973 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5974 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5975 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5976 }
5977 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
5978 {
5979 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
5980 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5981 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5982 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5983 }
5984 }
5986 //Style
5987 {
5988 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
5990 gboolean active = gtk_toggle_button_get_active (button);
5991 gboolean check = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
5993 if (active != check)
5994 {
5995 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5996 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5997 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5998 }
5999 }
6001 {
6002 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
6004 gboolean active = gtk_toggle_button_get_active (button);
6005 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
6007 if (active != check)
6008 {
6009 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6010 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6011 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6012 }
6013 }
6015 //Orientation
6016 //locking both buttons, changing one affect all group (both)
6017 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
6018 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6020 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
6021 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
6023 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
6024 {
6025 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6026 }
6027 else
6028 {
6029 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
6030 }
6031 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6032 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
6033 }
6035 sp_style_unref(query);
6036 }
6038 void
6039 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
6040 {
6041 sp_text_toolbox_selection_changed (selection, tbl);
6042 }
6044 void
6045 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
6046 {
6047 sp_text_toolbox_selection_changed (NULL, tbl);
6048 }
6050 void
6051 sp_text_toolbox_family_changed (GtkTreeSelection *selection,
6052 GObject *tbl)
6053 {
6054 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6055 GtkTreeModel *model = 0;
6056 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6057 GtkTreeIter iter;
6058 char *family = 0;
6060 gdk_pointer_ungrab (GDK_CURRENT_TIME);
6061 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
6063 if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
6064 return;
6065 }
6067 gtk_tree_model_get (model, &iter, 0, &family, -1);
6069 if (g_object_get_data (G_OBJECT (selection), "block"))
6070 {
6071 gtk_entry_set_text (GTK_ENTRY (entry), family);
6072 return;
6073 }
6075 gtk_entry_set_text (GTK_ENTRY (entry), family);
6077 SPStyle *query =
6078 sp_style_new (SP_ACTIVE_DOCUMENT);
6080 int result_fontspec =
6081 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6083 //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6085 SPCSSAttr *css = sp_repr_css_attr_new ();
6088 // First try to get the font spec from the stored value
6089 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
6091 if (fontSpec.empty()) {
6092 // Construct a new font specification if it does not yet exist
6093 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6094 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6095 fontFromStyle->Unref();
6096 }
6098 if (!fontSpec.empty()) {
6099 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
6100 if (!newFontSpec.empty() && fontSpec != newFontSpec) {
6101 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
6102 if (font) {
6103 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6105 // Set all the these just in case they were altered when finding the best
6106 // match for the new family and old style...
6108 gchar c[256];
6110 font->Family(c, 256);
6111 sp_repr_css_set_property (css, "font-family", c);
6113 font->Attribute( "weight", c, 256);
6114 sp_repr_css_set_property (css, "font-weight", c);
6116 font->Attribute("style", c, 256);
6117 sp_repr_css_set_property (css, "font-style", c);
6119 font->Attribute("stretch", c, 256);
6120 sp_repr_css_set_property (css, "font-stretch", c);
6122 font->Attribute("variant", c, 256);
6123 sp_repr_css_set_property (css, "font-variant", c);
6125 font->Unref();
6126 }
6127 }
6128 }
6130 // If querying returned nothing, set the default style of the tool (for new texts)
6131 if (result_fontspec == QUERY_STYLE_NOTHING)
6132 {
6133 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6134 prefs->setStyle("/tools/text/style", css);
6135 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
6136 }
6137 else
6138 {
6139 sp_desktop_set_style (desktop, css, true, true);
6140 }
6142 sp_style_unref(query);
6144 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6145 _("Text: Change font family"));
6146 sp_repr_css_attr_unref (css);
6147 g_free(family);
6148 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6150 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6151 }
6153 /* This is where execution comes when the contents of the font family box have been completed
6154 by the press of the return key */
6155 void
6156 sp_text_toolbox_family_entry_activate (GtkEntry *entry,
6157 GObject *tbl)
6158 {
6159 const char *family = gtk_entry_get_text (entry); // Fetch the requested font family
6161 // Try to match that to a known font. If not, then leave current font alone and remain focused on text box
6162 try {
6163 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
6164 GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
6165 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
6166 gtk_tree_selection_select_path (selection, path.gobj());
6167 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
6168 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6169 } catch (...) {
6170 if (family && strlen (family))
6171 {
6172 gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6173 }
6174 }
6175 }
6177 void
6178 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
6179 gpointer data)
6180 {
6181 if (g_object_get_data (G_OBJECT (button), "block")) return;
6182 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
6183 int prop = GPOINTER_TO_INT(data);
6185 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6186 SPCSSAttr *css = sp_repr_css_attr_new ();
6188 switch (prop)
6189 {
6190 case 0:
6191 {
6192 sp_repr_css_set_property (css, "text-anchor", "start");
6193 sp_repr_css_set_property (css, "text-align", "start");
6194 break;
6195 }
6196 case 1:
6197 {
6198 sp_repr_css_set_property (css, "text-anchor", "middle");
6199 sp_repr_css_set_property (css, "text-align", "center");
6200 break;
6201 }
6203 case 2:
6204 {
6205 sp_repr_css_set_property (css, "text-anchor", "end");
6206 sp_repr_css_set_property (css, "text-align", "end");
6207 break;
6208 }
6210 case 3:
6211 {
6212 sp_repr_css_set_property (css, "text-anchor", "start");
6213 sp_repr_css_set_property (css, "text-align", "justify");
6214 break;
6215 }
6216 }
6218 SPStyle *query =
6219 sp_style_new (SP_ACTIVE_DOCUMENT);
6220 int result_numbers =
6221 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6223 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6224 if (result_numbers == QUERY_STYLE_NOTHING)
6225 {
6226 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6227 prefs->setStyle("/tools/text/style", css);
6228 }
6230 sp_style_unref(query);
6232 sp_desktop_set_style (desktop, css, true, true);
6233 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6234 _("Text: Change alignment"));
6235 sp_repr_css_attr_unref (css);
6237 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6238 }
6240 void
6241 sp_text_toolbox_style_toggled (GtkToggleButton *button,
6242 gpointer data)
6243 {
6244 if (g_object_get_data (G_OBJECT (button), "block")) return;
6246 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6247 SPCSSAttr *css = sp_repr_css_attr_new ();
6248 int prop = GPOINTER_TO_INT(data);
6249 bool active = gtk_toggle_button_get_active (button);
6251 SPStyle *query =
6252 sp_style_new (SP_ACTIVE_DOCUMENT);
6254 int result_fontspec =
6255 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6257 //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6258 //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6259 //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6261 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
6262 Glib::ustring newFontSpec = "";
6264 if (fontSpec.empty()) {
6265 // Construct a new font specification if it does not yet exist
6266 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6267 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6268 fontFromStyle->Unref();
6269 }
6271 switch (prop)
6272 {
6273 case 0:
6274 {
6275 if (!fontSpec.empty()) {
6276 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
6277 }
6278 if (fontSpec != newFontSpec) {
6279 // Don't even set the bold if the font didn't exist on the system
6280 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
6281 }
6282 break;
6283 }
6285 case 1:
6286 {
6287 if (!fontSpec.empty()) {
6288 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
6289 }
6290 if (fontSpec != newFontSpec) {
6291 // Don't even set the italic if the font didn't exist on the system
6292 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
6293 }
6294 break;
6295 }
6296 }
6298 if (!newFontSpec.empty()) {
6299 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6300 }
6302 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6303 if (result_fontspec == QUERY_STYLE_NOTHING)
6304 {
6305 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6306 prefs->setStyle("/tools/text/style", css);
6307 }
6309 sp_style_unref(query);
6311 sp_desktop_set_style (desktop, css, true, true);
6312 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6313 _("Text: Change font style"));
6314 sp_repr_css_attr_unref (css);
6316 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6317 }
6319 void
6320 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
6321 gpointer data)
6322 {
6323 if (g_object_get_data (G_OBJECT (button), "block")) {
6324 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6325 return;
6326 }
6328 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6329 SPCSSAttr *css = sp_repr_css_attr_new ();
6330 int prop = GPOINTER_TO_INT(data);
6332 switch (prop)
6333 {
6334 case 0:
6335 {
6336 sp_repr_css_set_property (css, "writing-mode", "lr");
6337 break;
6338 }
6340 case 1:
6341 {
6342 sp_repr_css_set_property (css, "writing-mode", "tb");
6343 break;
6344 }
6345 }
6347 SPStyle *query =
6348 sp_style_new (SP_ACTIVE_DOCUMENT);
6349 int result_numbers =
6350 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6352 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6353 if (result_numbers == QUERY_STYLE_NOTHING)
6354 {
6355 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6356 prefs->setStyle("/tools/text/style", css);
6357 }
6359 sp_desktop_set_style (desktop, css, true, true);
6360 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6361 _("Text: Change orientation"));
6362 sp_repr_css_attr_unref (css);
6364 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6365 }
6367 gboolean
6368 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6369 {
6370 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6371 if (!desktop) return FALSE;
6373 switch (get_group0_keyval (event)) {
6374 case GDK_Escape: // defocus
6375 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6376 sp_text_toolbox_selection_changed (NULL, tbl); // update
6377 return TRUE; // I consumed the event
6378 break;
6379 }
6380 return FALSE;
6381 }
6383 gboolean
6384 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
6385 {
6386 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6387 if (!desktop) return FALSE;
6389 switch (get_group0_keyval (event)) {
6390 case GDK_KP_Enter:
6391 case GDK_Return:
6392 case GDK_Escape: // defocus
6393 gtk_widget_hide (w);
6394 popdown_visible = false;
6395 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6396 return TRUE; // I consumed the event
6397 break;
6398 case GDK_w:
6399 case GDK_W:
6400 if (event->state & GDK_CONTROL_MASK) {
6401 gtk_widget_hide (w);
6402 popdown_visible = false;
6403 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6404 return TRUE; // I consumed the event
6405 }
6406 break;
6407 }
6408 return FALSE;
6409 }
6412 void
6413 sp_text_toolbox_size_changed (GtkComboBox *cbox,
6414 GObject *tbl)
6415 {
6416 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6418 if (g_object_get_data (tbl, "size-block")) return;
6420 // If this is not from selecting a size in the list (in which case get_active will give the
6421 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
6422 // process this event. This fixes GTK's stupid insistence on sending an activate change every
6423 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
6424 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
6425 return;
6427 gdouble value = -1;
6428 {
6429 gchar *endptr;
6430 gchar *const text = gtk_combo_box_get_active_text(cbox);
6431 if (text) {
6432 value = g_strtod(text, &endptr);
6433 if (endptr == text) { // Conversion failed, non-numeric input.
6434 value = -1;
6435 }
6436 g_free(text);
6437 }
6438 }
6439 if (value <= 0) {
6440 return; // could not parse value
6441 }
6443 SPCSSAttr *css = sp_repr_css_attr_new ();
6444 Inkscape::CSSOStringStream osfs;
6445 osfs << value;
6446 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
6448 SPStyle *query =
6449 sp_style_new (SP_ACTIVE_DOCUMENT);
6450 int result_numbers =
6451 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6453 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6454 if (result_numbers == QUERY_STYLE_NOTHING)
6455 {
6456 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6457 prefs->setStyle("/tools/text/style", css);
6458 }
6460 sp_style_unref(query);
6462 sp_desktop_set_style (desktop, css, true, true);
6463 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
6464 _("Text: Change font size"));
6465 sp_repr_css_attr_unref (css);
6467 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6468 }
6470 gboolean
6471 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
6472 {
6473 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6474 if (!desktop) return FALSE;
6476 if (!g_object_get_data (tbl, "esc-pressed")) {
6477 g_object_set_data (tbl, "enter-pressed", gpointer(1));
6478 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6479 sp_text_toolbox_size_changed (cbox, tbl);
6480 g_object_set_data (tbl, "enter-pressed", gpointer(0));
6481 }
6482 return FALSE; // I consumed the event
6483 }
6486 gboolean
6487 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6488 {
6489 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6490 if (!desktop) return FALSE;
6492 switch (get_group0_keyval (event)) {
6493 case GDK_Escape: // defocus
6494 g_object_set_data (tbl, "esc-pressed", gpointer(1));
6495 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6496 g_object_set_data (tbl, "esc-pressed", gpointer(0));
6497 return TRUE; // I consumed the event
6498 break;
6499 case GDK_Return: // defocus
6500 case GDK_KP_Enter:
6501 g_object_set_data (tbl, "enter-pressed", gpointer(1));
6502 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6503 sp_text_toolbox_size_changed (cbox, tbl);
6504 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6505 g_object_set_data (tbl, "enter-pressed", gpointer(0));
6506 return TRUE; // I consumed the event
6507 break;
6508 }
6509 return FALSE;
6510 }
6512 void
6513 sp_text_toolbox_text_popdown_clicked (GtkButton */*button*/,
6514 GObject *tbl)
6515 {
6516 GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
6517 GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6518 int x, y;
6520 if (!popdown_visible)
6521 {
6522 gdk_window_get_origin (widget->window, &x, &y);
6523 gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
6524 gtk_widget_show_all (popdown);
6525 //sp_transientize (popdown);
6527 gdk_pointer_grab (widget->window, TRUE,
6528 GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
6529 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
6530 GDK_POINTER_MOTION_MASK),
6531 NULL, NULL, GDK_CURRENT_TIME);
6533 gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
6535 popdown_visible = true;
6536 }
6537 else
6538 {
6539 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6540 gdk_pointer_ungrab (GDK_CURRENT_TIME);
6541 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
6542 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6543 gtk_widget_hide (popdown);
6544 popdown_visible = false;
6545 }
6546 }
6548 gboolean
6549 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
6550 GdkEventFocus */*event*/,
6551 GObject */*tbl*/)
6552 {
6553 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
6554 return FALSE;
6555 }
6557 gboolean
6558 sp_text_toolbox_popdown_focus_out (GtkWidget *popdown,
6559 GdkEventFocus */*event*/,
6560 GObject */*tbl*/)
6561 {
6562 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6564 if (popdown_hasfocus) {
6565 gtk_widget_hide (popdown);
6566 popdown_hasfocus = false;
6567 popdown_visible = false;
6568 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6569 return TRUE;
6570 }
6571 return FALSE;
6572 }
6574 gboolean
6575 sp_text_toolbox_popdown_focus_in (GtkWidget */*popdown*/,
6576 GdkEventFocus */*event*/,
6577 GObject */*tbl*/)
6578 {
6579 popdown_hasfocus = true;
6580 return TRUE;
6581 }
6584 void
6585 cell_data_func (GtkTreeViewColumn */*column*/,
6586 GtkCellRenderer *cell,
6587 GtkTreeModel *tree_model,
6588 GtkTreeIter *iter,
6589 gpointer /*data*/)
6590 {
6591 gchar *family;
6592 gtk_tree_model_get(tree_model, iter, 0, &family, -1);
6593 gchar *const family_escaped = g_markup_escape_text(family, -1);
6595 static char const *const sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
6596 gchar *const sample_escaped = g_markup_escape_text(sample, -1);
6598 std::stringstream markup;
6599 markup << family_escaped << " <span foreground='darkgray' font_family='"
6600 << family_escaped << "'>" << sample_escaped << "</span>";
6601 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6603 g_free(family);
6604 g_free(family_escaped);
6605 g_free(sample_escaped);
6606 }
6608 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
6609 GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
6610 if (completion) {
6611 gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
6612 g_object_unref (completion);
6613 }
6614 }
6616 GtkWidget*
6617 sp_text_toolbox_new (SPDesktop *desktop)
6618 {
6619 GtkToolbar *tbl = GTK_TOOLBAR(gtk_toolbar_new());
6620 GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("/toolbox/secondary", 1));
6622 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
6623 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
6625 GtkTooltips *tt = gtk_tooltips_new();
6626 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
6628 ////////////Family
6629 //Window
6630 GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
6631 gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
6633 //Entry
6634 GtkWidget *entry = gtk_entry_new ();
6635 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
6636 GtkEntryCompletion *completion = gtk_entry_completion_new ();
6637 gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
6638 gtk_entry_completion_set_text_column (completion, 0);
6639 gtk_entry_completion_set_minimum_key_length (completion, 1);
6640 g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
6641 gtk_entry_set_completion (GTK_ENTRY(entry), completion);
6642 gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
6643 gtk_toolbar_append_widget( tbl, entry, "", "" );
6644 g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
6646 //Button
6647 GtkWidget *button = gtk_button_new ();
6648 gtk_container_add (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
6649 gtk_toolbar_append_widget( tbl, button, "", "");
6651 //Popdown
6652 GtkWidget *sw = gtk_scrolled_window_new (NULL, NULL);
6653 GtkWidget *treeview = gtk_tree_view_new ();
6655 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
6656 GtkTreeViewColumn *column = gtk_tree_view_column_new ();
6657 gtk_tree_view_column_pack_start (column, cell, FALSE);
6658 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
6659 gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
6660 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
6662 gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
6663 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
6664 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
6666 //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
6668 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
6669 gtk_container_add (GTK_CONTAINER (sw), treeview);
6671 gtk_container_add (GTK_CONTAINER (window), sw);
6672 gtk_widget_set_size_request (window, 300, 450);
6674 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
6675 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
6676 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
6678 g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
6680 g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
6681 g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
6682 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
6684 GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
6685 g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6687 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
6688 g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
6689 g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
6690 g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
6691 g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
6693 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
6694 GtkWidget *box = gtk_event_box_new ();
6695 gtk_container_add (GTK_CONTAINER (box), image);
6696 gtk_toolbar_append_widget( tbl, box, "", "");
6697 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
6698 GtkTooltips *tooltips = gtk_tooltips_new ();
6699 gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
6700 gtk_widget_hide (GTK_WIDGET (box));
6701 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
6703 ////////////Size
6704 gchar const *const sizes[] = {
6705 "4", "6", "8", "9", "10", "11", "12", "13", "14",
6706 "16", "18", "20", "22", "24", "28",
6707 "32", "36", "40", "48", "56", "64", "72", "144"
6708 };
6710 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
6711 for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
6712 gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
6713 }
6714 gtk_widget_set_size_request (cbox, 80, -1);
6715 gtk_toolbar_append_widget( tbl, cbox, "", "");
6716 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
6717 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
6718 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
6719 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
6721 ////////////Text anchor
6722 GtkWidget *group = gtk_radio_button_new (NULL);
6723 GtkWidget *row = gtk_hbox_new (FALSE, 4);
6724 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
6726 // left
6727 GtkWidget *rbutton = group;
6728 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6729 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
6730 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6732 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6733 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
6734 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
6735 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
6737 // center
6738 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6739 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6740 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
6741 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6743 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6744 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
6745 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
6746 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
6748 // right
6749 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6750 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6751 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
6752 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6754 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6755 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
6756 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
6757 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
6759 // fill
6760 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6761 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6762 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
6763 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6765 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6766 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
6767 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
6768 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
6770 gtk_toolbar_append_widget( tbl, row, "", "");
6772 //spacer
6773 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6775 ////////////Text style
6776 row = gtk_hbox_new (FALSE, 4);
6778 // bold
6779 rbutton = gtk_toggle_button_new ();
6780 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6781 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
6782 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6783 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
6785 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6786 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
6787 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
6789 // italic
6790 rbutton = gtk_toggle_button_new ();
6791 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6792 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
6793 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6794 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
6796 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6797 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
6798 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
6800 gtk_toolbar_append_widget( tbl, row, "", "");
6802 //spacer
6803 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6805 ////////////Text orientation
6806 group = gtk_radio_button_new (NULL);
6807 row = gtk_hbox_new (FALSE, 4);
6808 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
6810 // horizontal
6811 rbutton = group;
6812 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6813 gtk_container_add (GTK_CONTAINER (rbutton),
6814 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
6815 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6816 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
6818 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6819 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
6820 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
6822 // vertical
6823 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6824 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6825 gtk_container_add (GTK_CONTAINER (rbutton),
6826 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
6827 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6828 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
6830 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6831 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
6832 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
6833 gtk_toolbar_append_widget( tbl, row, "", "" );
6836 //watch selection
6837 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
6839 sigc::connection *c_selection_changed =
6840 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
6841 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
6842 pool->add_connection ("selection-changed", c_selection_changed);
6844 sigc::connection *c_selection_modified =
6845 new sigc::connection (sp_desktop_selection (desktop)->connectModified
6846 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
6847 pool->add_connection ("selection-modified", c_selection_modified);
6849 sigc::connection *c_subselection_changed =
6850 new sigc::connection (desktop->connectToolSubselectionChanged
6851 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
6852 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
6854 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
6857 gtk_widget_show_all( GTK_WIDGET(tbl) );
6859 return GTK_WIDGET(tbl);
6860 } // end of sp_text_toolbox_new()
6862 }//<unnamed> namespace
6865 //#########################
6866 //## Connector ##
6867 //#########################
6869 static void sp_connector_path_set_avoid(void)
6870 {
6871 cc_selection_set_avoid(true);
6872 }
6875 static void sp_connector_path_set_ignore(void)
6876 {
6877 cc_selection_set_avoid(false);
6878 }
6882 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
6883 {
6884 // quit if run by the _changed callbacks
6885 if (g_object_get_data( tbl, "freeze" )) {
6886 return;
6887 }
6889 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
6890 SPDocument *doc = sp_desktop_document(desktop);
6892 if (!sp_document_get_undo_sensitive(doc))
6893 {
6894 return;
6895 }
6897 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6899 if ( repr->attribute("inkscape:connector-spacing") ) {
6900 gdouble priorValue = gtk_adjustment_get_value(adj);
6901 sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
6902 if ( priorValue == gtk_adjustment_get_value(adj) ) {
6903 return;
6904 }
6905 } else if ( adj->value == defaultConnSpacing ) {
6906 return;
6907 }
6909 // in turn, prevent callbacks from responding
6910 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6912 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
6913 SP_OBJECT(desktop->namedview)->updateRepr();
6915 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
6916 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
6917 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
6918 Geom::Matrix m = Geom::identity();
6919 avoid_item_move(&m, item);
6920 }
6922 if (items) {
6923 g_slist_free(items);
6924 }
6926 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
6927 _("Change connector spacing"));
6929 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6931 spinbutton_defocus(GTK_OBJECT(tbl));
6932 }
6934 static void sp_connector_graph_layout(void)
6935 {
6936 if (!SP_ACTIVE_DESKTOP) return;
6937 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6939 // hack for clones, see comment in align-and-distribute.cpp
6940 int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
6941 prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
6943 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
6945 prefs->setInt("/options/clonecompensation/value", saved_compensation);
6947 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
6948 }
6950 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6951 {
6952 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6953 prefs->setBool("/tools/connector/directedlayout",
6954 gtk_toggle_action_get_active( act ));
6955 }
6957 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6958 {
6959 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6960 prefs->setBool("/tools/connector/avoidoverlaplayout",
6961 gtk_toggle_action_get_active( act ));
6962 }
6965 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
6966 {
6967 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6968 prefs->setDouble("/tools/connector/length", adj->value);
6969 spinbutton_defocus(GTK_OBJECT(tbl));
6970 }
6972 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
6973 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
6974 bool /*is_interactive*/, gpointer data)
6975 {
6976 GtkWidget *tbl = GTK_WIDGET(data);
6978 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6979 return;
6980 }
6981 if (strcmp(name, "inkscape:connector-spacing") != 0) {
6982 return;
6983 }
6985 GtkAdjustment *adj = (GtkAdjustment*)
6986 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
6987 gdouble spacing = defaultConnSpacing;
6988 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
6990 gtk_adjustment_set_value(adj, spacing);
6991 }
6994 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
6995 NULL, /* child_added */
6996 NULL, /* child_removed */
6997 connector_tb_event_attr_changed,
6998 NULL, /* content_changed */
6999 NULL /* order_changed */
7000 };
7003 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
7004 {
7005 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7006 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
7008 {
7009 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
7010 _("Avoid"),
7011 _("Make connectors avoid selected objects"),
7012 "connector_avoid",
7013 secondarySize );
7014 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
7015 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7016 }
7018 {
7019 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
7020 _("Ignore"),
7021 _("Make connectors ignore selected objects"),
7022 "connector_ignore",
7023 secondarySize );
7024 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
7025 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7026 }
7028 EgeAdjustmentAction* eact = 0;
7030 // Spacing spinbox
7031 eact = create_adjustment_action( "ConnectorSpacingAction",
7032 _("Connector Spacing"), _("Spacing:"),
7033 _("The amount of space left around objects by auto-routing connectors"),
7034 "/tools/connector/spacing", defaultConnSpacing,
7035 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
7036 0, 100, 1.0, 10.0,
7037 0, 0, 0,
7038 connector_spacing_changed, 1, 0 );
7039 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7041 // Graph (connector network) layout
7042 {
7043 InkAction* inky = ink_action_new( "ConnectorGraphAction",
7044 _("Graph"),
7045 _("Nicely arrange selected connector network"),
7046 "graph_layout",
7047 secondarySize );
7048 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
7049 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7050 }
7052 // Default connector length spinbox
7053 eact = create_adjustment_action( "ConnectorLengthAction",
7054 _("Connector Length"), _("Length:"),
7055 _("Ideal length for connectors when layout is applied"),
7056 "/tools/connector/length", 100,
7057 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
7058 10, 1000, 10.0, 100.0,
7059 0, 0, 0,
7060 connector_length_changed, 1, 0 );
7061 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7064 // Directed edges toggle button
7065 {
7066 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
7067 _("Downwards"),
7068 _("Make connectors with end-markers (arrows) point downwards"),
7069 "directed_graph",
7070 Inkscape::ICON_SIZE_DECORATION );
7071 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7073 bool tbuttonstate = prefs->getBool("/tools/connector/directedlayout");
7074 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7076 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
7077 }
7079 // Avoid overlaps toggle button
7080 {
7081 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
7082 _("Remove overlaps"),
7083 _("Do not allow overlapping shapes"),
7084 "remove_overlaps",
7085 Inkscape::ICON_SIZE_DECORATION );
7086 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7088 bool tbuttonstate = prefs->getBool("/tools/connector/avoidoverlaplayout");
7089 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), (tbuttonstate ? TRUE : FALSE ));
7091 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
7092 }
7094 // Code to watch for changes to the connector-spacing attribute in
7095 // the XML.
7096 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7097 g_assert(repr != NULL);
7099 purge_repr_listener( holder, holder );
7101 if (repr) {
7102 g_object_set_data( holder, "repr", repr );
7103 Inkscape::GC::anchor(repr);
7104 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
7105 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
7106 }
7107 } // end of sp_connector_toolbox_prep()
7110 //#########################
7111 //## Paintbucket ##
7112 //#########################
7114 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
7115 {
7116 gint channels = ege_select_one_action_get_active( act );
7117 flood_channels_set_channels( channels );
7118 }
7120 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
7121 {
7122 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7123 prefs->setInt("/tools/paintbucket/threshold", (gint)adj->value);
7124 }
7126 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
7127 {
7128 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7129 prefs->setBool("/tools/paintbucket/autogap", ege_select_one_action_get_active( act ));
7130 }
7132 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
7133 {
7134 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
7135 SPUnit const *unit = tracker->getActiveUnit();
7136 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7138 prefs->setDouble("/tools/paintbucket/offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
7139 prefs->setString("/tools/paintbucket/offsetunits", sp_unit_get_abbreviation(unit));
7140 }
7142 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
7143 {
7144 // FIXME: make defaults settable via Inkscape Options
7145 struct KeyValue {
7146 char const *key;
7147 double value;
7148 } const key_values[] = {
7149 {"threshold", 15},
7150 {"offset", 0.0}
7151 };
7153 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
7154 KeyValue const &kv = key_values[i];
7155 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
7156 if ( adj ) {
7157 gtk_adjustment_set_value(adj, kv.value);
7158 }
7159 }
7161 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
7162 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
7163 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
7164 ege_select_one_action_set_active( autogap_action, 0 );
7165 }
7167 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
7168 {
7169 EgeAdjustmentAction* eact = 0;
7170 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7172 {
7173 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7175 GList* items = 0;
7176 gint count = 0;
7177 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
7178 {
7179 GtkTreeIter iter;
7180 gtk_list_store_append( model, &iter );
7181 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7182 count++;
7183 }
7184 g_list_free( items );
7185 items = 0;
7186 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
7187 g_object_set( act1, "short_label", _("Fill by:"), NULL );
7188 ege_select_one_action_set_appearance( act1, "compact" );
7189 ege_select_one_action_set_active( act1, prefs->getInt("/tools/paintbucket/channels", 0) );
7190 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
7191 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
7192 g_object_set_data( holder, "channels_action", act1 );
7193 }
7195 // Spacing spinbox
7196 {
7197 eact = create_adjustment_action(
7198 "ThresholdAction",
7199 _("Fill Threshold"), _("Threshold:"),
7200 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
7201 "/tools/paintbucket/threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
7202 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
7203 0, 0, 0,
7204 paintbucket_threshold_changed, 1, 0 );
7206 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
7207 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7208 }
7210 // Create the units menu.
7211 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
7212 Glib::ustring stored_unit = prefs->getString("/tools/paintbucket/offsetunits");
7213 if (!stored_unit.empty())
7214 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit.data()));
7215 g_object_set_data( holder, "tracker", tracker );
7216 {
7217 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
7218 gtk_action_group_add_action( mainActions, act );
7219 }
7221 // Offset spinbox
7222 {
7223 eact = create_adjustment_action(
7224 "OffsetAction",
7225 _("Grow/shrink by"), _("Grow/shrink by:"),
7226 _("The amount to grow (positive) or shrink (negative) the created fill path"),
7227 "/tools/paintbucket/offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
7228 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
7229 0, 0, 0,
7230 paintbucket_offset_changed, 1, 2);
7231 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
7233 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7234 }
7236 /* Auto Gap */
7237 {
7238 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7240 GList* items = 0;
7241 gint count = 0;
7242 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
7243 {
7244 GtkTreeIter iter;
7245 gtk_list_store_append( model, &iter );
7246 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7247 count++;
7248 }
7249 g_list_free( items );
7250 items = 0;
7251 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
7252 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
7253 ege_select_one_action_set_appearance( act2, "compact" );
7254 ege_select_one_action_set_active( act2, prefs->getBool("/tools/paintbucket/autogap") );
7255 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
7256 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
7257 g_object_set_data( holder, "autogap_action", act2 );
7258 }
7260 /* Reset */
7261 {
7262 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
7263 _("Defaults"),
7264 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
7265 GTK_STOCK_CLEAR );
7266 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
7267 gtk_action_group_add_action( mainActions, act );
7268 gtk_action_set_sensitive( act, TRUE );
7269 }
7271 }
7273 /*
7274 Local Variables:
7275 mode:c++
7276 c-file-style:"stroustrup"
7277 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
7278 indent-tabs-mode:nil
7279 fill-column:99
7280 End:
7281 */
7282 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :