1 /** @file
2 * @brief Controls bars for some of Inkscape's tools (for some tools,
3 * they are in their own files)
4 */
5 /* Authors:
6 * MenTaLguY <mental@rydia.net>
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 * bulia byak <buliabyak@users.sf.net>
9 * Frank Felfe <innerspace@iname.com>
10 * John Cliff <simarilius@yahoo.com>
11 * David Turner <novalis@gnu.org>
12 * Josh Andler <scislac@scislac.com>
13 * Jon A. Cruz <jon@joncruz.org>
14 * Maximilian Albert <maximilian.albert@gmail.com>
15 *
16 * Copyright (C) 2004 David Turner
17 * Copyright (C) 2003 MenTaLguY
18 * Copyright (C) 1999-2008 authors
19 * Copyright (C) 2001-2002 Ximian, Inc.
20 *
21 * Released under GNU GPL, read the file 'COPYING' for more information
22 */
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
28 #include <cstring>
29 #include <string>
31 #include <gtkmm.h>
32 #include <gtk/gtk.h>
33 #include <iostream>
34 #include <sstream>
35 #include <glibmm/i18n.h>
37 #include "../box3d-context.h"
38 #include "../box3d.h"
39 #include "../conn-avoid-ref.h"
40 #include "../connection-pool.h"
41 #include "../connector-context.h"
42 #include "../desktop.h"
43 #include "../desktop-handles.h"
44 #include "../desktop-style.h"
45 #include "../dialogs/dialog-events.h"
46 #include "../dialogs/text-edit.h"
47 #include "../document-private.h"
48 #include "../ege-adjustment-action.h"
49 #include "../ege-output-action.h"
50 #include "../ege-select-one-action.h"
51 #include "../flood-context.h"
52 #include "gradient-toolbar.h"
53 #include "../graphlayout/graphlayout.h"
54 #include "../helper/unit-menu.h"
55 #include "../helper/units.h"
56 #include "../helper/unit-tracker.h"
57 #include "icon.h"
58 #include "../ink-action.h"
59 #include "../inkscape.h"
60 #include "../interface.h"
61 #include "../libnrtype/font-instance.h"
62 #include "../libnrtype/font-lister.h"
63 #include "../live_effects/effect.h"
64 #include "../live_effects/lpe-angle_bisector.h"
65 #include "../live_effects/lpe-line_segment.h"
66 #include "../lpe-tool-context.h"
67 #include "../mod360.h"
68 #include "../pen-context.h"
69 #include "../preferences.h"
70 #include "../selection-chemistry.h"
71 #include "../selection.h"
72 #include "select-toolbar.h"
73 #include "../shape-editor.h"
74 #include "../shortcuts.h"
75 #include "../sp-clippath.h"
76 #include "../sp-ellipse.h"
77 #include "../sp-flowtext.h"
78 #include "../sp-mask.h"
79 #include "../sp-namedview.h"
80 #include "../sp-rect.h"
81 #include "../sp-spiral.h"
82 #include "../sp-star.h"
83 #include "../sp-text.h"
84 #include "../style.h"
85 #include "../svg/css-ostringstream.h"
86 #include "../tools-switch.h"
87 #include "../tweak-context.h"
88 #include "../ui/dialog/calligraphic-profile-rename.h"
89 #include "../ui/icon-names.h"
90 #include "../ui/tool/control-point-selection.h"
91 #include "../ui/tool/node-tool.h"
92 #include "../ui/tool/multi-path-manipulator.h"
93 #include "../ui/widget/style-swatch.h"
94 #include "../verbs.h"
95 #include "../widgets/button.h"
96 #include "../widgets/spinbutton-events.h"
97 #include "../widgets/spw-utilities.h"
98 #include "../widgets/widget-sizes.h"
99 #include "../xml/attribute-record.h"
100 #include "../xml/node-event-vector.h"
101 #include "../xml/repr.h"
103 #include "toolbox.h"
105 using Inkscape::UnitTracker;
107 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
108 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
110 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
111 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
112 static void sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
113 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
114 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
115 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
116 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
117 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
118 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
119 static void sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
120 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
121 static void sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
122 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
123 static void sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
126 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
128 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
131 Inkscape::IconSize prefToSize( Glib::ustring const &path, int base ) {
132 static Inkscape::IconSize sizeChoices[] = {
133 Inkscape::ICON_SIZE_LARGE_TOOLBAR,
134 Inkscape::ICON_SIZE_SMALL_TOOLBAR,
135 Inkscape::ICON_SIZE_MENU
136 };
137 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
138 int index = prefs->getIntLimited( path, base, 0, G_N_ELEMENTS(sizeChoices) );
139 return sizeChoices[index];
140 }
142 static struct {
143 gchar const *type_name;
144 gchar const *data_name;
145 sp_verb_t verb;
146 sp_verb_t doubleclick_verb;
147 } const tools[] = {
148 { "SPSelectContext", "select_tool", SP_VERB_CONTEXT_SELECT, SP_VERB_CONTEXT_SELECT_PREFS},
149 { "InkNodeTool", "node_tool", SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
150 { "SPTweakContext", "tweak_tool", SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
151 { "SPZoomContext", "zoom_tool", SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
152 { "SPRectContext", "rect_tool", SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
153 { "Box3DContext", "3dbox_tool", SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
154 { "SPArcContext", "arc_tool", SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
155 { "SPStarContext", "star_tool", SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
156 { "SPSpiralContext", "spiral_tool", SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
157 { "SPPencilContext", "pencil_tool", SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
158 { "SPPenContext", "pen_tool", SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
159 { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
160 { "SPLPEToolContext", "lpetool_tool", SP_VERB_CONTEXT_LPETOOL, SP_VERB_CONTEXT_LPETOOL_PREFS },
161 { "SPEraserContext", "eraser_tool", SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
162 { "SPFloodContext", "paintbucket_tool", SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
163 { "SPTextContext", "text_tool", SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
164 { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
165 { "SPGradientContext", "gradient_tool", SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
166 { "SPDropperContext", "dropper_tool", SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
167 { NULL, NULL, 0, 0 }
168 };
170 static struct {
171 gchar const *type_name;
172 gchar const *data_name;
173 GtkWidget *(*create_func)(SPDesktop *desktop);
174 void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
175 gchar const *ui_name;
176 gint swatch_verb_id;
177 gchar const *swatch_tool;
178 gchar const *swatch_tip;
179 } const aux_toolboxes[] = {
180 { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep, "SelectToolbar",
181 SP_VERB_INVALID, 0, 0},
182 { "InkNodeTool", "node_toolbox", 0, sp_node_toolbox_prep, "NodeToolbar",
183 SP_VERB_INVALID, 0, 0},
184 { "SPTweakContext", "tweak_toolbox", 0, sp_tweak_toolbox_prep, "TweakToolbar",
185 SP_VERB_CONTEXT_TWEAK_PREFS, "/tools/tweak", N_("Color/opacity used for color tweaking")},
186 { "SPZoomContext", "zoom_toolbox", 0, sp_zoom_toolbox_prep, "ZoomToolbar",
187 SP_VERB_INVALID, 0, 0},
188 { "SPStarContext", "star_toolbox", 0, sp_star_toolbox_prep, "StarToolbar",
189 SP_VERB_CONTEXT_STAR_PREFS, "/tools/shapes/star", N_("Style of new stars")},
190 { "SPRectContext", "rect_toolbox", 0, sp_rect_toolbox_prep, "RectToolbar",
191 SP_VERB_CONTEXT_RECT_PREFS, "/tools/shapes/rect", N_("Style of new rectangles")},
192 { "Box3DContext", "3dbox_toolbox", 0, box3d_toolbox_prep, "3DBoxToolbar",
193 SP_VERB_CONTEXT_3DBOX_PREFS, "/tools/shapes/3dbox", N_("Style of new 3D boxes")},
194 { "SPArcContext", "arc_toolbox", 0, sp_arc_toolbox_prep, "ArcToolbar",
195 SP_VERB_CONTEXT_ARC_PREFS, "/tools/shapes/arc", N_("Style of new ellipses")},
196 { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep, "SpiralToolbar",
197 SP_VERB_CONTEXT_SPIRAL_PREFS, "/tools/shapes/spiral", N_("Style of new spirals")},
198 { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep, "PencilToolbar",
199 SP_VERB_CONTEXT_PENCIL_PREFS, "/tools/freehand/pencil", N_("Style of new paths created by Pencil")},
200 { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep, "PenToolbar",
201 SP_VERB_CONTEXT_PEN_PREFS, "/tools/freehand/pen", N_("Style of new paths created by Pen")},
202 { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
203 SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "/tools/calligraphic", N_("Style of new calligraphic strokes")},
204 { "SPEraserContext", "eraser_toolbox", 0, sp_eraser_toolbox_prep,"EraserToolbar",
205 SP_VERB_CONTEXT_ERASER_PREFS, "/tools/eraser", _("TBD")},
206 { "SPLPEToolContext", "lpetool_toolbox", 0, sp_lpetool_toolbox_prep, "LPEToolToolbar",
207 SP_VERB_CONTEXT_LPETOOL_PREFS, "/tools/lpetool", _("TBD")},
208 { "SPTextContext", "text_toolbox", sp_text_toolbox_new, 0, 0,
209 SP_VERB_INVALID, 0, 0},
210 { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep, "DropperToolbar",
211 SP_VERB_INVALID, 0, 0},
212 { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0, 0,
213 SP_VERB_INVALID, 0, 0},
214 { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep, "ConnectorToolbar",
215 SP_VERB_INVALID, 0, 0},
216 { "SPFloodContext", "paintbucket_toolbox", 0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
217 SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "/tools/paintbucket", N_("Style of Paint Bucket fill objects")},
218 { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
219 };
221 #define TOOLBAR_SLIDER_HINT "full"
223 static gchar const * ui_descr =
224 "<ui>"
225 " <toolbar name='SelectToolbar'>"
226 " <toolitem action='EditSelectAll' />"
227 " <toolitem action='EditSelectAllInAllLayers' />"
228 " <toolitem action='EditDeselect' />"
229 " <separator />"
230 " <toolitem action='ObjectRotate90CCW' />"
231 " <toolitem action='ObjectRotate90' />"
232 " <toolitem action='ObjectFlipHorizontally' />"
233 " <toolitem action='ObjectFlipVertically' />"
234 " <separator />"
235 " <toolitem action='SelectionToBack' />"
236 " <toolitem action='SelectionLower' />"
237 " <toolitem action='SelectionRaise' />"
238 " <toolitem action='SelectionToFront' />"
239 " <separator />"
240 " <toolitem action='XAction' />"
241 " <toolitem action='YAction' />"
242 " <toolitem action='WidthAction' />"
243 " <toolitem action='LockAction' />"
244 " <toolitem action='HeightAction' />"
245 " <toolitem action='UnitsAction' />"
246 " <separator />"
247 " <toolitem action='transform_affect_label' />"
248 " <toolitem action='transform_stroke' />"
249 " <toolitem action='transform_corners' />"
250 " <toolitem action='transform_gradient' />"
251 " <toolitem action='transform_pattern' />"
252 " </toolbar>"
254 " <toolbar name='NodeToolbar'>"
255 " <toolitem action='NodeInsertAction' />"
256 " <toolitem action='NodeDeleteAction' />"
257 " <separator />"
258 " <toolitem action='NodeJoinAction' />"
259 " <toolitem action='NodeBreakAction' />"
260 " <separator />"
261 " <toolitem action='NodeJoinSegmentAction' />"
262 " <toolitem action='NodeDeleteSegmentAction' />"
263 " <separator />"
264 " <toolitem action='NodeCuspAction' />"
265 " <toolitem action='NodeSmoothAction' />"
266 " <toolitem action='NodeSymmetricAction' />"
267 " <toolitem action='NodeAutoAction' />"
268 " <separator />"
269 " <toolitem action='NodeLineAction' />"
270 " <toolitem action='NodeCurveAction' />"
271 " <separator />"
272 " <toolitem action='ObjectToPath' />"
273 " <toolitem action='StrokeToPath' />"
274 " <separator />"
275 " <toolitem action='NodeXAction' />"
276 " <toolitem action='NodeYAction' />"
277 " <toolitem action='NodeUnitsAction' />"
278 " <separator />"
279 " <toolitem action='ObjectEditClipPathAction' />"
280 " <toolitem action='ObjectEditMaskPathAction' />"
281 " <toolitem action='EditNextLPEParameterAction' />"
282 " <separator />"
283 " <toolitem action='NodesShowHandlesAction' />"
284 " <toolitem action='NodesShowHelperpath' />"
285 " </toolbar>"
287 " <toolbar name='TweakToolbar'>"
288 " <toolitem action='TweakWidthAction' />"
289 " <separator />"
290 " <toolitem action='TweakForceAction' />"
291 " <toolitem action='TweakPressureAction' />"
292 " <separator />"
293 " <toolitem action='TweakModeAction' />"
294 " <separator />"
295 " <toolitem action='TweakFidelityAction' />"
296 " <separator />"
297 " <toolitem action='TweakChannelsLabel' />"
298 " <toolitem action='TweakDoH' />"
299 " <toolitem action='TweakDoS' />"
300 " <toolitem action='TweakDoL' />"
301 " <toolitem action='TweakDoO' />"
302 " </toolbar>"
304 " <toolbar name='ZoomToolbar'>"
305 " <toolitem action='ZoomIn' />"
306 " <toolitem action='ZoomOut' />"
307 " <separator />"
308 " <toolitem action='Zoom1:0' />"
309 " <toolitem action='Zoom1:2' />"
310 " <toolitem action='Zoom2:1' />"
311 " <separator />"
312 " <toolitem action='ZoomSelection' />"
313 " <toolitem action='ZoomDrawing' />"
314 " <toolitem action='ZoomPage' />"
315 " <toolitem action='ZoomPageWidth' />"
316 " <separator />"
317 " <toolitem action='ZoomPrev' />"
318 " <toolitem action='ZoomNext' />"
319 " </toolbar>"
321 " <toolbar name='StarToolbar'>"
322 " <separator />"
323 " <toolitem action='StarStateAction' />"
324 " <separator />"
325 " <toolitem action='FlatAction' />"
326 " <separator />"
327 " <toolitem action='MagnitudeAction' />"
328 " <toolitem action='SpokeAction' />"
329 " <toolitem action='RoundednessAction' />"
330 " <toolitem action='RandomizationAction' />"
331 " <separator />"
332 " <toolitem action='StarResetAction' />"
333 " </toolbar>"
335 " <toolbar name='RectToolbar'>"
336 " <toolitem action='RectStateAction' />"
337 " <toolitem action='RectWidthAction' />"
338 " <toolitem action='RectHeightAction' />"
339 " <toolitem action='RadiusXAction' />"
340 " <toolitem action='RadiusYAction' />"
341 " <toolitem action='RectUnitsAction' />"
342 " <separator />"
343 " <toolitem action='RectResetAction' />"
344 " </toolbar>"
346 " <toolbar name='3DBoxToolbar'>"
347 " <toolitem action='3DBoxAngleXAction' />"
348 " <toolitem action='3DBoxVPXStateAction' />"
349 " <separator />"
350 " <toolitem action='3DBoxAngleYAction' />"
351 " <toolitem action='3DBoxVPYStateAction' />"
352 " <separator />"
353 " <toolitem action='3DBoxAngleZAction' />"
354 " <toolitem action='3DBoxVPZStateAction' />"
355 " </toolbar>"
357 " <toolbar name='SpiralToolbar'>"
358 " <toolitem action='SpiralStateAction' />"
359 " <toolitem action='SpiralRevolutionAction' />"
360 " <toolitem action='SpiralExpansionAction' />"
361 " <toolitem action='SpiralT0Action' />"
362 " <separator />"
363 " <toolitem action='SpiralResetAction' />"
364 " </toolbar>"
366 " <toolbar name='PenToolbar'>"
367 " <toolitem action='FreehandModeActionPen' />"
368 " <separator />"
369 " <toolitem action='SetPenShapeAction'/>"
370 " </toolbar>"
372 " <toolbar name='PencilToolbar'>"
373 " <toolitem action='FreehandModeActionPencil' />"
374 " <separator />"
375 " <toolitem action='PencilToleranceAction' />"
376 " <separator />"
377 " <toolitem action='PencilResetAction' />"
378 " <separator />"
379 " <toolitem action='SetPencilShapeAction'/>"
380 " </toolbar>"
382 " <toolbar name='CalligraphyToolbar'>"
383 " <separator />"
384 " <toolitem action='SetProfileAction'/>"
385 " <separator />"
386 " <toolitem action='CalligraphyWidthAction' />"
387 " <toolitem action='PressureAction' />"
388 " <toolitem action='TraceAction' />"
389 " <toolitem action='ThinningAction' />"
390 " <separator />"
391 " <toolitem action='AngleAction' />"
392 " <toolitem action='TiltAction' />"
393 " <toolitem action='FixationAction' />"
394 " <separator />"
395 " <toolitem action='CapRoundingAction' />"
396 " <separator />"
397 " <toolitem action='TremorAction' />"
398 " <toolitem action='WiggleAction' />"
399 " <toolitem action='MassAction' />"
400 " <separator />"
401 " </toolbar>"
403 " <toolbar name='ArcToolbar'>"
404 " <toolitem action='ArcStateAction' />"
405 " <separator />"
406 " <toolitem action='ArcStartAction' />"
407 " <toolitem action='ArcEndAction' />"
408 " <separator />"
409 " <toolitem action='ArcOpenAction' />"
410 " <separator />"
411 " <toolitem action='ArcResetAction' />"
412 " <separator />"
413 " </toolbar>"
415 " <toolbar name='PaintbucketToolbar'>"
416 " <toolitem action='ChannelsAction' />"
417 " <separator />"
418 " <toolitem action='ThresholdAction' />"
419 " <separator />"
420 " <toolitem action='OffsetAction' />"
421 " <toolitem action='PaintbucketUnitsAction' />"
422 " <separator />"
423 " <toolitem action='AutoGapAction' />"
424 " <separator />"
425 " <toolitem action='PaintbucketResetAction' />"
426 " </toolbar>"
428 " <toolbar name='EraserToolbar'>"
429 " <toolitem action='EraserWidthAction' />"
430 " <separator />"
431 " <toolitem action='EraserModeAction' />"
432 " </toolbar>"
434 " <toolbar name='LPEToolToolbar'>"
435 " <toolitem action='LPEToolModeAction' />"
436 " <separator />"
437 " <toolitem action='LPEShowBBoxAction' />"
438 " <toolitem action='LPEBBoxFromSelectionAction' />"
439 " <separator />"
440 " <toolitem action='LPELineSegmentAction' />"
441 " <separator />"
442 " <toolitem action='LPEMeasuringAction' />"
443 " <toolitem action='LPEToolUnitsAction' />"
444 " <separator />"
445 " <toolitem action='LPEOpenLPEDialogAction' />"
446 " </toolbar>"
448 " <toolbar name='DropperToolbar'>"
449 " <toolitem action='DropperOpacityAction' />"
450 " <toolitem action='DropperPickAlphaAction' />"
451 " <toolitem action='DropperSetAlphaAction' />"
452 " </toolbar>"
454 " <toolbar name='ConnectorToolbar'>"
455 " <toolitem action='ConnectorAvoidAction' />"
456 " <toolitem action='ConnectorIgnoreAction' />"
457 " <toolitem action='ConnectorSpacingAction' />"
458 " <toolitem action='ConnectorGraphAction' />"
459 " <toolitem action='ConnectorLengthAction' />"
460 " <toolitem action='ConnectorDirectedAction' />"
461 " <toolitem action='ConnectorOverlapAction' />"
462 " </toolbar>"
464 "</ui>"
465 ;
467 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
469 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
471 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
472 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
474 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
475 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
477 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
478 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
480 GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick( GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
481 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
482 Inkscape::UI::View::View *view, GtkTooltips *tt);
484 class VerbAction : public Gtk::Action {
485 public:
486 static Glib::RefPtr<VerbAction> create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
488 virtual ~VerbAction();
489 virtual void set_active(bool active = true);
491 protected:
492 virtual Gtk::Widget* create_menu_item_vfunc();
493 virtual Gtk::Widget* create_tool_item_vfunc();
495 virtual void connect_proxy_vfunc(Gtk::Widget* proxy);
496 virtual void disconnect_proxy_vfunc(Gtk::Widget* proxy);
498 virtual void on_activate();
500 private:
501 Inkscape::Verb* verb;
502 Inkscape::Verb* verb2;
503 Inkscape::UI::View::View *view;
504 GtkTooltips *tooltips;
505 bool active;
507 VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
508 };
511 Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips)
512 {
513 Glib::RefPtr<VerbAction> result;
514 SPAction *action = verb->get_action(view);
515 if ( action ) {
516 //SPAction* action2 = verb2 ? verb2->get_action(view) : 0;
517 result = Glib::RefPtr<VerbAction>(new VerbAction(verb, verb2, view, tooltips));
518 }
520 return result;
521 }
523 VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips) :
524 Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(verb->get_image()), Glib::ustring(_(verb->get_name())), Glib::ustring(_(verb->get_tip()))),
525 verb(verb),
526 verb2(verb2),
527 view(view),
528 tooltips(tooltips),
529 active(false)
530 {
531 }
533 VerbAction::~VerbAction()
534 {
535 }
537 Gtk::Widget* VerbAction::create_menu_item_vfunc()
538 {
539 // First call in to get the icon rendered if present in SVG
540 Gtk::Widget *widget = sp_icon_get_icon( property_stock_id().get_value().get_string(), Inkscape::ICON_SIZE_MENU );
541 delete widget;
542 widget = 0;
544 Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
545 // g_message("create_menu_item_vfunc() = %p for '%s'", widg, verb->get_id());
546 return widg;
547 }
549 Gtk::Widget* VerbAction::create_tool_item_vfunc()
550 {
551 // Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
552 Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
553 GtkWidget* toolbox = 0;
554 GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
555 SP_BUTTON_TYPE_TOGGLE,
556 verb,
557 verb2,
558 view,
559 tooltips );
560 if ( active ) {
561 sp_button_toggle_set_down( SP_BUTTON(button), active);
562 }
563 gtk_widget_show_all( button );
564 Gtk::Widget* wrapped = Glib::wrap(button);
565 Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
566 holder->add(*wrapped);
568 // g_message("create_tool_item_vfunc() = %p for '%s'", holder, verb->get_id());
569 return holder;
570 }
572 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
573 {
574 // g_message("connect_proxy_vfunc(%p) for '%s'", proxy, verb->get_id());
575 Gtk::Action::connect_proxy_vfunc(proxy);
576 }
578 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
579 {
580 // g_message("disconnect_proxy_vfunc(%p) for '%s'", proxy, verb->get_id());
581 Gtk::Action::disconnect_proxy_vfunc(proxy);
582 }
584 void VerbAction::set_active(bool active)
585 {
586 this->active = active;
587 Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
588 for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
589 Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
590 if (ti) {
591 // *should* have one child that is the SPButton
592 Gtk::Widget* child = ti->get_child();
593 if ( child && SP_IS_BUTTON(child->gobj()) ) {
594 SPButton* button = SP_BUTTON(child->gobj());
595 sp_button_toggle_set_down( button, active );
596 }
597 }
598 }
599 }
601 void VerbAction::on_activate()
602 {
603 if ( verb ) {
604 SPAction *action = verb->get_action(view);
605 if ( action ) {
606 sp_action_perform(action, 0);
607 }
608 }
609 }
611 /* Global text entry widgets necessary for update */
612 /* GtkWidget *dropper_rgb_entry,
613 *dropper_opacity_entry ; */
614 // should be made a private member once this is converted to class
616 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
617 connection->disconnect();
618 delete connection;
619 }
621 static void purge_repr_listener( GObject* obj, GObject* tbl )
622 {
623 (void)obj;
624 Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
625 if (oldrepr) { // remove old listener
626 sp_repr_remove_listener_by_data(oldrepr, tbl);
627 Inkscape::GC::release(oldrepr);
628 oldrepr = 0;
629 g_object_set_data( tbl, "repr", NULL );
630 }
631 }
633 GtkWidget *
634 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
635 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
636 Inkscape::UI::View::View *view, GtkTooltips *tt)
637 {
638 SPAction *action = verb->get_action(view);
639 if (!action) return NULL;
641 SPAction *doubleclick_action;
642 if (doubleclick_verb)
643 doubleclick_action = doubleclick_verb->get_action(view);
644 else
645 doubleclick_action = NULL;
647 /* fixme: Handle sensitive/unsensitive */
648 /* fixme: Implement sp_button_new_from_action */
649 GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
650 gtk_widget_show(b);
653 unsigned int shortcut = sp_shortcut_get_primary(verb);
654 if (shortcut) {
655 gchar key[256];
656 sp_ui_shortcut_string(shortcut, key);
657 gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
658 if ( t ) {
659 gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
660 }
661 g_free(tip);
662 } else {
663 if ( t ) {
664 gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
665 }
666 }
668 return b;
669 }
672 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
673 {
674 SPAction* targetAction = SP_ACTION(user_data);
675 if ( targetAction ) {
676 sp_action_perform( targetAction, NULL );
677 }
678 }
680 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
681 {
682 if ( data ) {
683 GtkAction* act = GTK_ACTION(data);
684 gtk_action_set_sensitive( act, sensitive );
685 }
686 }
688 static SPActionEventVector action_event_vector = {
689 {NULL},
690 NULL,
691 NULL,
692 sp_action_action_set_sensitive,
693 NULL,
694 NULL
695 };
697 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
698 {
699 GtkAction* act = 0;
701 SPAction* targetAction = verb->get_action(view);
702 InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size );
703 act = GTK_ACTION(inky);
704 gtk_action_set_sensitive( act, targetAction->sensitive );
706 g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
708 SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
709 nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
711 return act;
712 }
714 Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
715 {
716 Inkscape::UI::View::View *view = desktop;
717 gint verbsToUse[] = {
718 // disabled until we have icons for them:
719 //find
720 //SP_VERB_EDIT_TILE,
721 //SP_VERB_EDIT_UNTILE,
722 SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
723 SP_VERB_DIALOG_DISPLAY,
724 SP_VERB_DIALOG_FILL_STROKE,
725 SP_VERB_DIALOG_NAMEDVIEW,
726 SP_VERB_DIALOG_TEXT,
727 SP_VERB_DIALOG_XML_EDITOR,
728 SP_VERB_DIALOG_LAYERS,
729 SP_VERB_EDIT_CLONE,
730 SP_VERB_EDIT_COPY,
731 SP_VERB_EDIT_CUT,
732 SP_VERB_EDIT_DUPLICATE,
733 SP_VERB_EDIT_PASTE,
734 SP_VERB_EDIT_REDO,
735 SP_VERB_EDIT_UNDO,
736 SP_VERB_EDIT_UNLINK_CLONE,
737 SP_VERB_FILE_EXPORT,
738 SP_VERB_FILE_IMPORT,
739 SP_VERB_FILE_NEW,
740 SP_VERB_FILE_OPEN,
741 SP_VERB_FILE_PRINT,
742 SP_VERB_FILE_SAVE,
743 SP_VERB_OBJECT_TO_CURVE,
744 SP_VERB_SELECTION_GROUP,
745 SP_VERB_SELECTION_OUTLINE,
746 SP_VERB_SELECTION_UNGROUP,
747 SP_VERB_ZOOM_1_1,
748 SP_VERB_ZOOM_1_2,
749 SP_VERB_ZOOM_2_1,
750 SP_VERB_ZOOM_DRAWING,
751 SP_VERB_ZOOM_IN,
752 SP_VERB_ZOOM_NEXT,
753 SP_VERB_ZOOM_OUT,
754 SP_VERB_ZOOM_PAGE,
755 SP_VERB_ZOOM_PAGE_WIDTH,
756 SP_VERB_ZOOM_PREV,
757 SP_VERB_ZOOM_SELECTION,
758 };
760 Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
762 static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
763 Glib::RefPtr<Gtk::ActionGroup> mainActions;
764 if ( groups.find(desktop) != groups.end() ) {
765 mainActions = groups[desktop];
766 }
768 if ( !mainActions ) {
769 mainActions = Gtk::ActionGroup::create("main");
770 groups[desktop] = mainActions;
771 }
773 for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
774 Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
775 if ( verb ) {
776 if (!mainActions->get_action(verb->get_id())) {
777 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
778 mainActions->add(Glib::wrap(act));
779 }
780 }
781 }
783 if ( !mainActions->get_action("ToolZoom") ) {
784 GtkTooltips *tt = gtk_tooltips_new();
785 for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
786 Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
787 if ( va ) {
788 mainActions->add(va);
789 if ( i == 0 ) {
790 va->set_active(true);
791 }
792 }
793 }
794 }
797 return mainActions;
798 }
801 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
802 {
803 gtk_widget_set_size_request( widget,
804 widget->allocation.width,
805 widget->allocation.height );
806 }
808 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
809 {
810 gtk_widget_set_size_request( widget, -1, -1 );
811 }
815 GtkWidget *
816 sp_tool_toolbox_new()
817 {
818 GtkTooltips *tt = gtk_tooltips_new();
819 GtkWidget* tb = gtk_toolbar_new();
820 gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
821 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
823 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
824 g_object_set_data(G_OBJECT(tb), "tooltips", tt);
826 gtk_widget_set_sensitive(tb, FALSE);
828 GtkWidget *hb = gtk_handle_box_new();
829 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
830 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
831 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
833 gtk_container_add(GTK_CONTAINER(hb), tb);
834 gtk_widget_show(GTK_WIDGET(tb));
836 sigc::connection* conn = new sigc::connection;
837 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
839 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
840 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
842 return hb;
843 }
845 GtkWidget *
846 sp_aux_toolbox_new()
847 {
848 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
850 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
852 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
854 gtk_widget_set_sensitive(tb, FALSE);
856 GtkWidget *hb = gtk_handle_box_new();
857 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
858 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
859 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
861 gtk_container_add(GTK_CONTAINER(hb), tb);
862 gtk_widget_show(GTK_WIDGET(tb));
864 sigc::connection* conn = new sigc::connection;
865 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
867 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
868 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
870 return hb;
871 }
873 //####################################
874 //# Commands Bar
875 //####################################
877 GtkWidget *
878 sp_commands_toolbox_new()
879 {
880 GtkWidget *tb = gtk_toolbar_new();
882 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
883 gtk_widget_set_sensitive(tb, FALSE);
885 GtkWidget *hb = gtk_handle_box_new();
886 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
887 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
888 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
890 gtk_container_add(GTK_CONTAINER(hb), tb);
891 gtk_widget_show(GTK_WIDGET(tb));
893 sigc::connection* conn = new sigc::connection;
894 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
896 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
897 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
899 return hb;
900 }
902 GtkWidget *
903 sp_snap_toolbox_new()
904 {
905 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
906 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
907 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
909 //GtkWidget *tb = gtk_toolbar_new();
910 //g_object_set_data(G_OBJECT(tb), "desktop", NULL);
912 gtk_widget_set_sensitive(tb, FALSE);
914 GtkWidget *hb = gtk_handle_box_new();
915 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
916 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
917 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
919 gtk_container_add(GTK_CONTAINER(hb), tb);
920 gtk_widget_show(GTK_WIDGET(tb));
922 sigc::connection* conn = new sigc::connection;
923 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
925 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
926 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
928 return hb;
929 }
931 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
932 gchar const *label, gchar const *shortLabel, gchar const *tooltip,
933 Glib::ustring const &path, gdouble def,
934 GtkWidget *focusTarget,
935 GtkWidget *us,
936 GObject *dataKludge,
937 gboolean altx, gchar const *altx_mark,
938 gdouble lower, gdouble upper, gdouble step, gdouble page,
939 gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
940 void (*callback)(GtkAdjustment *, GObject *),
941 gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
942 {
943 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
944 GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs->getDouble(path, def) * factor,
945 lower, upper, step, page, 0 ) );
946 if (us) {
947 sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
948 }
950 gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
952 EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
953 if ( shortLabel ) {
954 g_object_set( act, "short_label", shortLabel, NULL );
955 }
957 if ( (descrCount > 0) && descrLabels && descrValues ) {
958 ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
959 }
961 if ( focusTarget ) {
962 ege_adjustment_action_set_focuswidget( act, focusTarget );
963 }
965 if ( altx && altx_mark ) {
966 g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
967 }
969 if ( dataKludge ) {
970 // Rather lame, but it's the only place where we need to get the entry name
971 // but we don't have an Entry
972 g_object_set_data( dataKludge, prefs->getEntry(path).getEntryName().data(), adj );
973 }
975 // Using a cast just to make sure we pass in the right kind of function pointer
976 g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
978 return act;
979 }
982 //####################################
983 //# node editing callbacks
984 //####################################
986 /** Temporary hack: Returns the node tool in the active desktop.
987 * Will go away during tool refactoring. */
988 static InkNodeTool *get_node_tool()
989 {
990 if (!SP_ACTIVE_DESKTOP) return NULL;
991 SPEventContext *ec = SP_ACTIVE_DESKTOP->event_context;
992 if (!INK_IS_NODE_TOOL(ec)) return NULL;
993 return static_cast<InkNodeTool*>(ec);
994 }
997 void
998 sp_node_path_edit_add(void)
999 {
1000 InkNodeTool *nt = get_node_tool();
1001 if (nt) nt->_multipath->insertNodes();
1002 }
1004 void
1005 sp_node_path_edit_delete(void)
1006 {
1007 InkNodeTool *nt = get_node_tool();
1008 if (nt) nt->_multipath->deleteNodes();
1009 }
1011 void
1012 sp_node_path_edit_delete_segment(void)
1013 {
1014 InkNodeTool *nt = get_node_tool();
1015 if (nt) nt->_multipath->deleteSegments();
1016 }
1018 void
1019 sp_node_path_edit_break(void)
1020 {
1021 InkNodeTool *nt = get_node_tool();
1022 if (nt) nt->_multipath->breakNodes();
1023 }
1025 void
1026 sp_node_path_edit_join(void)
1027 {
1028 InkNodeTool *nt = get_node_tool();
1029 if (nt) nt->_multipath->joinNodes();
1030 }
1032 void
1033 sp_node_path_edit_join_segment(void)
1034 {
1035 InkNodeTool *nt = get_node_tool();
1036 if (nt) nt->_multipath->joinSegment();
1037 }
1039 void
1040 sp_node_path_edit_toline(void)
1041 {
1042 InkNodeTool *nt = get_node_tool();
1043 if (nt) nt->_multipath->setSegmentType(Inkscape::UI::SEGMENT_STRAIGHT);
1044 }
1046 void
1047 sp_node_path_edit_tocurve(void)
1048 {
1049 InkNodeTool *nt = get_node_tool();
1050 if (nt) nt->_multipath->setSegmentType(Inkscape::UI::SEGMENT_CUBIC_BEZIER);
1051 }
1053 void
1054 sp_node_path_edit_cusp(void)
1055 {
1056 InkNodeTool *nt = get_node_tool();
1057 if (nt) nt->_multipath->setNodeType(Inkscape::UI::NODE_CUSP);
1058 }
1060 void
1061 sp_node_path_edit_smooth(void)
1062 {
1063 InkNodeTool *nt = get_node_tool();
1064 if (nt) nt->_multipath->setNodeType(Inkscape::UI::NODE_SMOOTH);
1065 }
1067 void
1068 sp_node_path_edit_symmetrical(void)
1069 {
1070 InkNodeTool *nt = get_node_tool();
1071 if (nt) nt->_multipath->setNodeType(Inkscape::UI::NODE_SYMMETRIC);
1072 }
1074 void
1075 sp_node_path_edit_auto(void)
1076 {
1077 InkNodeTool *nt = get_node_tool();
1078 if (nt) nt->_multipath->setNodeType(Inkscape::UI::NODE_AUTO);
1079 }
1081 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1082 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1083 bool show = gtk_toggle_action_get_active( act );
1084 prefs->setBool("/tools/nodes/show_handles", show);
1085 }
1087 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1088 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1089 bool show = gtk_toggle_action_get_active( act );
1090 prefs->setBool("/tools/nodes/show_outline", show);
1091 }
1093 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1094 sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1095 }
1097 void toggle_edit_clip (GtkToggleAction *act, gpointer data) {
1098 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1099 bool edit = gtk_toggle_action_get_active( act );
1100 prefs->setBool("/tools/nodes/edit_clipping_paths", edit);
1101 }
1103 void toggle_edit_mask (GtkToggleAction *act, gpointer data) {
1104 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1105 bool edit = gtk_toggle_action_get_active( act );
1106 prefs->setBool("/tools/nodes/edit_masks", edit);
1107 }
1109 /* is called when the node selection is modified */
1110 static void
1111 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1112 {
1113 GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1114 GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1115 GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1116 GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1118 // quit if run by the attr_changed listener
1119 if (g_object_get_data( tbl, "freeze" )) {
1120 return;
1121 }
1123 // in turn, prevent listener from responding
1124 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1126 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1127 SPUnit const *unit = tracker->getActiveUnit();
1129 InkNodeTool *nt = get_node_tool();
1130 if (!nt || nt->_selected_nodes->empty()) {
1131 // no path selected
1132 gtk_action_set_sensitive(xact, FALSE);
1133 gtk_action_set_sensitive(yact, FALSE);
1134 } else {
1135 gtk_action_set_sensitive(xact, TRUE);
1136 gtk_action_set_sensitive(yact, TRUE);
1137 Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1138 Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1139 Geom::Point mid = nt->_selected_nodes->pointwiseBounds()->midpoint();
1141 if (oldx != mid[Geom::X])
1142 gtk_adjustment_set_value(xadj, sp_pixels_get_units(mid[Geom::X], *unit));
1143 if (oldy != mid[Geom::Y])
1144 gtk_adjustment_set_value(yadj, sp_pixels_get_units(mid[Geom::Y], *unit));
1145 }
1147 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1148 }
1150 static void
1151 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, Geom::Dim2 d)
1152 {
1153 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1154 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1156 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1157 SPUnit const *unit = tracker->getActiveUnit();
1159 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1160 prefs->setDouble(Glib::ustring("/tools/nodes/") + (d == Geom::X ? "x" : "y"),
1161 sp_units_get_pixels(adj->value, *unit));
1162 }
1164 // quit if run by the attr_changed listener
1165 if (g_object_get_data( tbl, "freeze" )) {
1166 return;
1167 }
1169 // in turn, prevent listener from responding
1170 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1172 InkNodeTool *nt = get_node_tool();
1173 if (nt && !nt->_selected_nodes->empty()) {
1174 double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1175 double oldval = nt->_selected_nodes->pointwiseBounds()->midpoint()[d];
1176 Geom::Point delta(0,0);
1177 delta[d] = val - oldval;
1178 nt->_multipath->move(delta);
1179 }
1181 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1182 }
1184 static void
1185 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1186 {
1187 sp_node_path_value_changed(adj, tbl, Geom::X);
1188 }
1190 static void
1191 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1192 {
1193 sp_node_path_value_changed(adj, tbl, Geom::Y);
1194 }
1196 void
1197 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1198 {
1199 {
1200 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1201 SPItem *item = selection->singleItem();
1202 if (item && SP_IS_LPE_ITEM(item)) {
1203 if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1204 gtk_action_set_sensitive(w, TRUE);
1205 } else {
1206 gtk_action_set_sensitive(w, FALSE);
1207 }
1208 } else {
1209 gtk_action_set_sensitive(w, FALSE);
1210 }
1211 }
1212 }
1214 void
1215 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1216 {
1217 sp_node_toolbox_sel_changed (selection, tbl);
1218 }
1222 //################################
1223 //## Node Editing Toolbox ##
1224 //################################
1226 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1227 {
1228 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1229 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1230 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1231 g_object_set_data( holder, "tracker", tracker );
1233 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
1235 {
1236 InkAction* inky = ink_action_new( "NodeInsertAction",
1237 _("Insert node"),
1238 _("Insert new nodes into selected segments"),
1239 INKSCAPE_ICON_NODE_ADD,
1240 secondarySize );
1241 g_object_set( inky, "short_label", _("Insert"), NULL );
1242 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1243 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1244 }
1246 {
1247 InkAction* inky = ink_action_new( "NodeDeleteAction",
1248 _("Delete node"),
1249 _("Delete selected nodes"),
1250 INKSCAPE_ICON_NODE_DELETE,
1251 secondarySize );
1252 g_object_set( inky, "short_label", _("Delete"), NULL );
1253 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1254 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1255 }
1257 {
1258 InkAction* inky = ink_action_new( "NodeJoinAction",
1259 _("Join endnodes"),
1260 _("Join selected endnodes"),
1261 INKSCAPE_ICON_NODE_JOIN,
1262 secondarySize );
1263 g_object_set( inky, "short_label", _("Join"), NULL );
1264 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1265 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1266 }
1268 {
1269 InkAction* inky = ink_action_new( "NodeBreakAction",
1270 _("Break nodes"),
1271 _("Break path at selected nodes"),
1272 INKSCAPE_ICON_NODE_BREAK,
1273 secondarySize );
1274 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1275 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1276 }
1279 {
1280 InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1281 _("Join with segment"),
1282 _("Join selected endnodes with a new segment"),
1283 INKSCAPE_ICON_NODE_JOIN_SEGMENT,
1284 secondarySize );
1285 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1286 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1287 }
1289 {
1290 InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1291 _("Delete segment"),
1292 _("Delete segment between two non-endpoint nodes"),
1293 INKSCAPE_ICON_NODE_DELETE_SEGMENT,
1294 secondarySize );
1295 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1296 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1297 }
1299 {
1300 InkAction* inky = ink_action_new( "NodeCuspAction",
1301 _("Node Cusp"),
1302 _("Make selected nodes corner"),
1303 INKSCAPE_ICON_NODE_TYPE_CUSP,
1304 secondarySize );
1305 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1306 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1307 }
1309 {
1310 InkAction* inky = ink_action_new( "NodeSmoothAction",
1311 _("Node Smooth"),
1312 _("Make selected nodes smooth"),
1313 INKSCAPE_ICON_NODE_TYPE_SMOOTH,
1314 secondarySize );
1315 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1316 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1317 }
1319 {
1320 InkAction* inky = ink_action_new( "NodeSymmetricAction",
1321 _("Node Symmetric"),
1322 _("Make selected nodes symmetric"),
1323 INKSCAPE_ICON_NODE_TYPE_SYMMETRIC,
1324 secondarySize );
1325 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1326 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1327 }
1329 {
1330 InkAction* inky = ink_action_new( "NodeAutoAction",
1331 _("Node Auto"),
1332 _("Make selected nodes auto-smooth"),
1333 INKSCAPE_ICON_NODE_TYPE_AUTO_SMOOTH,
1334 secondarySize );
1335 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_auto), 0 );
1336 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1337 }
1339 {
1340 InkAction* inky = ink_action_new( "NodeLineAction",
1341 _("Node Line"),
1342 _("Make selected segments lines"),
1343 INKSCAPE_ICON_NODE_SEGMENT_LINE,
1344 secondarySize );
1345 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1346 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1347 }
1349 {
1350 InkAction* inky = ink_action_new( "NodeCurveAction",
1351 _("Node Curve"),
1352 _("Make selected segments curves"),
1353 INKSCAPE_ICON_NODE_SEGMENT_CURVE,
1354 secondarySize );
1355 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1356 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1357 }
1359 {
1360 InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1361 _("Show Handles"),
1362 _("Show the Bezier handles of selected nodes"),
1363 INKSCAPE_ICON_SHOW_NODE_HANDLES,
1364 Inkscape::ICON_SIZE_DECORATION );
1365 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1366 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1367 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_handles", true) );
1368 }
1370 {
1371 InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1372 _("Show Outline"),
1373 _("Show the outline of the path"),
1374 INKSCAPE_ICON_SHOW_PATH_OUTLINE,
1375 Inkscape::ICON_SIZE_DECORATION );
1376 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1377 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1378 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_outline", false) );
1379 }
1381 {
1382 InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1383 _("Next path effect parameter"),
1384 _("Show next path effect parameter for editing"),
1385 INKSCAPE_ICON_PATH_EFFECT_PARAMETER_NEXT,
1386 Inkscape::ICON_SIZE_DECORATION );
1387 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1388 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1389 g_object_set_data( holder, "nodes_lpeedit", inky);
1390 }
1392 {
1393 InkToggleAction* inky = ink_toggle_action_new( "ObjectEditClipPathAction",
1394 _("Edit clipping paths"),
1395 _("Show editing controls for clipping paths of selected objects"),
1396 INKSCAPE_ICON_PATH_CLIP_EDIT,
1397 Inkscape::ICON_SIZE_DECORATION );
1398 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1399 g_signal_connect_after( G_OBJECT(inky), "toggled", G_CALLBACK(toggle_edit_clip), desktop );
1400 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(inky), prefs->getBool("/tools/nodes/edit_clipping_paths") );
1401 }
1403 {
1404 InkToggleAction* inky = ink_toggle_action_new( "ObjectEditMaskPathAction",
1405 _("Edit masks"),
1406 _("Show editing controls for masks of selected objects"),
1407 INKSCAPE_ICON_PATH_MASK_EDIT,
1408 Inkscape::ICON_SIZE_DECORATION );
1409 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1410 g_signal_connect_after( G_OBJECT(inky), "toggled", G_CALLBACK(toggle_edit_mask), desktop );
1411 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(inky), prefs->getBool("/tools/nodes/edit_masks") );
1412 }
1414 /* X coord of selected node(s) */
1415 {
1416 EgeAdjustmentAction* eact = 0;
1417 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1418 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1419 eact = create_adjustment_action( "NodeXAction",
1420 _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1421 "/tools/nodes/Xcoord", 0,
1422 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1423 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1424 labels, values, G_N_ELEMENTS(labels),
1425 sp_node_path_x_value_changed );
1426 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1427 g_object_set_data( holder, "nodes_x_action", eact );
1428 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1429 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1430 }
1432 /* Y coord of selected node(s) */
1433 {
1434 EgeAdjustmentAction* eact = 0;
1435 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1436 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1437 eact = create_adjustment_action( "NodeYAction",
1438 _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1439 "/tools/nodes/Ycoord", 0,
1440 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1441 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1442 labels, values, G_N_ELEMENTS(labels),
1443 sp_node_path_y_value_changed );
1444 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1445 g_object_set_data( holder, "nodes_y_action", eact );
1446 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1447 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1448 }
1450 // add the units menu
1451 {
1452 GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1453 gtk_action_group_add_action( mainActions, act );
1454 }
1457 sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1459 //watch selection
1460 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1462 sigc::connection *c_selection_changed =
1463 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1464 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1465 pool->add_connection ("selection-changed", c_selection_changed);
1467 sigc::connection *c_selection_modified =
1468 new sigc::connection (sp_desktop_selection (desktop)->connectModified
1469 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1470 pool->add_connection ("selection-modified", c_selection_modified);
1472 sigc::connection *c_subselection_changed =
1473 new sigc::connection (desktop->connectToolSubselectionChanged
1474 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1475 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1477 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1479 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1480 } // end of sp_node_toolbox_prep()
1483 //########################
1484 //## Zoom Toolbox ##
1485 //########################
1487 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1488 {
1489 // no custom GtkAction setup needed
1490 } // end of sp_zoom_toolbox_prep()
1492 void
1493 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1494 {
1495 toolbox_set_desktop(toolbox,
1496 desktop,
1497 setup_tool_toolbox,
1498 update_tool_toolbox,
1499 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1500 "event_context_connection")));
1501 }
1504 void
1505 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1506 {
1507 toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1508 desktop,
1509 setup_aux_toolbox,
1510 update_aux_toolbox,
1511 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1512 "event_context_connection")));
1513 }
1515 void
1516 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1517 {
1518 toolbox_set_desktop(toolbox,
1519 desktop,
1520 setup_commands_toolbox,
1521 update_commands_toolbox,
1522 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1523 "event_context_connection")));
1524 }
1526 void
1527 sp_snap_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1528 {
1529 toolbox_set_desktop(toolbox,
1530 desktop,
1531 setup_snap_toolbox,
1532 update_snap_toolbox,
1533 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1534 "event_context_connection")));
1535 }
1538 static void
1539 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1540 {
1541 gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1542 SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1544 if (old_desktop) {
1545 GList *children, *iter;
1547 children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1548 for ( iter = children ; iter ; iter = iter->next ) {
1549 gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1550 }
1551 g_list_free(children);
1552 }
1554 g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1556 if (desktop) {
1557 gtk_widget_set_sensitive(toolbox, TRUE);
1558 setup_func(toolbox, desktop);
1559 update_func(desktop, desktop->event_context, toolbox);
1560 *conn = desktop->connectEventContextChanged
1561 (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1562 } else {
1563 gtk_widget_set_sensitive(toolbox, FALSE);
1564 }
1566 } // end of toolbox_set_desktop()
1569 static void
1570 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1571 {
1572 gchar const * descr =
1573 "<ui>"
1574 " <toolbar name='ToolToolbar'>"
1575 " <toolitem action='ToolSelector' />"
1576 " <toolitem action='ToolNode' />"
1577 " <toolitem action='ToolTweak' />"
1578 " <toolitem action='ToolZoom' />"
1579 " <toolitem action='ToolRect' />"
1580 " <toolitem action='Tool3DBox' />"
1581 " <toolitem action='ToolArc' />"
1582 " <toolitem action='ToolStar' />"
1583 " <toolitem action='ToolSpiral' />"
1584 " <toolitem action='ToolPencil' />"
1585 " <toolitem action='ToolPen' />"
1586 " <toolitem action='ToolCalligraphic' />"
1587 " <toolitem action='ToolEraser' />"
1588 // " <toolitem action='ToolLPETool' />"
1589 " <toolitem action='ToolPaintBucket' />"
1590 " <toolitem action='ToolText' />"
1591 " <toolitem action='ToolConnector' />"
1592 " <toolitem action='ToolGradient' />"
1593 " <toolitem action='ToolDropper' />"
1594 " </toolbar>"
1595 "</ui>";
1596 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1597 GtkUIManager* mgr = gtk_ui_manager_new();
1598 GError* errVal = 0;
1599 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1601 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1602 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1604 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" );
1605 if ( prefs->getBool("/toolbox/icononly", true) ) {
1606 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1607 }
1608 Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
1609 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1611 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
1612 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1614 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1616 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1617 if ( child ) {
1618 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1619 }
1621 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1622 // Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
1623 }
1626 static void
1627 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1628 {
1629 gchar const *const tname = ( eventcontext
1630 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1631 : NULL );
1632 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1634 for (int i = 0 ; tools[i].type_name ; i++ ) {
1635 Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1636 if ( act ) {
1637 bool setActive = tname && !strcmp(tname, tools[i].type_name);
1638 Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1639 if ( verbAct ) {
1640 verbAct->set_active(setActive);
1641 }
1642 }
1643 }
1644 }
1646 static void
1647 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1648 {
1649 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1650 GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1651 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1652 GtkUIManager* mgr = gtk_ui_manager_new();
1653 GError* errVal = 0;
1654 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1655 gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1657 std::map<std::string, GtkWidget*> dataHolders;
1659 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1660 if ( aux_toolboxes[i].prep_func ) {
1661 // converted to GtkActions and UIManager
1663 GtkWidget* kludge = gtk_toolbar_new();
1664 g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1665 g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1666 dataHolders[aux_toolboxes[i].type_name] = kludge;
1667 aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1668 } else {
1670 GtkWidget *sub_toolbox = 0;
1671 if (aux_toolboxes[i].create_func == NULL)
1672 sub_toolbox = sp_empty_toolbox_new(desktop);
1673 else {
1674 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1675 }
1677 gtk_size_group_add_widget( grouper, sub_toolbox );
1679 gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1680 g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1682 }
1683 }
1685 // Second pass to create toolbars *after* all GtkActions are created
1686 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1687 if ( aux_toolboxes[i].prep_func ) {
1688 // converted to GtkActions and UIManager
1690 GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1692 GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1693 gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1695 gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1696 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1697 g_free( tmp );
1698 tmp = 0;
1700 if ( prefs->getBool( "/toolbox/icononly", true) ) {
1701 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1702 }
1704 Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
1705 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1707 gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1709 if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1710 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1711 swatch->setDesktop( desktop );
1712 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1713 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1714 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1715 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 );
1716 }
1718 gtk_widget_show_all( holder );
1719 sp_set_font_size_smaller( holder );
1721 gtk_size_group_add_widget( grouper, holder );
1723 gtk_container_add( GTK_CONTAINER(toolbox), holder );
1724 g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1725 }
1726 }
1728 g_object_unref( G_OBJECT(grouper) );
1729 }
1731 static void
1732 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1733 {
1734 gchar const *tname = ( eventcontext
1735 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1736 : NULL );
1737 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1738 GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1739 if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1740 gtk_widget_show_all(sub_toolbox);
1741 g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1742 } else {
1743 gtk_widget_hide(sub_toolbox);
1744 }
1745 }
1746 }
1748 static void
1749 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1750 {
1751 gchar const * descr =
1752 "<ui>"
1753 " <toolbar name='CommandsToolbar'>"
1754 " <toolitem action='FileNew' />"
1755 " <toolitem action='FileOpen' />"
1756 " <toolitem action='FileSave' />"
1757 " <toolitem action='FilePrint' />"
1758 " <separator />"
1759 " <toolitem action='FileImport' />"
1760 " <toolitem action='FileExport' />"
1761 " <separator />"
1762 " <toolitem action='EditUndo' />"
1763 " <toolitem action='EditRedo' />"
1764 " <separator />"
1765 " <toolitem action='EditCopy' />"
1766 " <toolitem action='EditCut' />"
1767 " <toolitem action='EditPaste' />"
1768 " <separator />"
1769 " <toolitem action='ZoomSelection' />"
1770 " <toolitem action='ZoomDrawing' />"
1771 " <toolitem action='ZoomPage' />"
1772 " <separator />"
1773 " <toolitem action='EditDuplicate' />"
1774 " <toolitem action='EditClone' />"
1775 " <toolitem action='EditUnlinkClone' />"
1776 " <separator />"
1777 " <toolitem action='SelectionGroup' />"
1778 " <toolitem action='SelectionUnGroup' />"
1779 " <separator />"
1780 " <toolitem action='DialogFillStroke' />"
1781 " <toolitem action='DialogText' />"
1782 " <toolitem action='DialogLayers' />"
1783 " <toolitem action='DialogXMLEditor' />"
1784 " <toolitem action='DialogAlignDistribute' />"
1785 " <separator />"
1786 " <toolitem action='DialogPreferences' />"
1787 " <toolitem action='DialogDocumentProperties' />"
1788 " </toolbar>"
1789 "</ui>";
1790 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1791 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1793 GtkUIManager* mgr = gtk_ui_manager_new();
1794 GError* errVal = 0;
1796 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1797 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1799 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1800 if ( prefs->getBool("/toolbox/icononly", true) ) {
1801 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1802 }
1804 Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
1805 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1807 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1808 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1811 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1813 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1814 if ( child ) {
1815 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1816 }
1818 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1819 }
1821 static void
1822 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1823 {
1824 }
1826 void toggle_snap_callback (GtkToggleAction *act, gpointer data) { //data points to the toolbox
1828 if (g_object_get_data(G_OBJECT(data), "freeze" )) {
1829 return;
1830 }
1832 gpointer ptr = g_object_get_data(G_OBJECT(data), "desktop");
1833 g_assert(ptr != NULL);
1835 SPDesktop *dt = reinterpret_cast<SPDesktop*>(ptr);
1836 SPNamedView *nv = sp_desktop_namedview(dt);
1837 SPDocument *doc = SP_OBJECT_DOCUMENT(nv);
1839 if (dt == NULL || nv == NULL) {
1840 g_warning("No desktop or namedview specified (in toggle_snap_callback)!");
1841 return;
1842 }
1844 Inkscape::XML::Node *repr = SP_OBJECT_REPR(nv);
1846 if (repr == NULL) {
1847 g_warning("This namedview doesn't have a xml representation attached!");
1848 return;
1849 }
1851 bool saved = sp_document_get_undo_sensitive(doc);
1852 sp_document_set_undo_sensitive(doc, false);
1854 bool v = false;
1855 SPAttributeEnum attr = (SPAttributeEnum) GPOINTER_TO_INT(g_object_get_data(G_OBJECT(act), "SP_ATTR_INKSCAPE"));
1857 switch (attr) {
1858 case SP_ATTR_INKSCAPE_SNAP_GLOBAL:
1859 dt->toggleSnapGlobal();
1860 break;
1861 case SP_ATTR_INKSCAPE_SNAP_BBOX:
1862 v = nv->snap_manager.snapprefs.getSnapModeBBox();
1863 sp_repr_set_boolean(repr, "inkscape:snap-bbox", !v);
1864 break;
1865 case SP_ATTR_INKSCAPE_BBOX_PATHS:
1866 v = nv->snap_manager.snapprefs.getSnapToBBoxPath();
1867 sp_repr_set_boolean(repr, "inkscape:bbox-paths", !v);
1868 break;
1869 case SP_ATTR_INKSCAPE_BBOX_NODES:
1870 v = nv->snap_manager.snapprefs.getSnapToBBoxNode();
1871 sp_repr_set_boolean(repr, "inkscape:bbox-nodes", !v);
1872 break;
1873 case SP_ATTR_INKSCAPE_SNAP_NODES:
1874 v = nv->snap_manager.snapprefs.getSnapModeNode();
1875 sp_repr_set_boolean(repr, "inkscape:snap-nodes", !v);
1876 break;
1877 case SP_ATTR_INKSCAPE_OBJECT_PATHS:
1878 v = nv->snap_manager.snapprefs.getSnapToItemPath();
1879 sp_repr_set_boolean(repr, "inkscape:object-paths", !v);
1880 break;
1881 case SP_ATTR_INKSCAPE_OBJECT_NODES:
1882 v = nv->snap_manager.snapprefs.getSnapToItemNode();
1883 sp_repr_set_boolean(repr, "inkscape:object-nodes", !v);
1884 break;
1885 case SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES:
1886 v = nv->snap_manager.snapprefs.getSnapSmoothNodes();
1887 sp_repr_set_boolean(repr, "inkscape:snap-smooth-nodes", !v);
1888 break;
1889 case SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS:
1890 v = nv->snap_manager.snapprefs.getSnapIntersectionCS();
1891 sp_repr_set_boolean(repr, "inkscape:snap-intersection-paths", !v);
1892 break;
1893 case SP_ATTR_INKSCAPE_SNAP_CENTER:
1894 v = nv->snap_manager.snapprefs.getIncludeItemCenter();
1895 sp_repr_set_boolean(repr, "inkscape:snap-center", !v);
1896 break;
1897 case SP_ATTR_INKSCAPE_SNAP_GRIDS:
1898 v = nv->snap_manager.snapprefs.getSnapToGrids();
1899 sp_repr_set_boolean(repr, "inkscape:snap-grids", !v);
1900 break;
1901 case SP_ATTR_INKSCAPE_SNAP_TO_GUIDES:
1902 v = nv->snap_manager.snapprefs.getSnapToGuides();
1903 sp_repr_set_boolean(repr, "inkscape:snap-to-guides", !v);
1904 break;
1905 case SP_ATTR_INKSCAPE_SNAP_PAGE:
1906 v = nv->snap_manager.snapprefs.getSnapToPageBorder();
1907 sp_repr_set_boolean(repr, "inkscape:snap-page", !v);
1908 break;
1909 /*case SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE:
1910 v = nv->snap_manager.snapprefs.getSnapIntersectionGG();
1911 sp_repr_set_boolean(repr, "inkscape:snap-intersection-grid-guide", !v);
1912 break;*/
1913 case SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS:
1914 v = nv->snap_manager.snapprefs.getSnapLineMidpoints();
1915 sp_repr_set_boolean(repr, "inkscape:snap-midpoints", !v);
1916 break;
1917 case SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS:
1918 v = nv->snap_manager.snapprefs.getSnapObjectMidpoints();
1919 sp_repr_set_boolean(repr, "inkscape:snap-object-midpoints", !v);
1920 break;
1921 case SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS:
1922 v = nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints();
1923 sp_repr_set_boolean(repr, "inkscape:snap-bbox-edge-midpoints", !v);
1924 break;
1925 case SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS:
1926 v = nv->snap_manager.snapprefs.getSnapBBoxMidpoints();
1927 sp_repr_set_boolean(repr, "inkscape:snap-bbox-midpoints", !v);
1928 break;
1929 default:
1930 g_warning("toggle_snap_callback has been called with an ID for which no action has been defined");
1931 break;
1932 }
1934 // The snapping preferences are stored in the document, and therefore toggling makes the document dirty
1935 doc->setModifiedSinceSave();
1937 sp_document_set_undo_sensitive(doc, saved);
1938 }
1940 void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1941 {
1942 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1943 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
1945 gchar const * descr =
1946 "<ui>"
1947 " <toolbar name='SnapToolbar'>"
1948 " <toolitem action='ToggleSnapGlobal' />"
1949 " <separator />"
1950 " <toolitem action='ToggleSnapFromBBoxCorner' />"
1951 " <toolitem action='ToggleSnapToBBoxPath' />"
1952 " <toolitem action='ToggleSnapToBBoxNode' />"
1953 " <toolitem action='ToggleSnapToFromBBoxEdgeMidpoints' />"
1954 " <toolitem action='ToggleSnapToFromBBoxCenters' />"
1955 " <separator />"
1956 " <toolitem action='ToggleSnapFromNode' />"
1957 " <toolitem action='ToggleSnapToItemPath' />"
1958 " <toolitem action='ToggleSnapToPathIntersections' />"
1959 " <toolitem action='ToggleSnapToItemNode' />"
1960 " <toolitem action='ToggleSnapToSmoothNodes' />"
1961 " <toolitem action='ToggleSnapToFromLineMidpoints' />"
1962 " <toolitem action='ToggleSnapToFromObjectCenters' />"
1963 " <toolitem action='ToggleSnapToFromRotationCenter' />"
1964 " <separator />"
1965 " <toolitem action='ToggleSnapToPageBorder' />"
1966 " <toolitem action='ToggleSnapToGrids' />"
1967 " <toolitem action='ToggleSnapToGuides' />"
1968 //" <toolitem action='ToggleSnapToGridGuideIntersections' />"
1969 " </toolbar>"
1970 "</ui>";
1972 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
1974 {
1975 InkToggleAction* act = ink_toggle_action_new("ToggleSnapGlobal",
1976 _("Snap"), _("Enable snapping"), INKSCAPE_ICON_SNAP, secondarySize,
1977 SP_ATTR_INKSCAPE_SNAP_GLOBAL);
1979 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
1980 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
1981 }
1983 {
1984 InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromBBoxCorner",
1985 _("Bounding box"), _("Snap bounding box corners"), INKSCAPE_ICON_SNAP_BOUNDING_BOX,
1986 secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX);
1988 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
1989 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
1990 }
1992 {
1993 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxPath",
1994 _("Bounding box edges"), _("Snap to edges of a bounding box"),
1995 INKSCAPE_ICON_SNAP_BOUNDING_BOX_EDGES, secondarySize, SP_ATTR_INKSCAPE_BBOX_PATHS);
1997 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
1998 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
1999 }
2001 {
2002 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxNode",
2003 _("Bounding box corners"), _("Snap to bounding box corners"),
2004 INKSCAPE_ICON_SNAP_BOUNDING_BOX_CORNERS, secondarySize, SP_ATTR_INKSCAPE_BBOX_NODES);
2006 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2007 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2008 }
2010 {
2011 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxEdgeMidpoints",
2012 _("BBox Edge Midpoints"), _("Snap from and to midpoints of bounding box edges"),
2013 INKSCAPE_ICON_SNAP_BOUNDING_BOX_MIDPOINTS, secondarySize,
2014 SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS);
2016 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2017 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2018 }
2020 {
2021 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxCenters",
2022 _("BBox Centers"), _("Snapping from and to centers of bounding boxes"),
2023 INKSCAPE_ICON_SNAP_BOUNDING_BOX_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS);
2025 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2026 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2027 }
2029 {
2030 InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromNode",
2031 _("Nodes"), _("Snap nodes or handles"), INKSCAPE_ICON_SNAP_NODES, secondarySize, SP_ATTR_INKSCAPE_SNAP_NODES);
2033 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2034 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2035 }
2037 {
2038 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemPath",
2039 _("Paths"), _("Snap to paths"), INKSCAPE_ICON_SNAP_NODES_PATH, secondarySize,
2040 SP_ATTR_INKSCAPE_OBJECT_PATHS);
2042 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2043 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2044 }
2046 {
2047 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPathIntersections",
2048 _("Path intersections"), _("Snap to path intersections"),
2049 INKSCAPE_ICON_SNAP_NODES_INTERSECTION, secondarySize, SP_ATTR_INKSCAPE_SNAP_INTERS_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("ToggleSnapToItemNode",
2057 _("To nodes"), _("Snap to cusp nodes"), INKSCAPE_ICON_SNAP_NODES_CUSP, secondarySize,
2058 SP_ATTR_INKSCAPE_OBJECT_NODES);
2060 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2061 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2062 }
2064 {
2065 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToSmoothNodes",
2066 _("Smooth nodes"), _("Snap to smooth nodes"), INKSCAPE_ICON_SNAP_NODES_SMOOTH,
2067 secondarySize, SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES);
2069 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2070 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2071 }
2073 {
2074 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromLineMidpoints",
2075 _("Line Midpoints"), _("Snap from and to midpoints of line segments"),
2076 INKSCAPE_ICON_SNAP_NODES_MIDPOINT, secondarySize, SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS);
2078 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2079 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2080 }
2082 {
2083 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromObjectCenters",
2084 _("Object Centers"), _("Snap from and to centers of objects"),
2085 INKSCAPE_ICON_SNAP_NODES_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_OBJECT_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("ToggleSnapToFromRotationCenter",
2093 _("Rotation Centers"), _("Snap from and to an item's rotation center"),
2094 INKSCAPE_ICON_SNAP_NODES_ROTATION_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_CENTER);
2096 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2097 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2098 }
2100 {
2101 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPageBorder",
2102 _("Page border"), _("Snap to the page border"), INKSCAPE_ICON_SNAP_PAGE,
2103 secondarySize, SP_ATTR_INKSCAPE_SNAP_PAGE);
2105 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2106 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2107 }
2109 {
2110 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGrids",
2111 _("Grids"), _("Snap to grids"), INKSCAPE_ICON_GRID_RECTANGULAR, secondarySize,
2112 SP_ATTR_INKSCAPE_SNAP_GRIDS);
2114 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2115 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2116 }
2118 {
2119 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGuides",
2120 _("Guides"), _("Snap to guides"), INKSCAPE_ICON_GUIDES, secondarySize,
2121 SP_ATTR_INKSCAPE_SNAP_TO_GUIDES);
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("ToggleSnapToGridGuideIntersections",
2129 _("Grid/guide intersections"), _("Snap to intersections of a grid with a guide"),
2130 INKSCAPE_ICON_SNAP_GRID_GUIDE_INTERSECTIONS, secondarySize,
2131 SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE);
2133 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2134 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2135 }*/
2137 GtkUIManager* mgr = gtk_ui_manager_new();
2138 GError* errVal = 0;
2140 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
2141 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
2143 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/SnapToolbar" );
2144 if ( prefs->getBool("/toolbox/icononly", true) ) {
2145 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
2146 }
2148 Inkscape::IconSize toolboxSize = prefToSize("/toolbox/secondary");
2149 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
2151 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
2152 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
2154 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
2156 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
2157 if ( child ) {
2158 gtk_container_remove( GTK_CONTAINER(toolbox), child );
2159 }
2161 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
2163 }
2165 void update_snap_toolbox(SPDesktop *desktop, SPEventContext */*eventcontext*/, GtkWidget *toolbox)
2166 {
2167 g_assert(desktop != NULL);
2168 g_assert(toolbox != NULL);
2170 SPNamedView *nv = sp_desktop_namedview(desktop);
2171 if (nv == NULL) {
2172 g_warning("Namedview cannot be retrieved (in update_snap_toolbox)!");
2173 return;
2174 }
2176 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2178 Glib::RefPtr<Gtk::Action> act1 = mainActions->get_action("ToggleSnapGlobal");
2179 Glib::RefPtr<Gtk::Action> act2 = mainActions->get_action("ToggleSnapFromBBoxCorner");
2180 Glib::RefPtr<Gtk::Action> act3 = mainActions->get_action("ToggleSnapToBBoxPath");
2181 Glib::RefPtr<Gtk::Action> act4 = mainActions->get_action("ToggleSnapToBBoxNode");
2182 Glib::RefPtr<Gtk::Action> act4b = mainActions->get_action("ToggleSnapToFromBBoxEdgeMidpoints");
2183 Glib::RefPtr<Gtk::Action> act4c = mainActions->get_action("ToggleSnapToFromBBoxCenters");
2184 Glib::RefPtr<Gtk::Action> act5 = mainActions->get_action("ToggleSnapFromNode");
2185 Glib::RefPtr<Gtk::Action> act6 = mainActions->get_action("ToggleSnapToItemPath");
2186 Glib::RefPtr<Gtk::Action> act6b = mainActions->get_action("ToggleSnapToPathIntersections");
2187 Glib::RefPtr<Gtk::Action> act7 = mainActions->get_action("ToggleSnapToItemNode");
2188 Glib::RefPtr<Gtk::Action> act8 = mainActions->get_action("ToggleSnapToSmoothNodes");
2189 Glib::RefPtr<Gtk::Action> act9 = mainActions->get_action("ToggleSnapToFromLineMidpoints");
2190 Glib::RefPtr<Gtk::Action> act10 = mainActions->get_action("ToggleSnapToFromObjectCenters");
2191 Glib::RefPtr<Gtk::Action> act11 = mainActions->get_action("ToggleSnapToFromRotationCenter");
2192 Glib::RefPtr<Gtk::Action> act12 = mainActions->get_action("ToggleSnapToPageBorder");
2193 //Glib::RefPtr<Gtk::Action> act13 = mainActions->get_action("ToggleSnapToGridGuideIntersections");
2194 Glib::RefPtr<Gtk::Action> act14 = mainActions->get_action("ToggleSnapToGrids");
2195 Glib::RefPtr<Gtk::Action> act15 = mainActions->get_action("ToggleSnapToGuides");
2198 if (!act1) {
2199 return; // The snap actions haven't been defined yet (might be the case during startup)
2200 }
2202 // The ..._set_active calls below will toggle the buttons, but this shouldn't lead to
2203 // changes in our document because we're only updating the UI;
2204 // Setting the "freeze" parameter to true will block the code in toggle_snap_callback()
2205 g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(TRUE));
2207 bool const c1 = nv->snap_manager.snapprefs.getSnapEnabledGlobally();
2208 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act1->gobj()), c1);
2210 bool const c2 = nv->snap_manager.snapprefs.getSnapModeBBox();
2211 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act2->gobj()), c2);
2212 gtk_action_set_sensitive(GTK_ACTION(act2->gobj()), c1);
2214 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act3->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxPath());
2215 gtk_action_set_sensitive(GTK_ACTION(act3->gobj()), c1 && c2);
2216 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxNode());
2217 gtk_action_set_sensitive(GTK_ACTION(act4->gobj()), c1 && c2);
2218 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4b->gobj()), nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints());
2219 gtk_action_set_sensitive(GTK_ACTION(act4b->gobj()), c1 && c2);
2220 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4c->gobj()), nv->snap_manager.snapprefs.getSnapBBoxMidpoints());
2221 gtk_action_set_sensitive(GTK_ACTION(act4c->gobj()), c1 && c2);
2223 bool const c3 = nv->snap_manager.snapprefs.getSnapModeNode();
2224 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act5->gobj()), c3);
2225 gtk_action_set_sensitive(GTK_ACTION(act5->gobj()), c1);
2227 bool const c4 = nv->snap_manager.snapprefs.getSnapToItemPath();
2228 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6->gobj()), c4);
2229 gtk_action_set_sensitive(GTK_ACTION(act6->gobj()), c1 && c3);
2230 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6b->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionCS());
2231 gtk_action_set_sensitive(GTK_ACTION(act6b->gobj()), c1 && c3 && c4);
2232 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act7->gobj()), nv->snap_manager.snapprefs.getSnapToItemNode());
2233 gtk_action_set_sensitive(GTK_ACTION(act7->gobj()), c1 && c3);
2234 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act8->gobj()), nv->snap_manager.snapprefs.getSnapSmoothNodes());
2235 gtk_action_set_sensitive(GTK_ACTION(act8->gobj()), c1 && c3);
2236 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act9->gobj()), nv->snap_manager.snapprefs.getSnapLineMidpoints());
2237 gtk_action_set_sensitive(GTK_ACTION(act9->gobj()), c1 && c3);
2238 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act10->gobj()), nv->snap_manager.snapprefs.getSnapObjectMidpoints());
2239 gtk_action_set_sensitive(GTK_ACTION(act10->gobj()), c1 && c3);
2240 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act11->gobj()), nv->snap_manager.snapprefs.getIncludeItemCenter());
2241 gtk_action_set_sensitive(GTK_ACTION(act11->gobj()), c1 && c3);
2243 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act12->gobj()), nv->snap_manager.snapprefs.getSnapToPageBorder());
2244 gtk_action_set_sensitive(GTK_ACTION(act12->gobj()), c1);
2245 //gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act13->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionGG());
2246 //gtk_action_set_sensitive(GTK_ACTION(act13->gobj()), c1);
2248 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act14->gobj()), nv->snap_manager.snapprefs.getSnapToGrids());
2249 gtk_action_set_sensitive(GTK_ACTION(act14->gobj()), c1);
2250 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act15->gobj()), nv->snap_manager.snapprefs.getSnapToGuides());
2251 gtk_action_set_sensitive(GTK_ACTION(act15->gobj()), c1);
2254 g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(FALSE)); // unfreeze (see above)
2255 }
2257 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
2258 {
2259 gtk_widget_show(toolbox_toplevel);
2260 GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
2262 GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
2263 if (!shown_toolbox) {
2264 return;
2265 }
2266 gtk_widget_show(toolbox);
2268 gtk_widget_show_all(shown_toolbox);
2269 }
2271 static GtkWidget *
2272 sp_empty_toolbox_new(SPDesktop *desktop)
2273 {
2274 GtkWidget *tbl = gtk_toolbar_new();
2275 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
2276 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
2278 gtk_widget_show_all(tbl);
2279 sp_set_font_size_smaller (tbl);
2281 return tbl;
2282 }
2284 #define MODE_LABEL_WIDTH 70
2286 //########################
2287 //## Star ##
2288 //########################
2290 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2291 {
2292 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2294 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2295 // do not remember prefs if this call is initiated by an undo change, because undoing object
2296 // creation sets bogus values to its attributes before it is deleted
2297 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2298 prefs->setInt("/tools/shapes/star/magnitude", (gint)adj->value);
2299 }
2301 // quit if run by the attr_changed listener
2302 if (g_object_get_data( dataKludge, "freeze" )) {
2303 return;
2304 }
2306 // in turn, prevent listener from responding
2307 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2309 bool modmade = false;
2311 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2312 GSList const *items = selection->itemList();
2313 for (; items != NULL; items = items->next) {
2314 if (SP_IS_STAR((SPItem *) items->data)) {
2315 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2316 sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
2317 sp_repr_set_svg_double(repr, "sodipodi:arg2",
2318 (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
2319 + M_PI / (gint)adj->value));
2320 SP_OBJECT((SPItem *) items->data)->updateRepr();
2321 modmade = true;
2322 }
2323 }
2324 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2325 _("Star: Change number of corners"));
2327 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2328 }
2330 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2331 {
2332 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2334 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2335 if (!IS_NAN(adj->value)) {
2336 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2337 prefs->setDouble("/tools/shapes/star/proportion", adj->value);
2338 }
2339 }
2341 // quit if run by the attr_changed listener
2342 if (g_object_get_data( dataKludge, "freeze" )) {
2343 return;
2344 }
2346 // in turn, prevent listener from responding
2347 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2349 bool modmade = false;
2350 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2351 GSList const *items = selection->itemList();
2352 for (; items != NULL; items = items->next) {
2353 if (SP_IS_STAR((SPItem *) items->data)) {
2354 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2356 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2357 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2358 if (r2 < r1) {
2359 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
2360 } else {
2361 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
2362 }
2364 SP_OBJECT((SPItem *) items->data)->updateRepr();
2365 modmade = true;
2366 }
2367 }
2369 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2370 _("Star: Change spoke ratio"));
2372 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2373 }
2375 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
2376 {
2377 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2378 bool flat = ege_select_one_action_get_active( act ) == 0;
2380 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2381 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2382 prefs->setBool( "/tools/shapes/star/isflatsided", flat);
2383 }
2385 // quit if run by the attr_changed listener
2386 if (g_object_get_data( dataKludge, "freeze" )) {
2387 return;
2388 }
2390 // in turn, prevent listener from responding
2391 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2393 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2394 GSList const *items = selection->itemList();
2395 GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2396 bool modmade = false;
2398 if ( prop_action ) {
2399 gtk_action_set_sensitive( prop_action, !flat );
2400 }
2402 for (; items != NULL; items = items->next) {
2403 if (SP_IS_STAR((SPItem *) items->data)) {
2404 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2405 repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
2406 SP_OBJECT((SPItem *) items->data)->updateRepr();
2407 modmade = true;
2408 }
2409 }
2411 if (modmade) {
2412 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2413 flat ? _("Make polygon") : _("Make star"));
2414 }
2416 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2417 }
2419 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2420 {
2421 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2423 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2424 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2425 prefs->setDouble("/tools/shapes/star/rounded", (gdouble) adj->value);
2426 }
2428 // quit if run by the attr_changed listener
2429 if (g_object_get_data( dataKludge, "freeze" )) {
2430 return;
2431 }
2433 // in turn, prevent listener from responding
2434 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2436 bool modmade = false;
2438 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2439 GSList const *items = selection->itemList();
2440 for (; items != NULL; items = items->next) {
2441 if (SP_IS_STAR((SPItem *) items->data)) {
2442 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2443 sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2444 SP_OBJECT(items->data)->updateRepr();
2445 modmade = true;
2446 }
2447 }
2448 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2449 _("Star: Change rounding"));
2451 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2452 }
2454 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2455 {
2456 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2458 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2459 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2460 prefs->setDouble("/tools/shapes/star/randomized", (gdouble) adj->value);
2461 }
2463 // quit if run by the attr_changed listener
2464 if (g_object_get_data( dataKludge, "freeze" )) {
2465 return;
2466 }
2468 // in turn, prevent listener from responding
2469 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2471 bool modmade = false;
2473 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2474 GSList const *items = selection->itemList();
2475 for (; items != NULL; items = items->next) {
2476 if (SP_IS_STAR((SPItem *) items->data)) {
2477 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2478 sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2479 SP_OBJECT(items->data)->updateRepr();
2480 modmade = true;
2481 }
2482 }
2483 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2484 _("Star: Change randomization"));
2486 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2487 }
2490 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2491 gchar const */*old_value*/, gchar const */*new_value*/,
2492 bool /*is_interactive*/, gpointer data)
2493 {
2494 GtkWidget *tbl = GTK_WIDGET(data);
2496 // quit if run by the _changed callbacks
2497 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2498 return;
2499 }
2501 // in turn, prevent callbacks from responding
2502 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2504 GtkAdjustment *adj = 0;
2506 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2507 bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2509 if (!strcmp(name, "inkscape:randomized")) {
2510 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2511 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2512 } else if (!strcmp(name, "inkscape:rounded")) {
2513 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2514 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2515 } else if (!strcmp(name, "inkscape:flatsided")) {
2516 GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2517 char const *flatsides = repr->attribute("inkscape:flatsided");
2518 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2519 if ( flatsides && !strcmp(flatsides,"false") ) {
2520 ege_select_one_action_set_active( flat_action, 1 );
2521 gtk_action_set_sensitive( prop_action, TRUE );
2522 } else {
2523 ege_select_one_action_set_active( flat_action, 0 );
2524 gtk_action_set_sensitive( prop_action, FALSE );
2525 }
2526 } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2527 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2528 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2529 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2530 if (r2 < r1) {
2531 gtk_adjustment_set_value(adj, r2/r1);
2532 } else {
2533 gtk_adjustment_set_value(adj, r1/r2);
2534 }
2535 } else if (!strcmp(name, "sodipodi:sides")) {
2536 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2537 gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2538 }
2540 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2541 }
2544 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2545 {
2546 NULL, /* child_added */
2547 NULL, /* child_removed */
2548 star_tb_event_attr_changed,
2549 NULL, /* content_changed */
2550 NULL /* order_changed */
2551 };
2554 /**
2555 * \param selection Should not be NULL.
2556 */
2557 static void
2558 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2559 {
2560 int n_selected = 0;
2561 Inkscape::XML::Node *repr = NULL;
2563 purge_repr_listener( tbl, tbl );
2565 for (GSList const *items = selection->itemList();
2566 items != NULL;
2567 items = items->next)
2568 {
2569 if (SP_IS_STAR((SPItem *) items->data)) {
2570 n_selected++;
2571 repr = SP_OBJECT_REPR((SPItem *) items->data);
2572 }
2573 }
2575 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2577 if (n_selected == 0) {
2578 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2579 } else if (n_selected == 1) {
2580 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2582 if (repr) {
2583 g_object_set_data( tbl, "repr", repr );
2584 Inkscape::GC::anchor(repr);
2585 sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2586 sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2587 }
2588 } else {
2589 // FIXME: implement averaging of all parameters for multiple selected stars
2590 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2591 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2592 }
2593 }
2596 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2597 {
2598 // FIXME: in this and all other _default functions, set some flag telling the value_changed
2599 // callbacks to lump all the changes for all selected objects in one undo step
2601 GtkAdjustment *adj = 0;
2603 // fixme: make settable in prefs!
2604 gint mag = 5;
2605 gdouble prop = 0.5;
2606 gboolean flat = FALSE;
2607 gdouble randomized = 0;
2608 gdouble rounded = 0;
2610 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2611 ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2613 GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2614 gtk_action_set_sensitive( sb2, !flat );
2616 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2617 gtk_adjustment_set_value(adj, mag);
2618 gtk_adjustment_value_changed(adj);
2620 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2621 gtk_adjustment_set_value(adj, prop);
2622 gtk_adjustment_value_changed(adj);
2624 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2625 gtk_adjustment_set_value(adj, rounded);
2626 gtk_adjustment_value_changed(adj);
2628 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2629 gtk_adjustment_set_value(adj, randomized);
2630 gtk_adjustment_value_changed(adj);
2631 }
2634 void
2635 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2636 {
2637 GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2638 if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2639 GtkWidget *l = gtk_label_new(NULL);
2640 gtk_label_set_markup(GTK_LABEL(l), title);
2641 gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2642 if ( GTK_IS_TOOLBAR(tbl) ) {
2643 gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2644 } else {
2645 gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2646 }
2647 gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2648 }
2651 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2652 {
2653 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
2655 {
2656 EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2657 ege_output_action_set_use_markup( act, TRUE );
2658 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2659 g_object_set_data( holder, "mode_action", act );
2660 }
2662 {
2663 EgeAdjustmentAction* eact = 0;
2664 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2665 bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2667 /* Flatsided checkbox */
2668 {
2669 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2671 GtkTreeIter iter;
2672 gtk_list_store_append( model, &iter );
2673 gtk_list_store_set( model, &iter,
2674 0, _("Polygon"),
2675 1, _("Regular polygon (with one handle) instead of a star"),
2676 2, INKSCAPE_ICON_DRAW_POLYGON,
2677 -1 );
2679 gtk_list_store_append( model, &iter );
2680 gtk_list_store_set( model, &iter,
2681 0, _("Star"),
2682 1, _("Star instead of a regular polygon (with one handle)"),
2683 2, INKSCAPE_ICON_DRAW_STAR,
2684 -1 );
2686 EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2687 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2688 g_object_set_data( holder, "flat_action", act );
2690 ege_select_one_action_set_appearance( act, "full" );
2691 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2692 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2693 ege_select_one_action_set_icon_column( act, 2 );
2694 ege_select_one_action_set_icon_size( act, secondarySize );
2695 ege_select_one_action_set_tooltip_column( act, 1 );
2697 ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2698 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2699 }
2701 /* Magnitude */
2702 {
2703 gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2704 gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2705 eact = create_adjustment_action( "MagnitudeAction",
2706 _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2707 "/tools/shapes/star/magnitude", 3,
2708 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2709 3, 1024, 1, 5,
2710 labels, values, G_N_ELEMENTS(labels),
2711 sp_stb_magnitude_value_changed,
2712 1.0, 0 );
2713 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2714 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2715 }
2717 /* Spoke ratio */
2718 {
2719 gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2720 gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2721 eact = create_adjustment_action( "SpokeAction",
2722 _("Spoke ratio"), _("Spoke ratio:"),
2723 // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2724 // Base radius is the same for the closest handle.
2725 _("Base radius to tip radius ratio"),
2726 "/tools/shapes/star/proportion", 0.5,
2727 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2728 0.01, 1.0, 0.01, 0.1,
2729 labels, values, G_N_ELEMENTS(labels),
2730 sp_stb_proportion_value_changed );
2731 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2732 g_object_set_data( holder, "prop_action", eact );
2733 }
2735 if ( !isFlatSided ) {
2736 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2737 } else {
2738 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2739 }
2741 /* Roundedness */
2742 {
2743 gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2744 gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2745 eact = create_adjustment_action( "RoundednessAction",
2746 _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2747 "/tools/shapes/star/rounded", 0.0,
2748 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2749 -10.0, 10.0, 0.01, 0.1,
2750 labels, values, G_N_ELEMENTS(labels),
2751 sp_stb_rounded_value_changed );
2752 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2753 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2754 }
2756 /* Randomization */
2757 {
2758 gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2759 gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2760 eact = create_adjustment_action( "RandomizationAction",
2761 _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2762 "/tools/shapes/star/randomized", 0.0,
2763 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2764 -10.0, 10.0, 0.001, 0.01,
2765 labels, values, G_N_ELEMENTS(labels),
2766 sp_stb_randomized_value_changed, 0.1, 3 );
2767 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2768 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2769 }
2770 }
2772 {
2773 /* Reset */
2774 {
2775 GtkAction* act = gtk_action_new( "StarResetAction",
2776 _("Defaults"),
2777 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2778 GTK_STOCK_CLEAR );
2779 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2780 gtk_action_group_add_action( mainActions, act );
2781 gtk_action_set_sensitive( act, TRUE );
2782 }
2783 }
2785 sigc::connection *connection = new sigc::connection(
2786 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2787 );
2788 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2789 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2790 }
2793 //########################
2794 //## Rect ##
2795 //########################
2797 static void sp_rtb_sensitivize( GObject *tbl )
2798 {
2799 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2800 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2801 GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2803 if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2804 gtk_action_set_sensitive( not_rounded, FALSE );
2805 } else {
2806 gtk_action_set_sensitive( not_rounded, TRUE );
2807 }
2808 }
2811 static void
2812 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2813 void (*setter)(SPRect *, gdouble))
2814 {
2815 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2817 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2818 SPUnit const *unit = tracker->getActiveUnit();
2820 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2821 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2822 prefs->setDouble(Glib::ustring("/tools/shapes/rect/") + value_name, sp_units_get_pixels(adj->value, *unit));
2823 }
2825 // quit if run by the attr_changed listener
2826 if (g_object_get_data( tbl, "freeze" )) {
2827 return;
2828 }
2830 // in turn, prevent listener from responding
2831 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2833 bool modmade = false;
2834 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2835 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2836 if (SP_IS_RECT(items->data)) {
2837 if (adj->value != 0) {
2838 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2839 } else {
2840 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2841 }
2842 modmade = true;
2843 }
2844 }
2846 sp_rtb_sensitivize( tbl );
2848 if (modmade) {
2849 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2850 _("Change rectangle"));
2851 }
2853 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2854 }
2856 static void
2857 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2858 {
2859 sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2860 }
2862 static void
2863 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2864 {
2865 sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2866 }
2868 static void
2869 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2870 {
2871 sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2872 }
2874 static void
2875 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2876 {
2877 sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2878 }
2882 static void
2883 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2884 {
2885 GtkAdjustment *adj = 0;
2887 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2888 gtk_adjustment_set_value(adj, 0.0);
2889 // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2890 gtk_adjustment_value_changed(adj);
2892 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2893 gtk_adjustment_set_value(adj, 0.0);
2894 gtk_adjustment_value_changed(adj);
2896 sp_rtb_sensitivize( obj );
2897 }
2899 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2900 gchar const */*old_value*/, gchar const */*new_value*/,
2901 bool /*is_interactive*/, gpointer data)
2902 {
2903 GObject *tbl = G_OBJECT(data);
2905 // quit if run by the _changed callbacks
2906 if (g_object_get_data( tbl, "freeze" )) {
2907 return;
2908 }
2910 // in turn, prevent callbacks from responding
2911 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2913 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2914 SPUnit const *unit = tracker->getActiveUnit();
2916 gpointer item = g_object_get_data( tbl, "item" );
2917 if (item && SP_IS_RECT(item)) {
2918 {
2919 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2920 gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2921 gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2922 }
2924 {
2925 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2926 gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2927 gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2928 }
2930 {
2931 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2932 gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2933 gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2934 }
2936 {
2937 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2938 gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2939 gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2940 }
2941 }
2943 sp_rtb_sensitivize( tbl );
2945 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2946 }
2949 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2950 NULL, /* child_added */
2951 NULL, /* child_removed */
2952 rect_tb_event_attr_changed,
2953 NULL, /* content_changed */
2954 NULL /* order_changed */
2955 };
2957 /**
2958 * \param selection should not be NULL.
2959 */
2960 static void
2961 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2962 {
2963 int n_selected = 0;
2964 Inkscape::XML::Node *repr = NULL;
2965 SPItem *item = NULL;
2967 if ( g_object_get_data( tbl, "repr" ) ) {
2968 g_object_set_data( tbl, "item", NULL );
2969 }
2970 purge_repr_listener( tbl, tbl );
2972 for (GSList const *items = selection->itemList();
2973 items != NULL;
2974 items = items->next) {
2975 if (SP_IS_RECT((SPItem *) items->data)) {
2976 n_selected++;
2977 item = (SPItem *) items->data;
2978 repr = SP_OBJECT_REPR(item);
2979 }
2980 }
2982 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2984 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2986 if (n_selected == 0) {
2987 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2989 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2990 gtk_action_set_sensitive(w, FALSE);
2991 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2992 gtk_action_set_sensitive(h, FALSE);
2994 } else if (n_selected == 1) {
2995 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2996 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2998 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2999 gtk_action_set_sensitive(w, TRUE);
3000 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3001 gtk_action_set_sensitive(h, TRUE);
3003 if (repr) {
3004 g_object_set_data( tbl, "repr", repr );
3005 g_object_set_data( tbl, "item", item );
3006 Inkscape::GC::anchor(repr);
3007 sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
3008 sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
3009 }
3010 } else {
3011 // FIXME: implement averaging of all parameters for multiple selected
3012 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3013 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3014 sp_rtb_sensitivize( tbl );
3015 }
3016 }
3019 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3020 {
3021 EgeAdjustmentAction* eact = 0;
3022 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3024 {
3025 EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
3026 ege_output_action_set_use_markup( act, TRUE );
3027 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3028 g_object_set_data( holder, "mode_action", act );
3029 }
3031 // rx/ry units menu: create
3032 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
3033 //tracker->addUnit( SP_UNIT_PERCENT, 0 );
3034 // fixme: add % meaning per cent of the width/height
3035 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
3036 g_object_set_data( holder, "tracker", tracker );
3038 /* W */
3039 {
3040 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3041 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3042 eact = create_adjustment_action( "RectWidthAction",
3043 _("Width"), _("W:"), _("Width of rectangle"),
3044 "/tools/shapes/rect/width", 0,
3045 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
3046 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3047 labels, values, G_N_ELEMENTS(labels),
3048 sp_rtb_width_value_changed );
3049 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3050 g_object_set_data( holder, "width_action", eact );
3051 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3052 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3053 }
3055 /* H */
3056 {
3057 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3058 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3059 eact = create_adjustment_action( "RectHeightAction",
3060 _("Height"), _("H:"), _("Height of rectangle"),
3061 "/tools/shapes/rect/height", 0,
3062 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3063 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3064 labels, values, G_N_ELEMENTS(labels),
3065 sp_rtb_height_value_changed );
3066 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3067 g_object_set_data( holder, "height_action", eact );
3068 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3069 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3070 }
3072 /* rx */
3073 {
3074 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3075 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3076 eact = create_adjustment_action( "RadiusXAction",
3077 _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
3078 "/tools/shapes/rect/rx", 0,
3079 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3080 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3081 labels, values, G_N_ELEMENTS(labels),
3082 sp_rtb_rx_value_changed);
3083 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3084 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3085 }
3087 /* ry */
3088 {
3089 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3090 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3091 eact = create_adjustment_action( "RadiusYAction",
3092 _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
3093 "/tools/shapes/rect/ry", 0,
3094 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3095 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3096 labels, values, G_N_ELEMENTS(labels),
3097 sp_rtb_ry_value_changed);
3098 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3099 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3100 }
3102 // add the units menu
3103 {
3104 GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
3105 gtk_action_group_add_action( mainActions, act );
3106 }
3108 /* Reset */
3109 {
3110 InkAction* inky = ink_action_new( "RectResetAction",
3111 _("Not rounded"),
3112 _("Make corners sharp"),
3113 INKSCAPE_ICON_RECTANGLE_MAKE_CORNERS_SHARP,
3114 secondarySize );
3115 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
3116 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3117 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3118 g_object_set_data( holder, "not_rounded", inky );
3119 }
3121 g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
3122 sp_rtb_sensitivize( holder );
3124 sigc::connection *connection = new sigc::connection(
3125 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
3126 );
3127 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3128 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3129 }
3131 //########################
3132 //## 3D Box ##
3133 //########################
3135 // normalize angle so that it lies in the interval [0,360]
3136 static double box3d_normalize_angle (double a) {
3137 double angle = a + ((int) (a/360.0))*360;
3138 if (angle < 0) {
3139 angle += 360.0;
3140 }
3141 return angle;
3142 }
3144 static void
3145 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
3146 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
3147 // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
3148 // have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
3149 // are reset).
3150 bool is_infinite = !persp3d_VP_is_finite(persp, axis);
3152 if (is_infinite) {
3153 gtk_toggle_action_set_active(tact, TRUE);
3154 gtk_action_set_sensitive(act, TRUE);
3156 double angle = persp3d_get_infinite_angle(persp, axis);
3157 if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
3158 gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
3159 }
3160 } else {
3161 gtk_toggle_action_set_active(tact, FALSE);
3162 gtk_action_set_sensitive(act, FALSE);
3163 }
3164 }
3166 static void
3167 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
3168 if (!persp_repr) {
3169 g_print ("No perspective given to box3d_resync_toolbar().\n");
3170 return;
3171 }
3173 GtkWidget *tbl = GTK_WIDGET(data);
3174 GtkAdjustment *adj = 0;
3175 GtkAction *act = 0;
3176 GtkToggleAction *tact = 0;
3177 Persp3D *persp = persp3d_get_from_repr(persp_repr);
3178 {
3179 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
3180 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
3181 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
3183 box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
3184 }
3185 {
3186 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
3187 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
3188 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
3190 box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
3191 }
3192 {
3193 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
3194 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
3195 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
3197 box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
3198 }
3199 }
3201 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3202 gchar const */*old_value*/, gchar const */*new_value*/,
3203 bool /*is_interactive*/, gpointer data)
3204 {
3205 GtkWidget *tbl = GTK_WIDGET(data);
3207 // quit if run by the attr_changed or selection changed listener
3208 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3209 return;
3210 }
3212 // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
3213 // sp_document_maybe_done() when the document is undo insensitive)
3214 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3216 // TODO: Only update the appropriate part of the toolbar
3217 // if (!strcmp(name, "inkscape:vp_z")) {
3218 box3d_resync_toolbar(repr, G_OBJECT(tbl));
3219 // }
3221 Persp3D *persp = persp3d_get_from_repr(repr);
3222 persp3d_update_box_reprs(persp);
3224 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3225 }
3227 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
3228 {
3229 NULL, /* child_added */
3230 NULL, /* child_removed */
3231 box3d_persp_tb_event_attr_changed,
3232 NULL, /* content_changed */
3233 NULL /* order_changed */
3234 };
3236 /**
3237 * \param selection Should not be NULL.
3238 */
3239 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
3240 // Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
3241 static void
3242 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3243 {
3244 // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
3245 // disable the angle entry fields for this direction (otherwise entering a value in them should only
3246 // update the perspectives with infinite VPs and leave the other ones untouched).
3248 Inkscape::XML::Node *persp_repr = NULL;
3249 purge_repr_listener(tbl, tbl);
3251 SPItem *item = selection->singleItem();
3252 if (item && SP_IS_BOX3D(item)) {
3253 // FIXME: Also deal with multiple selected boxes
3254 SPBox3D *box = SP_BOX3D(item);
3255 Persp3D *persp = box3d_get_perspective(box);
3256 persp_repr = SP_OBJECT_REPR(persp);
3257 if (persp_repr) {
3258 g_object_set_data(tbl, "repr", persp_repr);
3259 Inkscape::GC::anchor(persp_repr);
3260 sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
3261 sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
3262 }
3264 inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
3265 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3266 prefs->setString("/tools/shapes/3dbox/persp", persp_repr->attribute("id"));
3268 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
3269 box3d_resync_toolbar(persp_repr, tbl);
3270 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
3271 }
3272 }
3274 static void
3275 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
3276 {
3277 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3278 SPDocument *document = sp_desktop_document(desktop);
3280 // quit if run by the attr_changed or selection changed listener
3281 if (g_object_get_data( dataKludge, "freeze" )) {
3282 return;
3283 }
3285 // in turn, prevent listener from responding
3286 g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
3288 //Persp3D *persp = document->current_persp3d;
3289 std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
3290 if (sel_persps.empty()) {
3291 // this can happen when the document is created; we silently ignore it
3292 return;
3293 }
3294 Persp3D *persp = sel_persps.front();
3296 persp->tmat.set_infinite_direction (axis, adj->value);
3297 SP_OBJECT(persp)->updateRepr();
3299 // TODO: use the correct axis here, too
3300 sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
3302 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
3303 }
3306 static void
3307 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3308 {
3309 box3d_angle_value_changed(adj, dataKludge, Proj::X);
3310 }
3312 static void
3313 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3314 {
3315 box3d_angle_value_changed(adj, dataKludge, Proj::Y);
3316 }
3318 static void
3319 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3320 {
3321 box3d_angle_value_changed(adj, dataKludge, Proj::Z);
3322 }
3325 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
3326 {
3327 // TODO: Take all selected perspectives into account
3328 std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
3329 if (sel_persps.empty()) {
3330 // this can happen when the document is created; we silently ignore it
3331 return;
3332 }
3333 Persp3D *persp = sel_persps.front();
3335 bool set_infinite = gtk_toggle_action_get_active(act);
3336 persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
3337 }
3339 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3340 {
3341 box3d_vp_state_changed(act, box3d_angle, Proj::X);
3342 }
3344 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3345 {
3346 box3d_vp_state_changed(act, box3d_angle, Proj::Y);
3347 }
3349 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3350 {
3351 box3d_vp_state_changed(act, box3d_angle, Proj::Z);
3352 }
3354 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3355 {
3356 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3357 EgeAdjustmentAction* eact = 0;
3358 SPDocument *document = sp_desktop_document (desktop);
3359 Persp3D *persp = document->current_persp3d;
3361 EgeAdjustmentAction* box3d_angle_x = 0;
3362 EgeAdjustmentAction* box3d_angle_y = 0;
3363 EgeAdjustmentAction* box3d_angle_z = 0;
3365 /* Angle X */
3366 {
3367 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3368 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3369 eact = create_adjustment_action( "3DBoxAngleXAction",
3370 _("Angle in X direction"), _("Angle X:"),
3371 // Translators: PL is short for 'perspective line'
3372 _("Angle of PLs in X direction"),
3373 "/tools/shapes/3dbox/box3d_angle_x", 30,
3374 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
3375 -360.0, 360.0, 1.0, 10.0,
3376 labels, values, G_N_ELEMENTS(labels),
3377 box3d_angle_x_value_changed );
3378 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3379 g_object_set_data( holder, "box3d_angle_x_action", eact );
3380 box3d_angle_x = eact;
3381 }
3383 if (!persp3d_VP_is_finite(persp, Proj::X)) {
3384 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3385 } else {
3386 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3387 }
3390 /* VP X state */
3391 {
3392 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
3393 // Translators: VP is short for 'vanishing point'
3394 _("State of VP in X direction"),
3395 _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
3396 INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3397 Inkscape::ICON_SIZE_DECORATION );
3398 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3399 g_object_set_data( holder, "box3d_vp_x_state_action", act );
3400 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
3401 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3402 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3403 }
3405 /* Angle Y */
3406 {
3407 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3408 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3409 eact = create_adjustment_action( "3DBoxAngleYAction",
3410 _("Angle in Y direction"), _("Angle Y:"),
3411 // Translators: PL is short for 'perspective line'
3412 _("Angle of PLs in Y direction"),
3413 "/tools/shapes/3dbox/box3d_angle_y", 30,
3414 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3415 -360.0, 360.0, 1.0, 10.0,
3416 labels, values, G_N_ELEMENTS(labels),
3417 box3d_angle_y_value_changed );
3418 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3419 g_object_set_data( holder, "box3d_angle_y_action", eact );
3420 box3d_angle_y = eact;
3421 }
3423 if (!persp3d_VP_is_finite(persp, Proj::Y)) {
3424 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3425 } else {
3426 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3427 }
3429 /* VP Y state */
3430 {
3431 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
3432 // Translators: VP is short for 'vanishing point'
3433 _("State of VP in Y direction"),
3434 _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
3435 INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3436 Inkscape::ICON_SIZE_DECORATION );
3437 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3438 g_object_set_data( holder, "box3d_vp_y_state_action", act );
3439 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
3440 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3441 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3442 }
3444 /* Angle Z */
3445 {
3446 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3447 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3448 eact = create_adjustment_action( "3DBoxAngleZAction",
3449 _("Angle in Z direction"), _("Angle Z:"),
3450 // Translators: PL is short for 'perspective line'
3451 _("Angle of PLs in Z direction"),
3452 "/tools/shapes/3dbox/box3d_angle_z", 30,
3453 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3454 -360.0, 360.0, 1.0, 10.0,
3455 labels, values, G_N_ELEMENTS(labels),
3456 box3d_angle_z_value_changed );
3457 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3458 g_object_set_data( holder, "box3d_angle_z_action", eact );
3459 box3d_angle_z = eact;
3460 }
3462 if (!persp3d_VP_is_finite(persp, Proj::Z)) {
3463 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3464 } else {
3465 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3466 }
3468 /* VP Z state */
3469 {
3470 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3471 // Translators: VP is short for 'vanishing point'
3472 _("State of VP in Z direction"),
3473 _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3474 INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3475 Inkscape::ICON_SIZE_DECORATION );
3476 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3477 g_object_set_data( holder, "box3d_vp_z_state_action", act );
3478 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3479 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3480 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3481 }
3483 sigc::connection *connection = new sigc::connection(
3484 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3485 );
3486 g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3487 g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3488 }
3490 //########################
3491 //## Spiral ##
3492 //########################
3494 static void
3495 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, Glib::ustring const &value_name)
3496 {
3497 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3499 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3500 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3501 prefs->setDouble("/tools/shapes/spiral/" + value_name, adj->value);
3502 }
3504 // quit if run by the attr_changed listener
3505 if (g_object_get_data( tbl, "freeze" )) {
3506 return;
3507 }
3509 // in turn, prevent listener from responding
3510 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3512 gchar* namespaced_name = g_strconcat("sodipodi:", value_name.data(), NULL);
3514 bool modmade = false;
3515 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3516 items != NULL;
3517 items = items->next)
3518 {
3519 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3520 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3521 sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3522 SP_OBJECT((SPItem *) items->data)->updateRepr();
3523 modmade = true;
3524 }
3525 }
3527 g_free(namespaced_name);
3529 if (modmade) {
3530 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3531 _("Change spiral"));
3532 }
3534 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3535 }
3537 static void
3538 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3539 {
3540 sp_spl_tb_value_changed(adj, tbl, "revolution");
3541 }
3543 static void
3544 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3545 {
3546 sp_spl_tb_value_changed(adj, tbl, "expansion");
3547 }
3549 static void
3550 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3551 {
3552 sp_spl_tb_value_changed(adj, tbl, "t0");
3553 }
3555 static void
3556 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3557 {
3558 GtkWidget *tbl = GTK_WIDGET(obj);
3560 GtkAdjustment *adj;
3562 // fixme: make settable
3563 gdouble rev = 5;
3564 gdouble exp = 1.0;
3565 gdouble t0 = 0.0;
3567 adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3568 gtk_adjustment_set_value(adj, rev);
3569 gtk_adjustment_value_changed(adj);
3571 adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3572 gtk_adjustment_set_value(adj, exp);
3573 gtk_adjustment_value_changed(adj);
3575 adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3576 gtk_adjustment_set_value(adj, t0);
3577 gtk_adjustment_value_changed(adj);
3579 spinbutton_defocus(GTK_OBJECT(tbl));
3580 }
3583 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3584 gchar const */*old_value*/, gchar const */*new_value*/,
3585 bool /*is_interactive*/, gpointer data)
3586 {
3587 GtkWidget *tbl = GTK_WIDGET(data);
3589 // quit if run by the _changed callbacks
3590 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3591 return;
3592 }
3594 // in turn, prevent callbacks from responding
3595 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3597 GtkAdjustment *adj;
3598 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3599 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3601 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3602 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3604 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3605 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3607 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3608 }
3611 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3612 NULL, /* child_added */
3613 NULL, /* child_removed */
3614 spiral_tb_event_attr_changed,
3615 NULL, /* content_changed */
3616 NULL /* order_changed */
3617 };
3619 static void
3620 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3621 {
3622 int n_selected = 0;
3623 Inkscape::XML::Node *repr = NULL;
3625 purge_repr_listener( tbl, tbl );
3627 for (GSList const *items = selection->itemList();
3628 items != NULL;
3629 items = items->next)
3630 {
3631 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3632 n_selected++;
3633 repr = SP_OBJECT_REPR((SPItem *) items->data);
3634 }
3635 }
3637 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3639 if (n_selected == 0) {
3640 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3641 } else if (n_selected == 1) {
3642 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3644 if (repr) {
3645 g_object_set_data( tbl, "repr", repr );
3646 Inkscape::GC::anchor(repr);
3647 sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3648 sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3649 }
3650 } else {
3651 // FIXME: implement averaging of all parameters for multiple selected
3652 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3653 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3654 }
3655 }
3658 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3659 {
3660 EgeAdjustmentAction* eact = 0;
3661 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3663 {
3664 EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3665 ege_output_action_set_use_markup( act, TRUE );
3666 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3667 g_object_set_data( holder, "mode_action", act );
3668 }
3670 /* Revolution */
3671 {
3672 gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3673 gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3674 eact = create_adjustment_action( "SpiralRevolutionAction",
3675 _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3676 "/tools/shapes/spiral/revolution", 3.0,
3677 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3678 0.01, 1024.0, 0.1, 1.0,
3679 labels, values, G_N_ELEMENTS(labels),
3680 sp_spl_tb_revolution_value_changed, 1, 2);
3681 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3682 }
3684 /* Expansion */
3685 {
3686 gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3687 gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3688 eact = create_adjustment_action( "SpiralExpansionAction",
3689 _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3690 "/tools/shapes/spiral/expansion", 1.0,
3691 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3692 0.0, 1000.0, 0.01, 1.0,
3693 labels, values, G_N_ELEMENTS(labels),
3694 sp_spl_tb_expansion_value_changed);
3695 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3696 }
3698 /* T0 */
3699 {
3700 gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3701 gdouble values[] = {0, 0.5, 0.9};
3702 eact = create_adjustment_action( "SpiralT0Action",
3703 _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3704 "/tools/shapes/spiral/t0", 0.0,
3705 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3706 0.0, 0.999, 0.01, 1.0,
3707 labels, values, G_N_ELEMENTS(labels),
3708 sp_spl_tb_t0_value_changed);
3709 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3710 }
3712 /* Reset */
3713 {
3714 InkAction* inky = ink_action_new( "SpiralResetAction",
3715 _("Defaults"),
3716 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3717 GTK_STOCK_CLEAR,
3718 secondarySize );
3719 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3720 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3721 }
3724 sigc::connection *connection = new sigc::connection(
3725 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3726 );
3727 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3728 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3729 }
3731 //########################
3732 //## Pen/Pencil ##
3733 //########################
3735 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3736 static Glib::ustring const
3737 freehand_tool_name(GObject *dataKludge)
3738 {
3739 SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3740 return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3741 ? "/tools/freehand/pen"
3742 : "/tools/freehand/pencil" );
3743 }
3745 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3746 {
3747 gint mode = ege_select_one_action_get_active(act);
3749 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3750 prefs->setInt(freehand_tool_name(tbl) + "/freehand-mode", mode);
3752 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3754 // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3755 // preparatory work here
3756 if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3757 SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3758 sp_pen_context_set_polyline_mode(pc);
3759 }
3760 }
3762 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3763 {
3764 /* Freehand mode toggle buttons */
3765 {
3766 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3767 guint freehandMode = prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/freehand-mode" : "/tools/freehand/pen/freehand-mode" ), 0);
3768 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3770 {
3771 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3773 GtkTreeIter iter;
3774 gtk_list_store_append( model, &iter );
3775 gtk_list_store_set( model, &iter,
3776 0, _("Bezier"),
3777 1, _("Create regular Bezier path"),
3778 2, INKSCAPE_ICON_PATH_MODE_BEZIER,
3779 -1 );
3781 gtk_list_store_append( model, &iter );
3782 gtk_list_store_set( model, &iter,
3783 0, _("Spiro"),
3784 1, _("Create Spiro path"),
3785 2, INKSCAPE_ICON_PATH_MODE_SPIRO,
3786 -1 );
3788 if (!tool_is_pencil) {
3789 gtk_list_store_append( model, &iter );
3790 gtk_list_store_set( model, &iter,
3791 0, _("Zigzag"),
3792 1, _("Create a sequence of straight line segments"),
3793 2, INKSCAPE_ICON_PATH_MODE_POLYLINE,
3794 -1 );
3796 gtk_list_store_append( model, &iter );
3797 gtk_list_store_set( model, &iter,
3798 0, _("Paraxial"),
3799 1, _("Create a sequence of paraxial line segments"),
3800 2, INKSCAPE_ICON_PATH_MODE_POLYLINE_PARAXIAL,
3801 -1 );
3802 }
3804 EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3805 "FreehandModeActionPencil" :
3806 "FreehandModeActionPen",
3807 (_("Mode:")), (_("Mode of new lines drawn by this tool")), NULL, GTK_TREE_MODEL(model) );
3808 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3810 ege_select_one_action_set_appearance( act, "full" );
3811 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3812 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3813 ege_select_one_action_set_icon_column( act, 2 );
3814 ege_select_one_action_set_icon_size( act, secondarySize );
3815 ege_select_one_action_set_tooltip_column( act, 1 );
3817 ege_select_one_action_set_active( act, freehandMode);
3818 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3819 }
3820 }
3821 }
3823 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3824 gint shape = ege_select_one_action_get_active( act );
3825 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3826 prefs->setInt(freehand_tool_name(dataKludge) + "/shape", shape);
3827 }
3829 /**
3830 * \brief Generate the list of freehand advanced shape option entries.
3831 */
3832 GList * freehand_shape_dropdown_items_list() {
3833 GList *glist = NULL;
3835 glist = g_list_append (glist, _("None"));
3836 glist = g_list_append (glist, _("Triangle in"));
3837 glist = g_list_append (glist, _("Triangle out"));
3838 glist = g_list_append (glist, _("Ellipse"));
3839 glist = g_list_append (glist, _("From clipboard"));
3841 return glist;
3842 }
3844 static void
3845 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3846 /*advanced shape options */
3847 {
3848 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3849 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3851 GList* items = 0;
3852 gint count = 0;
3853 for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3854 {
3855 GtkTreeIter iter;
3856 gtk_list_store_append( model, &iter );
3857 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3858 count++;
3859 }
3860 g_list_free( items );
3861 items = 0;
3862 EgeSelectOneAction* act1 = ege_select_one_action_new(
3863 tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3864 _("Shape:"), (_("Shape of new paths drawn by this tool")), NULL, GTK_TREE_MODEL(model));
3865 g_object_set( act1, "short_label", _("Shape:"), NULL );
3866 ege_select_one_action_set_appearance( act1, "compact" );
3867 ege_select_one_action_set_active( act1, prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/shape" : "/tools/freehand/pen/shape" ), 0) );
3868 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3869 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3870 g_object_set_data( holder, "shape_action", act1 );
3871 }
3872 }
3874 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3875 {
3876 sp_add_freehand_mode_toggle(mainActions, holder, false);
3877 freehand_add_advanced_shape_options(mainActions, holder, false);
3878 }
3881 static void
3882 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3883 {
3884 GtkWidget *tbl = GTK_WIDGET(obj);
3886 GtkAdjustment *adj;
3888 // fixme: make settable
3889 gdouble tolerance = 4;
3891 adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3892 gtk_adjustment_set_value(adj, tolerance);
3893 gtk_adjustment_value_changed(adj);
3895 spinbutton_defocus(GTK_OBJECT(tbl));
3896 }
3898 static void
3899 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3900 {
3901 // quit if run by the attr_changed listener
3902 if (g_object_get_data( tbl, "freeze" )) {
3903 return;
3904 }
3905 // in turn, prevent listener from responding
3906 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3907 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3908 prefs->setDouble("/tools/freehand/pencil/tolerance", adj->value);
3909 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3910 }
3912 class PencilToleranceObserver : public Inkscape::Preferences::Observer {
3913 public:
3914 PencilToleranceObserver(Glib::ustring const &path, GObject *x) : Observer(path), _obj(x)
3915 {
3916 g_object_set_data(_obj, "prefobserver", this);
3917 }
3918 virtual ~PencilToleranceObserver() {
3919 if (g_object_get_data(_obj, "prefobserver") == this) {
3920 g_object_set_data(_obj, "prefobserver", NULL);
3921 }
3922 }
3923 virtual void notify(Inkscape::Preferences::Entry const &val) {
3924 GObject* tbl = _obj;
3925 if (g_object_get_data( tbl, "freeze" )) {
3926 return;
3927 }
3928 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3930 GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl, "tolerance");
3932 double v = val.getDouble(adj->value);
3933 gtk_adjustment_set_value(adj, v);
3934 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3935 }
3936 private:
3937 GObject *_obj;
3938 };
3941 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3942 {
3943 sp_add_freehand_mode_toggle(mainActions, holder, true);
3945 EgeAdjustmentAction* eact = 0;
3947 /* Tolerance */
3948 {
3949 gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
3950 gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
3951 eact = create_adjustment_action( "PencilToleranceAction",
3952 _("Smoothing:"), _("Smoothing: "),
3953 _("How much smoothing (simplifying) is applied to the line"),
3954 "/tools/freehand/pencil/tolerance",
3955 3.0,
3956 GTK_WIDGET(desktop->canvas), NULL,
3957 holder, TRUE, "altx-pencil",
3958 1, 100.0, 0.5, 1.0,
3959 labels, values, G_N_ELEMENTS(labels),
3960 sp_pencil_tb_tolerance_value_changed,
3961 1, 2);
3962 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3963 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3965 PencilToleranceObserver *obs =
3966 new PencilToleranceObserver("/tools/freehand/pencil/tolerance", G_OBJECT(holder));
3967 }
3969 /* advanced shape options */
3970 freehand_add_advanced_shape_options(mainActions, holder, true);
3972 /* Reset */
3973 {
3974 InkAction* inky = ink_action_new( "PencilResetAction",
3975 _("Defaults"),
3976 _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3977 GTK_STOCK_CLEAR,
3978 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3979 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
3980 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3981 }
3983 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3985 }
3988 //########################
3989 //## Tweak ##
3990 //########################
3992 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3993 {
3994 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3995 prefs->setDouble( "/tools/tweak/width", adj->value * 0.01 );
3996 }
3998 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3999 {
4000 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4001 prefs->setDouble( "/tools/tweak/force", adj->value * 0.01 );
4002 }
4004 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4005 {
4006 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4007 prefs->setBool("/tools/tweak/usepressure", gtk_toggle_action_get_active(act));
4008 }
4010 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4011 {
4012 int mode = ege_select_one_action_get_active( act );
4013 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4014 prefs->setInt("/tools/tweak/mode", mode);
4016 GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
4017 GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
4018 GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
4019 GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
4020 GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
4021 GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
4022 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
4023 if (doh) gtk_action_set_sensitive (doh, TRUE);
4024 if (dos) gtk_action_set_sensitive (dos, TRUE);
4025 if (dol) gtk_action_set_sensitive (dol, TRUE);
4026 if (doo) gtk_action_set_sensitive (doo, TRUE);
4027 if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
4028 if (fid) gtk_action_set_sensitive (fid, FALSE);
4029 } else {
4030 if (doh) gtk_action_set_sensitive (doh, FALSE);
4031 if (dos) gtk_action_set_sensitive (dos, FALSE);
4032 if (dol) gtk_action_set_sensitive (dol, FALSE);
4033 if (doo) gtk_action_set_sensitive (doo, FALSE);
4034 if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
4035 if (fid) gtk_action_set_sensitive (fid, TRUE);
4036 }
4037 }
4039 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4040 {
4041 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4042 prefs->setDouble( "/tools/tweak/fidelity", adj->value * 0.01 );
4043 }
4045 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
4046 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4047 prefs->setBool("/tools/tweak/doh", gtk_toggle_action_get_active(act));
4048 }
4049 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
4050 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4051 prefs->setBool("/tools/tweak/dos", gtk_toggle_action_get_active(act));
4052 }
4053 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
4054 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4055 prefs->setBool("/tools/tweak/dol", gtk_toggle_action_get_active(act));
4056 }
4057 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
4058 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4059 prefs->setBool("/tools/tweak/doo", gtk_toggle_action_get_active(act));
4060 }
4062 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4063 {
4064 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
4065 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4067 {
4068 /* Width */
4069 gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
4070 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4071 EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
4072 _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
4073 "/tools/tweak/width", 15,
4074 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
4075 1, 100, 1.0, 10.0,
4076 labels, values, G_N_ELEMENTS(labels),
4077 sp_tweak_width_value_changed, 0.01, 0, 100 );
4078 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4079 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4080 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4081 }
4084 {
4085 /* Force */
4086 gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
4087 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4088 EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
4089 _("Force"), _("Force:"), _("The force of the tweak action"),
4090 "/tools/tweak/force", 20,
4091 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
4092 1, 100, 1.0, 10.0,
4093 labels, values, G_N_ELEMENTS(labels),
4094 sp_tweak_force_value_changed, 0.01, 0, 100 );
4095 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4096 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4097 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4098 }
4100 /* Mode */
4101 {
4102 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4104 GtkTreeIter iter;
4105 gtk_list_store_append( model, &iter );
4106 gtk_list_store_set( model, &iter,
4107 0, _("Move mode"),
4108 1, _("Move objects in any direction"),
4109 2, INKSCAPE_ICON_OBJECT_TWEAK_PUSH,
4110 -1 );
4112 gtk_list_store_append( model, &iter );
4113 gtk_list_store_set( model, &iter,
4114 0, _("Move in/out mode"),
4115 1, _("Move objects towards cursor; with Shift from cursor"),
4116 2, INKSCAPE_ICON_OBJECT_TWEAK_ATTRACT,
4117 -1 );
4119 gtk_list_store_append( model, &iter );
4120 gtk_list_store_set( model, &iter,
4121 0, _("Move jitter mode"),
4122 1, _("Move objects in random directions"),
4123 2, INKSCAPE_ICON_OBJECT_TWEAK_RANDOMIZE,
4124 -1 );
4126 gtk_list_store_append( model, &iter );
4127 gtk_list_store_set( model, &iter,
4128 0, _("Scale mode"),
4129 1, _("Shrink objects, with Shift enlarge"),
4130 2, INKSCAPE_ICON_OBJECT_TWEAK_SHRINK,
4131 -1 );
4133 gtk_list_store_append( model, &iter );
4134 gtk_list_store_set( model, &iter,
4135 0, _("Rotate mode"),
4136 1, _("Rotate objects, with Shift counterclockwise"),
4137 2, INKSCAPE_ICON_OBJECT_TWEAK_ROTATE,
4138 -1 );
4140 gtk_list_store_append( model, &iter );
4141 gtk_list_store_set( model, &iter,
4142 0, _("Duplicate/delete mode"),
4143 1, _("Duplicate objects, with Shift delete"),
4144 2, INKSCAPE_ICON_OBJECT_TWEAK_DUPLICATE,
4145 -1 );
4147 gtk_list_store_append( model, &iter );
4148 gtk_list_store_set( model, &iter,
4149 0, _("Push mode"),
4150 1, _("Push parts of paths in any direction"),
4151 2, INKSCAPE_ICON_PATH_TWEAK_PUSH,
4152 -1 );
4154 gtk_list_store_append( model, &iter );
4155 gtk_list_store_set( model, &iter,
4156 0, _("Shrink/grow mode"),
4157 1, _("Shrink (inset) parts of paths; with Shift grow (outset)"),
4158 2, INKSCAPE_ICON_PATH_TWEAK_SHRINK,
4159 -1 );
4161 gtk_list_store_append( model, &iter );
4162 gtk_list_store_set( model, &iter,
4163 0, _("Attract/repel mode"),
4164 1, _("Attract parts of paths towards cursor; with Shift from cursor"),
4165 2, INKSCAPE_ICON_PATH_TWEAK_ATTRACT,
4166 -1 );
4168 gtk_list_store_append( model, &iter );
4169 gtk_list_store_set( model, &iter,
4170 0, _("Roughen mode"),
4171 1, _("Roughen parts of paths"),
4172 2, INKSCAPE_ICON_PATH_TWEAK_ROUGHEN,
4173 -1 );
4175 gtk_list_store_append( model, &iter );
4176 gtk_list_store_set( model, &iter,
4177 0, _("Color paint mode"),
4178 1, _("Paint the tool's color upon selected objects"),
4179 2, INKSCAPE_ICON_OBJECT_TWEAK_PAINT,
4180 -1 );
4182 gtk_list_store_append( model, &iter );
4183 gtk_list_store_set( model, &iter,
4184 0, _("Color jitter mode"),
4185 1, _("Jitter the colors of selected objects"),
4186 2, INKSCAPE_ICON_OBJECT_TWEAK_JITTER_COLOR,
4187 -1 );
4189 gtk_list_store_append( model, &iter );
4190 gtk_list_store_set( model, &iter,
4191 0, _("Blur mode"),
4192 1, _("Blur selected objects more; with Shift, blur less"),
4193 2, INKSCAPE_ICON_OBJECT_TWEAK_BLUR,
4194 -1 );
4197 EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4198 g_object_set( act, "short_label", _("Mode:"), NULL );
4199 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4200 g_object_set_data( holder, "mode_action", act );
4202 ege_select_one_action_set_appearance( act, "full" );
4203 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4204 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4205 ege_select_one_action_set_icon_column( act, 2 );
4206 ege_select_one_action_set_icon_size( act, secondarySize );
4207 ege_select_one_action_set_tooltip_column( act, 1 );
4209 gint mode = prefs->getInt("/tools/tweak/mode", 0);
4210 ege_select_one_action_set_active( act, mode );
4211 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
4213 g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
4214 }
4216 guint mode = prefs->getInt("/tools/tweak/mode", 0);
4218 {
4219 EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
4220 ege_output_action_set_use_markup( act, TRUE );
4221 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4222 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4223 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4224 g_object_set_data( holder, "tweak_channels_label", act);
4225 }
4227 {
4228 InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
4229 _("Hue"),
4230 _("In color mode, act on objects' hue"),
4231 NULL,
4232 Inkscape::ICON_SIZE_DECORATION );
4233 //TRANSLATORS: "H" here stands for hue
4234 g_object_set( act, "short_label", _("H"), NULL );
4235 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4236 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
4237 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doh", true) );
4238 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4239 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4240 g_object_set_data( holder, "tweak_doh", act);
4241 }
4242 {
4243 InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
4244 _("Saturation"),
4245 _("In color mode, act on objects' saturation"),
4246 NULL,
4247 Inkscape::ICON_SIZE_DECORATION );
4248 //TRANSLATORS: "S" here stands for Saturation
4249 g_object_set( act, "short_label", _("S"), NULL );
4250 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4251 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
4252 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dos", true) );
4253 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4254 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4255 g_object_set_data( holder, "tweak_dos", act );
4256 }
4257 {
4258 InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
4259 _("Lightness"),
4260 _("In color mode, act on objects' lightness"),
4261 NULL,
4262 Inkscape::ICON_SIZE_DECORATION );
4263 //TRANSLATORS: "L" here stands for Lightness
4264 g_object_set( act, "short_label", _("L"), NULL );
4265 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4266 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
4267 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dol", true) );
4268 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4269 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4270 g_object_set_data( holder, "tweak_dol", act );
4271 }
4272 {
4273 InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
4274 _("Opacity"),
4275 _("In color mode, act on objects' opacity"),
4276 NULL,
4277 Inkscape::ICON_SIZE_DECORATION );
4278 //TRANSLATORS: "O" here stands for Opacity
4279 g_object_set( act, "short_label", _("O"), NULL );
4280 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4281 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
4282 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doo", true) );
4283 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4284 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4285 g_object_set_data( holder, "tweak_doo", act );
4286 }
4288 { /* Fidelity */
4289 gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
4290 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4291 EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
4292 _("Fidelity"), _("Fidelity:"),
4293 _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
4294 "/tools/tweak/fidelity", 50,
4295 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
4296 1, 100, 1.0, 10.0,
4297 labels, values, G_N_ELEMENTS(labels),
4298 sp_tweak_fidelity_value_changed, 0.01, 0, 100 );
4299 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4300 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4301 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
4302 gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
4303 g_object_set_data( holder, "tweak_fidelity", eact );
4304 }
4307 /* Use Pressure button */
4308 {
4309 InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
4310 _("Pressure"),
4311 _("Use the pressure of the input device to alter the force of tweak action"),
4312 INKSCAPE_ICON_DRAW_USE_PRESSURE,
4313 Inkscape::ICON_SIZE_DECORATION );
4314 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4315 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
4316 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/usepressure", true) );
4317 }
4319 }
4322 //########################
4323 //## Calligraphy ##
4324 //########################
4325 static void update_presets_list (GObject *tbl)
4326 {
4327 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4328 if (g_object_get_data(tbl, "presets_blocked"))
4329 return;
4331 EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4332 if (!sel) {
4333 // WTF!? This will cause a segfault if ever reached
4334 //ege_select_one_action_set_active(sel, 0);
4335 return;
4336 }
4338 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4340 int ege_index = 1;
4341 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++ege_index) {
4342 bool match = true;
4344 std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(*i);
4345 for (std::vector<Inkscape::Preferences::Entry>::iterator j = preset.begin(); j != preset.end(); ++j) {
4346 Glib::ustring entry_name = j->getEntryName();
4347 if (entry_name == "id" || entry_name == "name") continue;
4349 void *widget = g_object_get_data(tbl, entry_name.data());
4350 if (widget) {
4351 if (GTK_IS_ADJUSTMENT(widget)) {
4352 double v = j->getDouble();
4353 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4354 //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
4355 if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
4356 match = false;
4357 break;
4358 }
4359 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4360 bool v = j->getBool();
4361 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4362 //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
4363 if ( static_cast<bool>(gtk_toggle_action_get_active(toggle)) != v ) {
4364 match = false;
4365 break;
4366 }
4367 }
4368 }
4369 }
4371 if (match) {
4372 // newly added item is at the same index as the
4373 // save command, so we need to change twice for it to take effect
4374 ege_select_one_action_set_active(sel, 0);
4375 ege_select_one_action_set_active(sel, ege_index); // one-based index
4376 return;
4377 }
4378 }
4380 // no match found
4381 ege_select_one_action_set_active(sel, 0);
4382 }
4384 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
4385 {
4386 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4387 prefs->setDouble( "/tools/calligraphic/mass", adj->value );
4388 update_presets_list(tbl);
4389 }
4391 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
4392 {
4393 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4394 prefs->setDouble( "/tools/calligraphic/wiggle", adj->value );
4395 update_presets_list(tbl);
4396 }
4398 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
4399 {
4400 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4401 prefs->setDouble( "/tools/calligraphic/angle", adj->value );
4402 update_presets_list(tbl);
4403 }
4405 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
4406 {
4407 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4408 prefs->setDouble( "/tools/calligraphic/width", adj->value );
4409 update_presets_list(tbl);
4410 }
4412 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
4413 {
4414 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4415 prefs->setDouble("/tools/calligraphic/thinning", adj->value );
4416 update_presets_list(tbl);
4417 }
4419 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
4420 {
4421 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4422 prefs->setDouble( "/tools/calligraphic/flatness", adj->value );
4423 update_presets_list(tbl);
4424 }
4426 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
4427 {
4428 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4429 prefs->setDouble( "/tools/calligraphic/tremor", adj->value );
4430 update_presets_list(tbl);
4431 }
4433 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
4434 {
4435 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4436 prefs->setDouble( "/tools/calligraphic/cap_rounding", adj->value );
4437 update_presets_list(tbl);
4438 }
4440 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject* tbl )
4441 {
4442 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4443 prefs->setBool("/tools/calligraphic/usepressure", gtk_toggle_action_get_active( act ));
4444 update_presets_list(tbl);
4445 }
4447 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject* tbl )
4448 {
4449 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4450 prefs->setBool("/tools/calligraphic/tracebackground", gtk_toggle_action_get_active( act ));
4451 update_presets_list(tbl);
4452 }
4454 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject* tbl )
4455 {
4456 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4457 GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
4458 prefs->setBool("/tools/calligraphic/usetilt", gtk_toggle_action_get_active( act ));
4459 update_presets_list(tbl);
4460 if (calligraphy_angle )
4461 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
4462 }
4465 static gchar const *const widget_names[] = {
4466 "width",
4467 "mass",
4468 "wiggle",
4469 "angle",
4470 "thinning",
4471 "tremor",
4472 "flatness",
4473 "cap_rounding",
4474 "usepressure",
4475 "tracebackground",
4476 "usetilt"
4477 };
4480 static void sp_dcc_build_presets_list(GObject *tbl)
4481 {
4482 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4484 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4485 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4486 gtk_list_store_clear (model);
4488 {
4489 GtkTreeIter iter;
4490 gtk_list_store_append( model, &iter );
4491 gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4492 }
4494 // iterate over all presets to populate the list
4495 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4496 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4497 int ii=1;
4499 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i) {
4500 GtkTreeIter iter;
4501 Glib::ustring preset_name = prefs->getString(*i + "/name");
4502 gtk_list_store_append( model, &iter );
4503 gtk_list_store_set( model, &iter, 0, _(preset_name.data()), 1, ii++, -1 );
4504 }
4506 {
4507 GtkTreeIter iter;
4508 gtk_list_store_append( model, &iter );
4509 gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4510 g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4511 }
4513 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4515 update_presets_list (tbl);
4516 }
4518 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4519 {
4520 using Inkscape::UI::Dialog::CalligraphicProfileRename;
4521 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4522 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4523 if (! desktop) return;
4525 if (g_object_get_data(tbl, "presets_blocked"))
4526 return;
4528 CalligraphicProfileRename::show(desktop);
4529 if ( !CalligraphicProfileRename::applied()) {
4530 // dialog cancelled
4531 update_presets_list (tbl);
4532 return;
4533 }
4534 Glib::ustring profile_name = CalligraphicProfileRename::getProfileName();
4536 if (profile_name.empty()) {
4537 // empty name entered
4538 update_presets_list (tbl);
4539 return;
4540 }
4542 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4544 // If there's a preset with the given name, find it and set save_path appropriately
4545 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4546 int total_presets = presets.size();
4547 int new_index = -1;
4548 Glib::ustring save_path; // profile pref path without a trailing slash
4550 int temp_index = 0;
4551 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++temp_index) {
4552 Glib::ustring name = prefs->getString(*i + "/name");
4553 if (!name.empty() && profile_name == name) {
4554 new_index = temp_index;
4555 save_path = *i;
4556 break;
4557 }
4558 }
4560 if (new_index == -1) {
4561 // no preset with this name, create
4562 new_index = total_presets + 1;
4563 gchar *profile_id = g_strdup_printf("/dcc%d", new_index);
4564 save_path = Glib::ustring("/tools/calligraphic/preset") + profile_id;
4565 g_free(profile_id);
4566 }
4568 for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4569 gchar const *const widget_name = widget_names[i];
4570 void *widget = g_object_get_data(tbl, widget_name);
4571 if (widget) {
4572 if (GTK_IS_ADJUSTMENT(widget)) {
4573 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4574 prefs->setDouble(save_path + "/" + widget_name, gtk_adjustment_get_value(adj));
4575 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4576 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4577 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4578 prefs->setBool(save_path + "/" + widget_name, gtk_toggle_action_get_active(toggle));
4579 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4580 } else {
4581 g_warning("Unknown widget type for preset: %s\n", widget_name);
4582 }
4583 } else {
4584 g_warning("Bad key when writing preset: %s\n", widget_name);
4585 }
4586 }
4587 prefs->setString(save_path + "/name", profile_name);
4589 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4590 sp_dcc_build_presets_list (tbl);
4591 }
4594 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4596 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4598 gint preset_index = ege_select_one_action_get_active( act );
4599 // This is necessary because EgeSelectOneAction spams us with GObject "changed" signal calls
4600 // even when the preset is not changed. It would be good to replace it with something more
4601 // modern. Index 0 means "No preset", so we don't do anything.
4602 if (preset_index == 0) return;
4604 gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4606 if (preset_index == save_presets_index) {
4607 // this is the Save command
4608 sp_dcc_save_profile(NULL, tbl);
4609 return;
4610 }
4612 if (g_object_get_data(tbl, "presets_blocked"))
4613 return;
4615 // preset_index is one-based so we subtract 1
4616 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4617 Glib::ustring preset_path = presets.at(preset_index - 1);
4619 if (!preset_path.empty()) {
4620 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
4622 std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(preset_path);
4624 // Shouldn't this be std::map?
4625 for (std::vector<Inkscape::Preferences::Entry>::iterator i = preset.begin(); i != preset.end(); ++i) {
4626 Glib::ustring entry_name = i->getEntryName();
4627 if (entry_name == "id" || entry_name == "name") continue;
4628 void *widget = g_object_get_data(tbl, entry_name.data());
4629 if (widget) {
4630 if (GTK_IS_ADJUSTMENT(widget)) {
4631 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4632 gtk_adjustment_set_value(adj, i->getDouble());
4633 //std::cout << "set adj " << attr_name << " to " << v << "\n";
4634 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4635 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4636 gtk_toggle_action_set_active(toggle, i->getBool());
4637 //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4638 } else {
4639 g_warning("Unknown widget type for preset: %s\n", entry_name.data());
4640 }
4641 } else {
4642 g_warning("Bad key found in a preset record: %s\n", entry_name.data());
4643 }
4644 }
4645 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4646 }
4647 }
4650 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4651 {
4652 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4653 {
4654 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4656 EgeAdjustmentAction* calligraphy_angle = 0;
4658 {
4659 /* Width */
4660 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4661 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4662 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4663 _("Pen Width"), _("Width:"),
4664 _("The width of the calligraphic pen (relative to the visible canvas area)"),
4665 "/tools/calligraphic/width", 15,
4666 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4667 1, 100, 1.0, 10.0,
4668 labels, values, G_N_ELEMENTS(labels),
4669 sp_ddc_width_value_changed, 1, 0);
4670 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4671 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4672 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4673 }
4675 {
4676 /* Thinning */
4677 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4678 gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4679 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4680 _("Stroke Thinning"), _("Thinning:"),
4681 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4682 "/tools/calligraphic/thinning", 10,
4683 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4684 -100, 100, 1, 10.0,
4685 labels, values, G_N_ELEMENTS(labels),
4686 sp_ddc_velthin_value_changed, 1, 0);
4687 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4688 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4689 }
4691 {
4692 /* Angle */
4693 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4694 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4695 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4696 _("Pen Angle"), _("Angle:"),
4697 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4698 "/tools/calligraphic/angle", 30,
4699 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4700 -90.0, 90.0, 1.0, 10.0,
4701 labels, values, G_N_ELEMENTS(labels),
4702 sp_ddc_angle_value_changed, 1, 0 );
4703 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4704 g_object_set_data( holder, "angle_action", eact );
4705 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4706 calligraphy_angle = eact;
4707 }
4709 {
4710 /* Fixation */
4711 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4712 gdouble values[] = {0, 20, 40, 60, 90, 100};
4713 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4714 _("Fixation"), _("Fixation:"),
4715 _("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"),
4716 "/tools/calligraphic/flatness", 90,
4717 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4718 0.0, 100, 1.0, 10.0,
4719 labels, values, G_N_ELEMENTS(labels),
4720 sp_ddc_flatness_value_changed, 1, 0);
4721 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4722 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4723 }
4725 {
4726 /* Cap Rounding */
4727 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4728 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4729 // TRANSLATORS: "cap" means "end" (both start and finish) here
4730 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4731 _("Cap rounding"), _("Caps:"),
4732 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4733 "/tools/calligraphic/cap_rounding", 0.0,
4734 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4735 0.0, 5.0, 0.01, 0.1,
4736 labels, values, G_N_ELEMENTS(labels),
4737 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4738 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4739 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4740 }
4742 {
4743 /* Tremor */
4744 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4745 gdouble values[] = {0, 10, 20, 40, 60, 100};
4746 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4747 _("Stroke Tremor"), _("Tremor:"),
4748 _("Increase to make strokes rugged and trembling"),
4749 "/tools/calligraphic/tremor", 0.0,
4750 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4751 0.0, 100, 1, 10.0,
4752 labels, values, G_N_ELEMENTS(labels),
4753 sp_ddc_tremor_value_changed, 1, 0);
4755 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4756 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4757 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4758 }
4760 {
4761 /* Wiggle */
4762 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4763 gdouble values[] = {0, 20, 40, 60, 100};
4764 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4765 _("Pen Wiggle"), _("Wiggle:"),
4766 _("Increase to make the pen waver and wiggle"),
4767 "/tools/calligraphic/wiggle", 0.0,
4768 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4769 0.0, 100, 1, 10.0,
4770 labels, values, G_N_ELEMENTS(labels),
4771 sp_ddc_wiggle_value_changed, 1, 0);
4772 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4773 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4774 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4775 }
4777 {
4778 /* Mass */
4779 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4780 gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4781 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4782 _("Pen Mass"), _("Mass:"),
4783 _("Increase to make the pen drag behind, as if slowed by inertia"),
4784 "/tools/calligraphic/mass", 2.0,
4785 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4786 0.0, 100, 1, 10.0,
4787 labels, values, G_N_ELEMENTS(labels),
4788 sp_ddc_mass_value_changed, 1, 0);
4789 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4790 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4791 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4792 }
4795 /* Trace Background button */
4796 {
4797 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4798 _("Trace Background"),
4799 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4800 INKSCAPE_ICON_DRAW_TRACE_BACKGROUND,
4801 Inkscape::ICON_SIZE_DECORATION );
4802 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4803 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4804 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/tracebackground", false) );
4805 g_object_set_data( holder, "tracebackground", act );
4806 }
4808 /* Use Pressure button */
4809 {
4810 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4811 _("Pressure"),
4812 _("Use the pressure of the input device to alter the width of the pen"),
4813 INKSCAPE_ICON_DRAW_USE_PRESSURE,
4814 Inkscape::ICON_SIZE_DECORATION );
4815 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4816 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4817 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usepressure", true) );
4818 g_object_set_data( holder, "usepressure", act );
4819 }
4821 /* Use Tilt button */
4822 {
4823 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4824 _("Tilt"),
4825 _("Use the tilt of the input device to alter the angle of the pen's nib"),
4826 INKSCAPE_ICON_DRAW_USE_TILT,
4827 Inkscape::ICON_SIZE_DECORATION );
4828 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4829 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4830 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs->getBool("/tools/calligraphic/usetilt", true) );
4831 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usetilt", true) );
4832 g_object_set_data( holder, "usetilt", act );
4833 }
4835 /*calligraphic profile */
4836 {
4837 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
4838 EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
4839 ege_select_one_action_set_appearance (act1, "compact");
4840 g_object_set_data (holder, "profile_selector", act1 );
4842 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
4844 sp_dcc_build_presets_list (holder);
4846 g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
4847 gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
4848 }
4849 }
4850 }
4853 //########################
4854 //## Circle / Arc ##
4855 //########################
4857 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4858 {
4859 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4860 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4862 if (v1 == 0 && v2 == 0) {
4863 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4864 gtk_action_set_sensitive( ocb, FALSE );
4865 gtk_action_set_sensitive( make_whole, FALSE );
4866 }
4867 } else {
4868 gtk_action_set_sensitive( ocb, TRUE );
4869 gtk_action_set_sensitive( make_whole, TRUE );
4870 }
4871 }
4873 static void
4874 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4875 {
4876 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4878 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4879 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4880 prefs->setDouble(Glib::ustring("/tools/shapes/arc/") + value_name, adj->value);
4881 }
4883 // quit if run by the attr_changed listener
4884 if (g_object_get_data( tbl, "freeze" )) {
4885 return;
4886 }
4888 // in turn, prevent listener from responding
4889 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4891 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4893 bool modmade = false;
4894 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4895 items != NULL;
4896 items = items->next)
4897 {
4898 SPItem *item = SP_ITEM(items->data);
4900 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4902 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4903 SPArc *arc = SP_ARC(item);
4905 if (!strcmp(value_name, "start"))
4906 ge->start = (adj->value * M_PI)/ 180;
4907 else
4908 ge->end = (adj->value * M_PI)/ 180;
4910 sp_genericellipse_normalize(ge);
4911 ((SPObject *)arc)->updateRepr();
4912 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4914 modmade = true;
4915 }
4916 }
4918 g_free(namespaced_name);
4920 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4922 sp_arctb_sensitivize( tbl, adj->value, other->value );
4924 if (modmade) {
4925 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4926 _("Arc: Change start/end"));
4927 }
4929 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4930 }
4933 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
4934 {
4935 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
4936 }
4938 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4939 {
4940 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
4941 }
4944 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4945 {
4946 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4947 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4948 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4949 prefs->setBool("/tools/shapes/arc/open", ege_select_one_action_get_active(act) != 0);
4950 }
4952 // quit if run by the attr_changed listener
4953 if (g_object_get_data( tbl, "freeze" )) {
4954 return;
4955 }
4957 // in turn, prevent listener from responding
4958 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4960 bool modmade = false;
4962 if ( ege_select_one_action_get_active(act) != 0 ) {
4963 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4964 items != NULL;
4965 items = items->next)
4966 {
4967 if (SP_IS_ARC((SPItem *) items->data)) {
4968 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4969 repr->setAttribute("sodipodi:open", "true");
4970 SP_OBJECT((SPItem *) items->data)->updateRepr();
4971 modmade = true;
4972 }
4973 }
4974 } else {
4975 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4976 items != NULL;
4977 items = items->next)
4978 {
4979 if (SP_IS_ARC((SPItem *) items->data)) {
4980 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4981 repr->setAttribute("sodipodi:open", NULL);
4982 SP_OBJECT((SPItem *) items->data)->updateRepr();
4983 modmade = true;
4984 }
4985 }
4986 }
4988 if (modmade) {
4989 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4990 _("Arc: Change open/closed"));
4991 }
4993 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4994 }
4996 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4997 {
4998 GtkAdjustment *adj;
4999 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
5000 gtk_adjustment_set_value(adj, 0.0);
5001 gtk_adjustment_value_changed(adj);
5003 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
5004 gtk_adjustment_set_value(adj, 0.0);
5005 gtk_adjustment_value_changed(adj);
5007 spinbutton_defocus( GTK_OBJECT(obj) );
5008 }
5010 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
5011 gchar const */*old_value*/, gchar const */*new_value*/,
5012 bool /*is_interactive*/, gpointer data)
5013 {
5014 GObject *tbl = G_OBJECT(data);
5016 // quit if run by the _changed callbacks
5017 if (g_object_get_data( tbl, "freeze" )) {
5018 return;
5019 }
5021 // in turn, prevent callbacks from responding
5022 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5024 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
5025 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
5027 GtkAdjustment *adj1,*adj2;
5028 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
5029 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
5030 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
5031 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
5033 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
5035 char const *openstr = NULL;
5036 openstr = repr->attribute("sodipodi:open");
5037 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
5039 if (openstr) {
5040 ege_select_one_action_set_active( ocb, 1 );
5041 } else {
5042 ege_select_one_action_set_active( ocb, 0 );
5043 }
5045 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5046 }
5048 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
5049 NULL, /* child_added */
5050 NULL, /* child_removed */
5051 arc_tb_event_attr_changed,
5052 NULL, /* content_changed */
5053 NULL /* order_changed */
5054 };
5057 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
5058 {
5059 int n_selected = 0;
5060 Inkscape::XML::Node *repr = NULL;
5062 purge_repr_listener( tbl, tbl );
5064 for (GSList const *items = selection->itemList();
5065 items != NULL;
5066 items = items->next)
5067 {
5068 if (SP_IS_ARC((SPItem *) items->data)) {
5069 n_selected++;
5070 repr = SP_OBJECT_REPR((SPItem *) items->data);
5071 }
5072 }
5074 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
5076 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
5077 if (n_selected == 0) {
5078 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
5079 } else if (n_selected == 1) {
5080 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
5081 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5083 if (repr) {
5084 g_object_set_data( tbl, "repr", repr );
5085 Inkscape::GC::anchor(repr);
5086 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
5087 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
5088 }
5089 } else {
5090 // FIXME: implement averaging of all parameters for multiple selected
5091 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
5092 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5093 sp_arctb_sensitivize( tbl, 1, 0 );
5094 }
5095 }
5098 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5099 {
5100 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5102 EgeAdjustmentAction* eact = 0;
5103 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
5106 {
5107 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
5108 ege_output_action_set_use_markup( act, TRUE );
5109 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5110 g_object_set_data( holder, "mode_action", act );
5111 }
5113 /* Start */
5114 {
5115 eact = create_adjustment_action( "ArcStartAction",
5116 _("Start"), _("Start:"),
5117 _("The angle (in degrees) from the horizontal to the arc's start point"),
5118 "/tools/shapes/arc/start", 0.0,
5119 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
5120 -360.0, 360.0, 1.0, 10.0,
5121 0, 0, 0,
5122 sp_arctb_start_value_changed);
5123 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5124 }
5126 /* End */
5127 {
5128 eact = create_adjustment_action( "ArcEndAction",
5129 _("End"), _("End:"),
5130 _("The angle (in degrees) from the horizontal to the arc's end point"),
5131 "/tools/shapes/arc/end", 0.0,
5132 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
5133 -360.0, 360.0, 1.0, 10.0,
5134 0, 0, 0,
5135 sp_arctb_end_value_changed);
5136 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5137 }
5139 /* Segments / Pie checkbox */
5140 {
5141 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5143 GtkTreeIter iter;
5144 gtk_list_store_append( model, &iter );
5145 gtk_list_store_set( model, &iter,
5146 0, _("Closed arc"),
5147 1, _("Switch to segment (closed shape with two radii)"),
5148 2, INKSCAPE_ICON_DRAW_ELLIPSE_SEGMENT,
5149 -1 );
5151 gtk_list_store_append( model, &iter );
5152 gtk_list_store_set( model, &iter,
5153 0, _("Open Arc"),
5154 1, _("Switch to arc (unclosed shape)"),
5155 2, INKSCAPE_ICON_DRAW_ELLIPSE_ARC,
5156 -1 );
5158 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5159 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5160 g_object_set_data( holder, "open_action", act );
5162 ege_select_one_action_set_appearance( act, "full" );
5163 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5164 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5165 ege_select_one_action_set_icon_column( act, 2 );
5166 ege_select_one_action_set_icon_size( act, secondarySize );
5167 ege_select_one_action_set_tooltip_column( act, 1 );
5169 bool isClosed = !prefs->getBool("/tools/shapes/arc/open", false);
5170 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
5171 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
5172 }
5174 /* Make Whole */
5175 {
5176 InkAction* inky = ink_action_new( "ArcResetAction",
5177 _("Make whole"),
5178 _("Make the shape a whole ellipse, not arc or segment"),
5179 INKSCAPE_ICON_DRAW_ELLIPSE_WHOLE,
5180 secondarySize );
5181 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
5182 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5183 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
5184 g_object_set_data( holder, "make_whole", inky );
5185 }
5187 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
5188 // sensitivize make whole and open checkbox
5189 {
5190 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
5191 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
5192 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
5193 }
5196 sigc::connection *connection = new sigc::connection(
5197 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
5198 );
5199 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
5200 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
5201 }
5206 // toggle button callbacks and updaters
5208 //########################
5209 //## Dropper ##
5210 //########################
5212 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
5213 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5214 prefs->setInt( "/tools/dropper/pick", gtk_toggle_action_get_active( act ) );
5215 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
5216 if ( set_action ) {
5217 if ( gtk_toggle_action_get_active( act ) ) {
5218 gtk_action_set_sensitive( set_action, TRUE );
5219 } else {
5220 gtk_action_set_sensitive( set_action, FALSE );
5221 }
5222 }
5224 spinbutton_defocus(GTK_OBJECT(tbl));
5225 }
5227 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
5228 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5229 prefs->setBool( "/tools/dropper/setalpha", gtk_toggle_action_get_active( act ) );
5230 spinbutton_defocus(GTK_OBJECT(tbl));
5231 }
5234 /**
5235 * Dropper auxiliary toolbar construction and setup.
5236 *
5237 * TODO: Would like to add swatch of current color.
5238 * TODO: Add queue of last 5 or so colors selected with new swatches so that
5239 * can drag and drop places. Will provide a nice mixing palette.
5240 */
5241 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
5242 {
5243 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5244 gint pickAlpha = prefs->getInt( "/tools/dropper/pick", 1 );
5246 {
5247 EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
5248 ege_output_action_set_use_markup( act, TRUE );
5249 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5250 }
5252 {
5253 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
5254 _("Pick opacity"),
5255 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
5256 NULL,
5257 Inkscape::ICON_SIZE_DECORATION );
5258 g_object_set( act, "short_label", _("Pick"), NULL );
5259 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5260 g_object_set_data( holder, "pick_action", act );
5261 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
5262 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
5263 }
5265 {
5266 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
5267 _("Assign opacity"),
5268 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
5269 NULL,
5270 Inkscape::ICON_SIZE_DECORATION );
5271 g_object_set( act, "short_label", _("Assign"), NULL );
5272 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5273 g_object_set_data( holder, "set_action", act );
5274 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/dropper/setalpha", true) );
5275 // make sure it's disabled if we're not picking alpha
5276 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
5277 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
5278 }
5279 }
5282 //########################
5283 //## LPETool ##
5284 //########################
5286 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
5288 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
5289 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
5290 {
5291 using namespace Inkscape::LivePathEffect;
5293 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
5294 SPEventContext *ec = desktop->event_context;
5295 if (!SP_IS_LPETOOL_CONTEXT(ec)) {
5296 return;
5297 }
5299 // only take action if run by the attr_changed listener
5300 if (!g_object_get_data(tbl, "freeze")) {
5301 // in turn, prevent listener from responding
5302 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5304 gint mode = ege_select_one_action_get_active(act);
5305 EffectType type = lpesubtools[mode].type;
5307 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5308 bool success = lpetool_try_construction(lc, type);
5309 if (success) {
5310 // since the construction was already performed, we set the state back to inactive
5311 ege_select_one_action_set_active(act, 0);
5312 mode = 0;
5313 } else {
5314 // switch to the chosen subtool
5315 SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
5316 }
5318 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5319 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5320 prefs->setInt( "/tools/lpetool/mode", mode );
5321 }
5323 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
5324 }
5325 }
5327 void sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject */*tbl*/)
5328 {
5329 SPEventContext *ec = selection->desktop()->event_context;
5330 if (!SP_IS_LPETOOL_CONTEXT(ec))
5331 return;
5333 lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
5334 }
5336 void
5337 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
5338 {
5339 using namespace Inkscape::LivePathEffect;
5340 SPEventContext *ec = selection->desktop()->event_context;
5341 if (!SP_IS_LPETOOL_CONTEXT(ec))
5342 return;
5343 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
5345 lpetool_delete_measuring_items(lc);
5346 lpetool_create_measuring_items(lc, selection);
5348 // activate line segment combo box if a single item with LPELineSegment is selected
5349 GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
5350 SPItem *item = selection->singleItem();
5351 if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
5352 SPLPEItem *lpeitem = SP_LPE_ITEM(item);
5353 Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
5354 if (lpe && lpe->effectType() == LINE_SEGMENT) {
5355 LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
5356 g_object_set_data(tbl, "currentlpe", lpe);
5357 g_object_set_data(tbl, "currentlpeitem", lpeitem);
5358 gtk_action_set_sensitive(w, TRUE);
5359 ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
5360 } else {
5361 g_object_set_data(tbl, "currentlpe", NULL);
5362 g_object_set_data(tbl, "currentlpeitem", NULL);
5363 gtk_action_set_sensitive(w, FALSE);
5364 }
5365 } else {
5366 g_object_set_data(tbl, "currentlpe", NULL);
5367 g_object_set_data(tbl, "currentlpeitem", NULL);
5368 gtk_action_set_sensitive(w, FALSE);
5369 }
5370 }
5372 static void
5373 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
5374 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5375 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5377 bool show = gtk_toggle_action_get_active( act );
5378 prefs->setBool("/tools/lpetool/show_bbox", show);
5380 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5381 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5382 lpetool_context_reset_limiting_bbox(lc);
5383 }
5384 }
5386 static void
5387 lpetool_toggle_show_measuring_info (GtkToggleAction *act, GObject *tbl) {
5388 SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
5389 if (!tools_isactive(desktop, TOOLS_LPETOOL))
5390 return;
5392 GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
5393 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5394 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5395 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5396 bool show = gtk_toggle_action_get_active( act );
5397 prefs->setBool("/tools/lpetool/show_measuring_info", show);
5398 lpetool_show_measuring_info(lc, show);
5399 gtk_action_set_sensitive(GTK_ACTION(unitact), show);
5400 }
5401 }
5403 static void lpetool_unit_changed(GtkAction* /*act*/, GObject* tbl) {
5404 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
5405 SPUnit const *unit = tracker->getActiveUnit();
5406 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5407 prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5409 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5410 if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
5411 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5412 lpetool_delete_measuring_items(lc);
5413 lpetool_create_measuring_items(lc);
5414 }
5415 }
5417 static void
5418 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
5419 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5420 Inkscape::Selection *selection = desktop->selection;
5422 Geom::OptRect bbox = selection->bounds();
5424 if (bbox) {
5425 Geom::Point A(bbox->min());
5426 Geom::Point B(bbox->max());
5428 A *= desktop->doc2dt();
5429 B *= desktop->doc2dt();
5431 // TODO: should we provide a way to store points in prefs?
5432 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5433 prefs->setDouble("/tools/lpetool/bbox_upperleftx", A[Geom::X]);
5434 prefs->setDouble("/tools/lpetool/bbox_upperlefty", A[Geom::Y]);
5435 prefs->setDouble("/tools/lpetool/bbox_lowerrightx", B[Geom::X]);
5436 prefs->setDouble("/tools/lpetool/bbox_lowerrighty", B[Geom::Y]);
5438 lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
5439 }
5441 gtk_toggle_action_set_active(act, false);
5442 }
5444 static void
5445 sp_line_segment_build_list(GObject *tbl)
5446 {
5447 g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
5449 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
5450 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
5451 gtk_list_store_clear (model);
5453 // TODO: we add the entries of rht combo box manually; later this should be done automatically
5454 {
5455 GtkTreeIter iter;
5456 gtk_list_store_append( model, &iter );
5457 gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
5458 gtk_list_store_append( model, &iter );
5459 gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
5460 gtk_list_store_append( model, &iter );
5461 gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
5462 gtk_list_store_append( model, &iter );
5463 gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
5464 }
5466 g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5467 }
5469 static void
5470 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
5471 using namespace Inkscape::LivePathEffect;
5473 // quit if run by the attr_changed listener
5474 if (g_object_get_data(tbl, "freeze")) {
5475 return;
5476 }
5478 // in turn, prevent listener from responding
5479 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5481 LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
5482 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5483 if (lpeitem) {
5484 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5485 lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
5486 sp_lpe_item_update_patheffect(lpeitem, true, true);
5487 }
5489 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5490 }
5492 static void
5493 lpetool_open_lpe_dialog (GtkToggleAction *act, gpointer data) {
5494 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5496 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5497 sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5498 }
5499 gtk_toggle_action_set_active(act, false);
5500 }
5502 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5503 {
5504 UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5505 tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5506 g_object_set_data(holder, "tracker", tracker);
5507 SPUnit const *unit = tracker->getActiveUnit();
5509 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5510 prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5512 /** Automatically create a list of LPEs that get added to the toolbar **/
5513 {
5514 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5516 GtkTreeIter iter;
5518 // the first toggle button represents the state that no subtool is active (remove this when
5519 // this can be modeled by EgeSelectOneAction or some other action)
5520 gtk_list_store_append( model, &iter );
5521 gtk_list_store_set( model, &iter,
5522 0, _("All inactive"),
5523 1, _("No geometric tool is active"),
5524 2, "draw-geometry-inactive",
5525 -1 );
5527 Inkscape::LivePathEffect::EffectType type;
5528 for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5529 type = lpesubtools[i].type;
5530 gtk_list_store_append( model, &iter );
5531 gtk_list_store_set( model, &iter,
5532 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5533 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5534 2, lpesubtools[i].icon_name,
5535 -1 );
5536 }
5538 EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5539 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5540 g_object_set_data( holder, "lpetool_mode_action", act );
5542 ege_select_one_action_set_appearance( act, "full" );
5543 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5544 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5545 ege_select_one_action_set_icon_column( act, 2 );
5546 ege_select_one_action_set_tooltip_column( act, 1 );
5548 gint lpeToolMode = prefs->getInt("/tools/lpetool/mode", 0);
5549 ege_select_one_action_set_active( act, lpeToolMode );
5550 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5551 }
5553 /* Show limiting bounding box */
5554 {
5555 InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5556 _("Show limiting bounding box"),
5557 _("Show bounding box (used to cut infinite lines)"),
5558 "show-bounding-box",
5559 Inkscape::ICON_SIZE_DECORATION );
5560 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5561 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5562 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_bbox", true ) );
5563 }
5565 /* Set limiting bounding box to bbox of current selection */
5566 {
5567 InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5568 _("Get limiting bounding box from selection"),
5569 _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5570 "draw-geometry-set-bounding-box",
5571 Inkscape::ICON_SIZE_DECORATION );
5572 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5573 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5574 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5575 }
5578 /* Combo box to choose line segment type */
5579 {
5580 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5581 EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5582 ege_select_one_action_set_appearance (act, "compact");
5583 g_object_set_data (holder, "lpetool_line_segment_action", act );
5585 g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5587 sp_line_segment_build_list (holder);
5589 g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5590 gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5591 gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5592 }
5594 /* Display measuring info for selected items */
5595 {
5596 InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5597 _("Display measuring info"),
5598 _("Display measuring info for selected items"),
5599 "draw-geometry-show-measuring-info",
5600 Inkscape::ICON_SIZE_DECORATION );
5601 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5602 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
5603 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_measuring_info", true ) );
5604 }
5606 // add the units menu
5607 {
5608 GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5609 gtk_action_group_add_action( mainActions, act );
5610 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5611 g_object_set_data(holder, "lpetool_units_action", act);
5612 gtk_action_set_sensitive(act, prefs->getBool("/tools/lpetool/show_measuring_info", true));
5613 }
5615 /* Open LPE dialog (to adapt parameters numerically) */
5616 {
5617 InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
5618 _("Open LPE dialog"),
5619 _("Open LPE dialog (to adapt parameters numerically)"),
5620 "dialog-geometry",
5621 Inkscape::ICON_SIZE_DECORATION );
5622 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5623 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
5624 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5625 }
5627 //watch selection
5628 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5630 sigc::connection *c_selection_modified =
5631 new sigc::connection (sp_desktop_selection (desktop)->connectModified
5632 (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
5633 pool->add_connection ("selection-modified", c_selection_modified);
5635 sigc::connection *c_selection_changed =
5636 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5637 (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5638 pool->add_connection ("selection-changed", c_selection_changed);
5639 }
5641 //########################
5642 //## Eraser ##
5643 //########################
5645 static void sp_erc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
5646 {
5647 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5648 prefs->setDouble( "/tools/eraser/width", adj->value );
5649 update_presets_list(tbl);
5650 }
5652 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5653 {
5654 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5655 bool eraserMode = ege_select_one_action_get_active( act ) != 0;
5656 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5657 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5658 prefs->setBool( "/tools/eraser/mode", eraserMode );
5659 }
5661 // only take action if run by the attr_changed listener
5662 if (!g_object_get_data( tbl, "freeze" )) {
5663 // in turn, prevent listener from responding
5664 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5666 if ( eraserMode != 0 ) {
5667 } else {
5668 }
5669 // TODO finish implementation
5671 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5672 }
5673 }
5675 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5676 {
5677 {
5678 /* Width */
5679 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5680 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5681 EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5682 _("Pen Width"), _("Width:"),
5683 _("The width of the eraser pen (relative to the visible canvas area)"),
5684 "/tools/eraser/width", 15,
5685 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5686 1, 100, 1.0, 10.0,
5687 labels, values, G_N_ELEMENTS(labels),
5688 sp_erc_width_value_changed, 1, 0);
5689 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5690 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5691 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5692 }
5694 {
5695 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5697 GtkTreeIter iter;
5698 gtk_list_store_append( model, &iter );
5699 gtk_list_store_set( model, &iter,
5700 0, _("Delete"),
5701 1, _("Delete objects touched by the eraser"),
5702 2, INKSCAPE_ICON_DRAW_ERASER_DELETE_OBJECTS,
5703 -1 );
5705 gtk_list_store_append( model, &iter );
5706 gtk_list_store_set( model, &iter,
5707 0, _("Cut"),
5708 1, _("Cut out from objects"),
5709 2, INKSCAPE_ICON_PATH_DIFFERENCE,
5710 -1 );
5712 EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5713 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5714 g_object_set_data( holder, "eraser_mode_action", act );
5716 ege_select_one_action_set_appearance( act, "full" );
5717 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5718 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5719 ege_select_one_action_set_icon_column( act, 2 );
5720 ege_select_one_action_set_tooltip_column( act, 1 );
5722 /// @todo Convert to boolean?
5723 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5724 gint eraserMode = prefs->getBool("/tools/eraser/mode") ? 1 : 0;
5725 ege_select_one_action_set_active( act, eraserMode );
5726 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
5727 }
5729 }
5731 //########################
5732 //## Text Toolbox ##
5733 //########################
5734 /*
5735 static void
5736 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
5737 {
5738 //Call back for letter sizing spinbutton
5739 }
5741 static void
5742 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
5743 {
5744 //Call back for line height spinbutton
5745 }
5747 static void
5748 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5749 {
5750 //Call back for horizontal kerning spinbutton
5751 }
5753 static void
5754 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5755 {
5756 //Call back for vertical kerning spinbutton
5757 }
5759 static void
5760 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
5761 {
5762 //Call back for letter rotation spinbutton
5763 }*/
5765 namespace {
5767 void
5768 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
5769 {
5770 // quit if run by the _changed callbacks
5771 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5772 return;
5773 }
5775 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5777 SPStyle *query =
5778 sp_style_new (SP_ACTIVE_DOCUMENT);
5780 int result_family =
5781 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5783 int result_style =
5784 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5786 int result_numbers =
5787 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5789 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5791 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5792 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
5793 // there are no texts in selection, read from prefs
5795 sp_style_read_from_prefs(query, "/tools/text");
5797 if (g_object_get_data(tbl, "text_style_from_prefs")) {
5798 // do not reset the toolbar style from prefs if we already did it last time
5799 sp_style_unref(query);
5800 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5801 return;
5802 }
5803 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
5804 } else {
5805 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
5806 }
5808 if (query->text)
5809 {
5810 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
5811 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5812 gtk_entry_set_text (GTK_ENTRY (entry), "");
5814 } else if (query->text->font_specification.value || query->text->font_family.value) {
5816 Gtk::ComboBoxEntry *combo = (Gtk::ComboBoxEntry *) (g_object_get_data (G_OBJECT (tbl), "family-entry-combo"));
5817 GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5819 // Get the font that corresponds
5820 Glib::ustring familyName;
5822 font_instance * font = font_factory::Default()->FaceFromStyle(query);
5823 if (font) {
5824 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
5825 font->Unref();
5826 font = NULL;
5827 }
5829 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
5831 Gtk::TreeIter iter;
5832 try {
5833 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
5834 Glib::RefPtr<Gtk::TreeModel> model = combo->get_model();
5835 iter = model->get_iter(path);
5836 } catch (...) {
5837 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
5838 sp_style_unref(query);
5839 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5840 return;
5841 }
5843 combo->set_active (iter);
5844 }
5846 //Size
5847 {
5848 GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
5849 gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
5850 gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
5851 g_free(str);
5852 }
5854 //Anchor
5855 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
5856 {
5857 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
5858 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5859 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5860 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5861 }
5862 else
5863 {
5864 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
5865 {
5866 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
5867 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5868 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5869 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5870 }
5871 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
5872 {
5873 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
5874 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5875 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5876 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5877 }
5878 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
5879 {
5880 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
5881 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5882 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5883 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5884 }
5885 }
5887 //Style
5888 {
5889 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
5891 gboolean active = gtk_toggle_button_get_active (button);
5892 gboolean check = ((query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700) && (query->font_weight.computed != SP_CSS_FONT_WEIGHT_NORMAL) && (query->font_weight.computed != SP_CSS_FONT_WEIGHT_LIGHTER));
5894 if (active != check)
5895 {
5896 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5897 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5898 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5899 }
5900 }
5902 {
5903 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
5905 gboolean active = gtk_toggle_button_get_active (button);
5906 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
5908 if (active != check)
5909 {
5910 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5911 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5912 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5913 }
5914 }
5916 //Orientation
5917 //locking both buttons, changing one affect all group (both)
5918 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
5919 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5921 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
5922 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
5924 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
5925 {
5926 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5927 }
5928 else
5929 {
5930 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
5931 }
5932 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5933 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
5934 }
5936 sp_style_unref(query);
5938 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5939 }
5941 void
5942 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
5943 {
5944 sp_text_toolbox_selection_changed (selection, tbl);
5945 }
5947 void
5948 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
5949 {
5950 sp_text_toolbox_selection_changed (NULL, tbl);
5951 }
5953 void
5954 sp_text_toolbox_family_changed (GtkComboBoxEntry *,
5955 GObject *tbl)
5956 {
5957 // quit if run by the _changed callbacks
5958 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5959 return;
5960 }
5962 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5964 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5965 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5966 const gchar* family = gtk_entry_get_text (GTK_ENTRY (entry));
5968 //g_print ("family changed to: %s\n", family);
5970 SPStyle *query =
5971 sp_style_new (SP_ACTIVE_DOCUMENT);
5973 int result_fontspec =
5974 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5976 SPCSSAttr *css = sp_repr_css_attr_new ();
5978 // First try to get the font spec from the stored value
5979 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
5981 if (fontSpec.empty()) {
5982 // Construct a new font specification if it does not yet exist
5983 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5984 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5985 fontFromStyle->Unref();
5986 }
5988 if (!fontSpec.empty()) {
5990 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
5992 if (!newFontSpec.empty()) {
5994 if (fontSpec != newFontSpec) {
5996 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
5998 if (font) {
5999 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6001 // Set all the these just in case they were altered when finding the best
6002 // match for the new family and old style...
6004 gchar c[256];
6006 font->Family(c, 256);
6008 sp_repr_css_set_property (css, "font-family", c);
6010 font->Attribute( "weight", c, 256);
6011 sp_repr_css_set_property (css, "font-weight", c);
6013 font->Attribute("style", c, 256);
6014 sp_repr_css_set_property (css, "font-style", c);
6016 font->Attribute("stretch", c, 256);
6017 sp_repr_css_set_property (css, "font-stretch", c);
6019 font->Attribute("variant", c, 256);
6020 sp_repr_css_set_property (css, "font-variant", c);
6022 font->Unref();
6023 }
6024 }
6026 } else {
6027 // If the old font on selection (or default) was not existing on the system,
6028 // ReplaceFontSpecificationFamily does not work. In that case we fall back to blindly
6029 // setting the family reported by the family chooser.
6031 //g_print ("fallback setting family: %s\n", family);
6032 sp_repr_css_set_property (css, "-inkscape-font-specification", family);
6033 sp_repr_css_set_property (css, "font-family", family);
6034 }
6035 }
6037 // If querying returned nothing, set the default style of the tool (for new texts)
6038 if (result_fontspec == QUERY_STYLE_NOTHING)
6039 {
6040 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6041 prefs->mergeStyle("/tools/text/style", css);
6042 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
6043 }
6044 else
6045 {
6046 sp_desktop_set_style (desktop, css, true, true);
6047 }
6049 sp_style_unref(query);
6051 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6052 _("Text: Change font family"));
6053 sp_repr_css_attr_unref (css);
6055 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6057 // unfreeze
6058 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6060 // focus to canvas
6061 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6062 }
6065 void
6066 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
6067 gpointer data)
6068 {
6069 if (g_object_get_data (G_OBJECT (button), "block")) return;
6070 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
6071 int prop = GPOINTER_TO_INT(data);
6073 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6074 SPCSSAttr *css = sp_repr_css_attr_new ();
6076 switch (prop)
6077 {
6078 case 0:
6079 {
6080 sp_repr_css_set_property (css, "text-anchor", "start");
6081 sp_repr_css_set_property (css, "text-align", "start");
6082 break;
6083 }
6084 case 1:
6085 {
6086 sp_repr_css_set_property (css, "text-anchor", "middle");
6087 sp_repr_css_set_property (css, "text-align", "center");
6088 break;
6089 }
6091 case 2:
6092 {
6093 sp_repr_css_set_property (css, "text-anchor", "end");
6094 sp_repr_css_set_property (css, "text-align", "end");
6095 break;
6096 }
6098 case 3:
6099 {
6100 sp_repr_css_set_property (css, "text-anchor", "start");
6101 sp_repr_css_set_property (css, "text-align", "justify");
6102 break;
6103 }
6104 }
6106 SPStyle *query =
6107 sp_style_new (SP_ACTIVE_DOCUMENT);
6108 int result_numbers =
6109 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6111 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6112 if (result_numbers == QUERY_STYLE_NOTHING)
6113 {
6114 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6115 prefs->mergeStyle("/tools/text/style", css);
6116 }
6118 sp_style_unref(query);
6120 sp_desktop_set_style (desktop, css, true, true);
6121 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6122 _("Text: Change alignment"));
6123 sp_repr_css_attr_unref (css);
6125 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6126 }
6128 void
6129 sp_text_toolbox_style_toggled (GtkToggleButton *button,
6130 gpointer data)
6131 {
6132 if (g_object_get_data (G_OBJECT (button), "block")) return;
6134 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6135 SPCSSAttr *css = sp_repr_css_attr_new ();
6136 int prop = GPOINTER_TO_INT(data);
6137 bool active = gtk_toggle_button_get_active (button);
6139 SPStyle *query =
6140 sp_style_new (SP_ACTIVE_DOCUMENT);
6142 int result_fontspec =
6143 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6145 //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6146 //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6147 //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6149 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
6150 Glib::ustring newFontSpec = "";
6152 if (fontSpec.empty()) {
6153 // Construct a new font specification if it does not yet exist
6154 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6155 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6156 fontFromStyle->Unref();
6157 }
6159 bool nochange = true;
6160 switch (prop)
6161 {
6162 case 0:
6163 {
6164 if (!fontSpec.empty()) {
6165 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
6166 if (!newFontSpec.empty()) {
6167 // Don't even set the bold if the font didn't exist on the system
6168 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
6169 nochange = false;
6170 }
6171 }
6172 // set or reset the button according
6173 if(nochange) {
6174 gboolean check = gtk_toggle_button_get_active (button);
6176 if (active != check)
6177 {
6178 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6179 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6180 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6181 }
6182 }
6184 break;
6185 }
6187 case 1:
6188 {
6189 if (!fontSpec.empty()) {
6190 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
6191 if (!newFontSpec.empty()) {
6192 // Don't even set the italic if the font didn't exist on the system
6193 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
6194 nochange = false;
6195 }
6196 }
6197 if(nochange) {
6198 gboolean check = gtk_toggle_button_get_active (button);
6200 if (active != check)
6201 {
6202 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6203 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6204 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6205 }
6206 }
6207 break;
6208 }
6209 }
6211 if (!newFontSpec.empty()) {
6212 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6213 }
6215 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6216 if (result_fontspec == QUERY_STYLE_NOTHING)
6217 {
6218 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6219 prefs->mergeStyle("/tools/text/style", css);
6220 }
6222 sp_style_unref(query);
6224 sp_desktop_set_style (desktop, css, true, true);
6225 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6226 _("Text: Change font style"));
6227 sp_repr_css_attr_unref (css);
6229 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6230 }
6232 void
6233 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
6234 gpointer data)
6235 {
6236 if (g_object_get_data (G_OBJECT (button), "block")) {
6237 return;
6238 }
6240 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6241 SPCSSAttr *css = sp_repr_css_attr_new ();
6242 int prop = GPOINTER_TO_INT(data);
6244 switch (prop)
6245 {
6246 case 0:
6247 {
6248 sp_repr_css_set_property (css, "writing-mode", "lr");
6249 break;
6250 }
6252 case 1:
6253 {
6254 sp_repr_css_set_property (css, "writing-mode", "tb");
6255 break;
6256 }
6257 }
6259 SPStyle *query =
6260 sp_style_new (SP_ACTIVE_DOCUMENT);
6261 int result_numbers =
6262 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6264 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6265 if (result_numbers == QUERY_STYLE_NOTHING)
6266 {
6267 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6268 prefs->mergeStyle("/tools/text/style", css);
6269 }
6271 sp_desktop_set_style (desktop, css, true, true);
6272 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6273 _("Text: Change orientation"));
6274 sp_repr_css_attr_unref (css);
6276 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6277 }
6279 gboolean
6280 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6281 {
6282 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6283 if (!desktop) return FALSE;
6285 switch (get_group0_keyval (event)) {
6286 case GDK_KP_Enter: // chosen
6287 case GDK_Return:
6288 // unfreeze and update, which will defocus
6289 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6290 sp_text_toolbox_family_changed (NULL, tbl);
6291 return TRUE; // I consumed the event
6292 break;
6293 case GDK_Escape:
6294 // defocus
6295 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6296 return TRUE; // I consumed the event
6297 break;
6298 }
6299 return FALSE;
6300 }
6302 gboolean
6303 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
6304 {
6305 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6306 if (!desktop) return FALSE;
6308 switch (get_group0_keyval (event)) {
6309 case GDK_KP_Enter:
6310 case GDK_Return:
6311 case GDK_Escape: // defocus
6312 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6313 return TRUE; // I consumed the event
6314 break;
6315 case GDK_w:
6316 case GDK_W:
6317 if (event->state & GDK_CONTROL_MASK) {
6318 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6319 return TRUE; // I consumed the event
6320 }
6321 break;
6322 }
6323 return FALSE;
6324 }
6327 void
6328 sp_text_toolbox_size_changed (GtkComboBox *cbox,
6329 GObject *tbl)
6330 {
6331 // quit if run by the _changed callbacks
6332 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6333 return;
6334 }
6336 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6338 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6340 // If this is not from selecting a size in the list (in which case get_active will give the
6341 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
6342 // process this event. This fixes GTK's stupid insistence on sending an activate change every
6343 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
6344 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed")) {
6345 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6346 return;
6347 }
6349 gdouble value = -1;
6350 {
6351 gchar *endptr;
6352 gchar *const text = gtk_combo_box_get_active_text(cbox);
6353 if (text) {
6354 value = g_strtod(text, &endptr);
6355 if (endptr == text) { // Conversion failed, non-numeric input.
6356 value = -1;
6357 }
6358 g_free(text);
6359 }
6360 }
6361 if (value <= 0) {
6362 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6363 return; // could not parse value
6364 }
6366 SPCSSAttr *css = sp_repr_css_attr_new ();
6367 Inkscape::CSSOStringStream osfs;
6368 osfs << value;
6369 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
6371 SPStyle *query =
6372 sp_style_new (SP_ACTIVE_DOCUMENT);
6373 int result_numbers =
6374 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6376 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6377 if (result_numbers == QUERY_STYLE_NOTHING)
6378 {
6379 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6380 prefs->mergeStyle("/tools/text/style", css);
6381 }
6383 sp_style_unref(query);
6385 sp_desktop_set_style (desktop, css, true, true);
6386 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
6387 _("Text: Change font size"));
6388 sp_repr_css_attr_unref (css);
6390 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6392 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6393 }
6395 gboolean
6396 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
6397 {
6398 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6399 if (!desktop) return FALSE;
6401 if (!g_object_get_data (tbl, "esc-pressed")) {
6402 g_object_set_data (tbl, "enter-pressed", gpointer(1));
6403 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6404 sp_text_toolbox_size_changed (cbox, tbl);
6405 g_object_set_data (tbl, "enter-pressed", gpointer(0));
6406 }
6407 return FALSE; // I consumed the event
6408 }
6411 gboolean
6412 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6413 {
6414 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6415 if (!desktop) return FALSE;
6417 switch (get_group0_keyval (event)) {
6418 case GDK_Escape: // defocus
6419 g_object_set_data (tbl, "esc-pressed", gpointer(1));
6420 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6421 g_object_set_data (tbl, "esc-pressed", gpointer(0));
6422 return TRUE; // I consumed the event
6423 break;
6424 case GDK_Return: // defocus
6425 case GDK_KP_Enter:
6426 g_object_set_data (tbl, "enter-pressed", gpointer(1));
6427 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6428 sp_text_toolbox_size_changed (cbox, tbl);
6429 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6430 g_object_set_data (tbl, "enter-pressed", gpointer(0));
6431 return TRUE; // I consumed the event
6432 break;
6433 }
6434 return FALSE;
6435 }
6437 // While editing font name in the entry, disable family_changed by freezing, otherwise completion
6438 // does not work!
6439 gboolean
6440 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
6441 GdkEventFocus */*event*/,
6442 GObject *tbl)
6443 {
6444 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6445 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1); // select all
6446 return FALSE;
6447 }
6449 gboolean
6450 sp_text_toolbox_entry_focus_out (GtkWidget *entry,
6451 GdkEventFocus */*event*/,
6452 GObject *tbl)
6453 {
6454 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6455 gtk_entry_select_region (GTK_ENTRY (entry), 0, 0); // deselect
6456 return FALSE;
6457 }
6459 void
6460 cell_data_func (GtkCellLayout */*cell_layout*/,
6461 GtkCellRenderer *cell,
6462 GtkTreeModel *tree_model,
6463 GtkTreeIter *iter,
6464 gpointer /*data*/)
6465 {
6466 gchar *family;
6467 gtk_tree_model_get(tree_model, iter, 0, &family, -1);
6468 gchar *const family_escaped = g_markup_escape_text(family, -1);
6470 static char const *const sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
6471 gchar *const sample_escaped = g_markup_escape_text(sample, -1);
6473 std::stringstream markup;
6474 markup << family_escaped << " <span foreground='darkgray' font_family='"
6475 << family_escaped << "'>" << sample_escaped << "</span>";
6476 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6478 g_free(family);
6479 g_free(family_escaped);
6480 g_free(sample_escaped);
6481 }
6483 gboolean text_toolbox_completion_match_selected (GtkEntryCompletion *widget,
6484 GtkTreeModel *model,
6485 GtkTreeIter *iter,
6486 GObject *tbl)
6487 {
6488 // We intercept this signal so as to fire family_changed at once (without it, you'd have to
6489 // press Enter again after choosing a completion)
6490 gchar *family;
6491 gtk_tree_model_get(model, iter, 0, &family, -1);
6493 GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6494 gtk_entry_set_text (GTK_ENTRY (entry), family);
6496 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6497 sp_text_toolbox_family_changed (NULL, tbl);
6498 return TRUE;
6499 }
6502 static void
6503 cbe_add_completion (GtkComboBoxEntry *cbe, GObject *tbl){
6504 GtkEntry *entry;
6505 GtkEntryCompletion *completion;
6506 GtkTreeModel *model;
6508 entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(cbe)));
6509 completion = gtk_entry_completion_new();
6510 model = gtk_combo_box_get_model(GTK_COMBO_BOX(cbe));
6511 gtk_entry_completion_set_model(completion, model);
6512 gtk_entry_completion_set_text_column(completion, 0);
6513 gtk_entry_completion_set_inline_completion(completion, FALSE);
6514 gtk_entry_completion_set_inline_selection(completion, FALSE);
6515 gtk_entry_completion_set_popup_completion(completion, TRUE);
6516 gtk_entry_set_completion(entry, completion);
6518 g_signal_connect (G_OBJECT (completion), "match-selected", G_CALLBACK (text_toolbox_completion_match_selected), tbl);
6520 g_object_unref(completion);
6521 }
6523 void sp_text_toolbox_family_popnotify (GtkComboBox *widget,
6524 void *property,
6525 GObject *tbl)
6526 {
6527 // while the drop-down is open, we disable font family changing, reenabling it only when it closes
6529 gboolean shown;
6530 g_object_get (G_OBJECT(widget), "popup-shown", &shown, NULL);
6531 if (shown) {
6532 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6533 //g_print("POP: notify: SHOWN\n");
6534 } else {
6535 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6537 // stupid GTK doesn't let us attach to events in the drop-down window, so we peek here to
6538 // find out if the drop down was closed by Enter and if so, manually update (only
6539 // necessary on Windows, on Linux it updates itself - what a mess, but we'll manage)
6540 GdkEvent *ev = gtk_get_current_event();
6541 if (ev) {
6542 //g_print ("ev type: %d\n", ev->type);
6543 if (ev->type == GDK_KEY_PRESS) {
6544 switch (get_group0_keyval ((GdkEventKey *) ev)) {
6545 case GDK_KP_Enter: // chosen
6546 case GDK_Return:
6547 {
6548 // make sure the chosen one is inserted into the entry
6549 GtkComboBox *combo = GTK_COMBO_BOX (((Gtk::ComboBox *) (g_object_get_data (tbl, "family-entry-combo")))->gobj());
6550 GtkTreeModel *model = gtk_combo_box_get_model(combo);
6551 GtkTreeIter iter;
6552 gboolean has_active = gtk_combo_box_get_active_iter (combo, &iter);
6553 if (has_active) {
6554 gchar *family;
6555 gtk_tree_model_get(model, &iter, 0, &family, -1);
6556 GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6557 gtk_entry_set_text (GTK_ENTRY (entry), family);
6558 }
6560 // update
6561 sp_text_toolbox_family_changed (NULL, tbl);
6562 break;
6563 }
6564 }
6565 }
6566 }
6568 // regardless of whether we updated, defocus the widget
6569 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6570 if (desktop)
6571 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6572 //g_print("POP: notify: HIDDEN\n");
6573 }
6574 }
6576 GtkWidget*
6577 sp_text_toolbox_new (SPDesktop *desktop)
6578 {
6579 GtkToolbar *tbl = GTK_TOOLBAR(gtk_toolbar_new());
6580 GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("/toolbox/secondary", 1));
6582 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
6583 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
6585 GtkTooltips *tt = gtk_tooltips_new();
6587 ////////////Family
6588 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
6589 Gtk::ComboBoxEntry *font_sel = Gtk::manage(new Gtk::ComboBoxEntry(store));
6591 gtk_rc_parse_string (
6592 "style \"dropdown-as-list-style\"\n"
6593 "{\n"
6594 " GtkComboBox::appears-as-list = 1\n"
6595 "}\n"
6596 "widget \"*.toolbox-fontfamily-list\" style \"dropdown-as-list-style\"");
6597 gtk_widget_set_name(GTK_WIDGET (font_sel->gobj()), "toolbox-fontfamily-list");
6598 gtk_tooltips_set_tip (tt, GTK_WIDGET (font_sel->gobj()), _("Select font family (Alt+X to access)"), "");
6600 g_signal_connect (G_OBJECT (font_sel->gobj()), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
6602 cbe_add_completion(font_sel->gobj(), G_OBJECT(tbl));
6604 gtk_toolbar_append_widget( tbl, (GtkWidget*) font_sel->gobj(), "", "");
6605 g_object_set_data (G_OBJECT (tbl), "family-entry-combo", font_sel);
6607 // expand the field a bit so as to view more of the previews in the drop-down
6608 GtkRequisition req;
6609 gtk_widget_size_request (GTK_WIDGET (font_sel->gobj()), &req);
6610 gtk_widget_set_size_request (GTK_WIDGET (font_sel->gobj()), req.width + 40, -1);
6612 GtkWidget* entry = (GtkWidget*) font_sel->get_entry()->gobj();
6613 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6615 g_signal_connect (G_OBJECT (font_sel->gobj()), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6616 g_signal_connect (G_OBJECT (font_sel->gobj()), "notify::popup-shown",
6617 G_CALLBACK (sp_text_toolbox_family_popnotify), tbl);
6618 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
6619 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
6620 g_signal_connect (G_OBJECT (entry), "focus-out-event", G_CALLBACK (sp_text_toolbox_entry_focus_out), tbl);
6622 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
6623 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
6625 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
6626 gtk_cell_layout_clear( GTK_CELL_LAYOUT(font_sel->gobj()) );
6627 gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(font_sel->gobj()) , cell , TRUE );
6628 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT(font_sel->gobj()), cell, GtkCellLayoutDataFunc (cell_data_func), NULL, NULL);
6630 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
6631 GtkWidget *box = gtk_event_box_new ();
6632 gtk_container_add (GTK_CONTAINER (box), image);
6633 gtk_toolbar_append_widget( tbl, box, "", "");
6634 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
6635 gtk_tooltips_set_tip (tt, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
6636 gtk_widget_hide (GTK_WIDGET (box));
6637 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
6639 ////////////Size
6640 gchar const *const sizes[] = {
6641 "4", "6", "8", "9", "10", "11", "12", "13", "14",
6642 "16", "18", "20", "22", "24", "28",
6643 "32", "36", "40", "48", "56", "64", "72", "144"
6644 };
6646 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
6647 for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
6648 gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
6649 }
6650 gtk_widget_set_size_request (cbox, 80, -1);
6651 gtk_toolbar_append_widget( tbl, cbox, "", "");
6652 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
6653 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
6654 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
6655 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
6657 ////////////Text anchor
6658 GtkWidget *group = gtk_radio_button_new (NULL);
6659 GtkWidget *row = gtk_hbox_new (FALSE, 4);
6660 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
6662 // left
6663 GtkWidget *rbutton = group;
6664 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6665 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
6666 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6668 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6669 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
6670 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
6671 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
6673 // center
6674 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6675 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6676 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
6677 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6679 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6680 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
6681 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
6682 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
6684 // right
6685 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6686 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6687 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
6688 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6690 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6691 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
6692 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
6693 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
6695 // fill
6696 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6697 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6698 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
6699 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6701 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6702 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
6703 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
6704 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
6706 gtk_toolbar_append_widget( tbl, row, "", "");
6708 //spacer
6709 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6711 ////////////Text style
6712 row = gtk_hbox_new (FALSE, 4);
6714 // bold
6715 rbutton = gtk_toggle_button_new ();
6716 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6717 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
6718 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6719 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
6721 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6722 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
6723 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
6725 // italic
6726 rbutton = gtk_toggle_button_new ();
6727 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6728 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
6729 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6730 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
6732 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6733 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
6734 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
6736 gtk_toolbar_append_widget( tbl, row, "", "");
6738 //spacer
6739 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6741 // Text orientation
6742 group = gtk_radio_button_new (NULL);
6743 row = gtk_hbox_new (FALSE, 4);
6744 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
6746 // horizontal
6747 rbutton = group;
6748 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6749 gtk_container_add (GTK_CONTAINER (rbutton),
6750 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_HORIZONTAL));
6751 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6752 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
6754 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6755 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
6756 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
6758 // vertical
6759 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6760 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6761 gtk_container_add (GTK_CONTAINER (rbutton),
6762 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_VERTICAL));
6763 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6764 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
6766 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6767 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
6768 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
6769 gtk_toolbar_append_widget( tbl, row, "", "" );
6772 //watch selection
6773 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
6775 sigc::connection *c_selection_changed =
6776 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
6777 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
6778 pool->add_connection ("selection-changed", c_selection_changed);
6780 sigc::connection *c_selection_modified =
6781 new sigc::connection (sp_desktop_selection (desktop)->connectModified
6782 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
6783 pool->add_connection ("selection-modified", c_selection_modified);
6785 sigc::connection *c_subselection_changed =
6786 new sigc::connection (desktop->connectToolSubselectionChanged
6787 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
6788 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
6790 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
6793 gtk_widget_show_all( GTK_WIDGET(tbl) );
6795 return GTK_WIDGET(tbl);
6796 } // end of sp_text_toolbox_new()
6798 }//<unnamed> namespace
6801 //#########################
6802 //## Connector ##
6803 //#########################
6805 static void sp_connector_path_set_avoid(void)
6806 {
6807 cc_selection_set_avoid(true);
6808 }
6811 static void sp_connector_path_set_ignore(void)
6812 {
6813 cc_selection_set_avoid(false);
6814 }
6818 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
6819 {
6820 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
6821 SPDocument *doc = sp_desktop_document(desktop);
6823 if (!sp_document_get_undo_sensitive(doc))
6824 {
6825 return;
6826 }
6828 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6830 if ( !repr->attribute("inkscape:connector-spacing") &&
6831 ( adj->value == defaultConnSpacing )) {
6832 // Don't need to update the repr if the attribute doesn't
6833 // exist and it is being set to the default value -- as will
6834 // happen at startup.
6835 return;
6836 }
6838 // quit if run by the attr_changed listener
6839 if (g_object_get_data( tbl, "freeze" )) {
6840 return;
6841 }
6843 // in turn, prevent listener from responding
6844 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
6846 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
6847 SP_OBJECT(desktop->namedview)->updateRepr();
6849 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
6850 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
6851 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
6852 Geom::Matrix m = Geom::identity();
6853 avoid_item_move(&m, item);
6854 }
6856 if (items) {
6857 g_slist_free(items);
6858 }
6860 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
6861 _("Change connector spacing"));
6863 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6864 }
6866 static void sp_connector_graph_layout(void)
6867 {
6868 if (!SP_ACTIVE_DESKTOP) return;
6869 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6871 // hack for clones, see comment in align-and-distribute.cpp
6872 int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
6873 prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
6875 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
6877 prefs->setInt("/options/clonecompensation/value", saved_compensation);
6879 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
6880 }
6882 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6883 {
6884 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6885 prefs->setBool("/tools/connector/directedlayout",
6886 gtk_toggle_action_get_active( act ));
6887 }
6889 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6890 {
6891 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6892 prefs->setBool("/tools/connector/avoidoverlaplayout",
6893 gtk_toggle_action_get_active( act ));
6894 }
6897 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
6898 {
6899 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6900 prefs->setDouble("/tools/connector/length", adj->value);
6901 }
6903 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
6904 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
6905 bool /*is_interactive*/, gpointer data)
6906 {
6907 GtkWidget *tbl = GTK_WIDGET(data);
6909 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6910 return;
6911 }
6912 if (strcmp(name, "inkscape:connector-spacing") != 0) {
6913 return;
6914 }
6916 GtkAdjustment *adj = (GtkAdjustment*)
6917 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
6918 gdouble spacing = defaultConnSpacing;
6919 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
6921 gtk_adjustment_set_value(adj, spacing);
6922 gtk_adjustment_value_changed(adj);
6924 spinbutton_defocus(GTK_OBJECT(tbl));
6925 }
6928 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
6929 NULL, /* child_added */
6930 NULL, /* child_removed */
6931 connector_tb_event_attr_changed,
6932 NULL, /* content_changed */
6933 NULL /* order_changed */
6934 };
6937 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
6938 {
6939 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6940 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
6942 {
6943 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
6944 _("Avoid"),
6945 _("Make connectors avoid selected objects"),
6946 INKSCAPE_ICON_CONNECTOR_AVOID,
6947 secondarySize );
6948 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
6949 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6950 }
6952 {
6953 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
6954 _("Ignore"),
6955 _("Make connectors ignore selected objects"),
6956 INKSCAPE_ICON_CONNECTOR_IGNORE,
6957 secondarySize );
6958 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
6959 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6960 }
6962 EgeAdjustmentAction* eact = 0;
6964 // Spacing spinbox
6965 eact = create_adjustment_action( "ConnectorSpacingAction",
6966 _("Connector Spacing"), _("Spacing:"),
6967 _("The amount of space left around objects by auto-routing connectors"),
6968 "/tools/connector/spacing", defaultConnSpacing,
6969 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
6970 0, 100, 1.0, 10.0,
6971 0, 0, 0,
6972 connector_spacing_changed, 1, 0 );
6973 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6975 // Graph (connector network) layout
6976 {
6977 InkAction* inky = ink_action_new( "ConnectorGraphAction",
6978 _("Graph"),
6979 _("Nicely arrange selected connector network"),
6980 INKSCAPE_ICON_DISTRIBUTE_GRAPH,
6981 secondarySize );
6982 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
6983 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6984 }
6986 // Default connector length spinbox
6987 eact = create_adjustment_action( "ConnectorLengthAction",
6988 _("Connector Length"), _("Length:"),
6989 _("Ideal length for connectors when layout is applied"),
6990 "/tools/connector/length", 100,
6991 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
6992 10, 1000, 10.0, 100.0,
6993 0, 0, 0,
6994 connector_length_changed, 1, 0 );
6995 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6998 // Directed edges toggle button
6999 {
7000 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
7001 _("Downwards"),
7002 _("Make connectors with end-markers (arrows) point downwards"),
7003 INKSCAPE_ICON_DISTRIBUTE_GRAPH_DIRECTED,
7004 Inkscape::ICON_SIZE_DECORATION );
7005 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7007 bool tbuttonstate = prefs->getBool("/tools/connector/directedlayout");
7008 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7010 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
7011 }
7013 // Avoid overlaps toggle button
7014 {
7015 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
7016 _("Remove overlaps"),
7017 _("Do not allow overlapping shapes"),
7018 INKSCAPE_ICON_DISTRIBUTE_REMOVE_OVERLAPS,
7019 Inkscape::ICON_SIZE_DECORATION );
7020 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7022 bool tbuttonstate = prefs->getBool("/tools/connector/avoidoverlaplayout");
7023 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), (tbuttonstate ? TRUE : FALSE ));
7025 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
7026 }
7028 // Code to watch for changes to the connector-spacing attribute in
7029 // the XML.
7030 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7031 g_assert(repr != NULL);
7033 purge_repr_listener( holder, holder );
7035 if (repr) {
7036 g_object_set_data( holder, "repr", repr );
7037 Inkscape::GC::anchor(repr);
7038 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
7039 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
7040 }
7041 } // end of sp_connector_toolbox_prep()
7044 //#########################
7045 //## Paintbucket ##
7046 //#########################
7048 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
7049 {
7050 gint channels = ege_select_one_action_get_active( act );
7051 flood_channels_set_channels( channels );
7052 }
7054 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
7055 {
7056 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7057 prefs->setInt("/tools/paintbucket/threshold", (gint)adj->value);
7058 }
7060 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
7061 {
7062 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7063 prefs->setBool("/tools/paintbucket/autogap", ege_select_one_action_get_active( act ));
7064 }
7066 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
7067 {
7068 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
7069 SPUnit const *unit = tracker->getActiveUnit();
7070 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7072 prefs->setDouble("/tools/paintbucket/offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
7073 prefs->setString("/tools/paintbucket/offsetunits", sp_unit_get_abbreviation(unit));
7074 }
7076 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
7077 {
7078 // FIXME: make defaults settable via Inkscape Options
7079 struct KeyValue {
7080 char const *key;
7081 double value;
7082 } const key_values[] = {
7083 {"threshold", 15},
7084 {"offset", 0.0}
7085 };
7087 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
7088 KeyValue const &kv = key_values[i];
7089 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
7090 if ( adj ) {
7091 gtk_adjustment_set_value(adj, kv.value);
7092 }
7093 }
7095 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
7096 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
7097 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
7098 ege_select_one_action_set_active( autogap_action, 0 );
7099 }
7101 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
7102 {
7103 EgeAdjustmentAction* eact = 0;
7104 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7106 {
7107 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7109 GList* items = 0;
7110 gint count = 0;
7111 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
7112 {
7113 GtkTreeIter iter;
7114 gtk_list_store_append( model, &iter );
7115 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7116 count++;
7117 }
7118 g_list_free( items );
7119 items = 0;
7120 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
7121 g_object_set( act1, "short_label", _("Fill by:"), NULL );
7122 ege_select_one_action_set_appearance( act1, "compact" );
7123 ege_select_one_action_set_active( act1, prefs->getInt("/tools/paintbucket/channels", 0) );
7124 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
7125 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
7126 g_object_set_data( holder, "channels_action", act1 );
7127 }
7129 // Spacing spinbox
7130 {
7131 eact = create_adjustment_action(
7132 "ThresholdAction",
7133 _("Fill Threshold"), _("Threshold:"),
7134 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
7135 "/tools/paintbucket/threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
7136 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
7137 0, 0, 0,
7138 paintbucket_threshold_changed, 1, 0 );
7140 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
7141 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7142 }
7144 // Create the units menu.
7145 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
7146 Glib::ustring stored_unit = prefs->getString("/tools/paintbucket/offsetunits");
7147 if (!stored_unit.empty())
7148 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit.data()));
7149 g_object_set_data( holder, "tracker", tracker );
7150 {
7151 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
7152 gtk_action_group_add_action( mainActions, act );
7153 }
7155 // Offset spinbox
7156 {
7157 eact = create_adjustment_action(
7158 "OffsetAction",
7159 _("Grow/shrink by"), _("Grow/shrink by:"),
7160 _("The amount to grow (positive) or shrink (negative) the created fill path"),
7161 "/tools/paintbucket/offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
7162 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
7163 0, 0, 0,
7164 paintbucket_offset_changed, 1, 2);
7165 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
7167 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7168 }
7170 /* Auto Gap */
7171 {
7172 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7174 GList* items = 0;
7175 gint count = 0;
7176 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
7177 {
7178 GtkTreeIter iter;
7179 gtk_list_store_append( model, &iter );
7180 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7181 count++;
7182 }
7183 g_list_free( items );
7184 items = 0;
7185 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
7186 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
7187 ege_select_one_action_set_appearance( act2, "compact" );
7188 ege_select_one_action_set_active( act2, prefs->getBool("/tools/paintbucket/autogap") );
7189 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
7190 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
7191 g_object_set_data( holder, "autogap_action", act2 );
7192 }
7194 /* Reset */
7195 {
7196 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
7197 _("Defaults"),
7198 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
7199 GTK_STOCK_CLEAR );
7200 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
7201 gtk_action_group_add_action( mainActions, act );
7202 gtk_action_set_sensitive( act, TRUE );
7203 }
7205 }
7207 /*
7208 Local Variables:
7209 mode:c++
7210 c-file-style:"stroustrup"
7211 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
7212 indent-tabs-mode:nil
7213 fill-column:99
7214 End:
7215 */
7216 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :