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 * Tavmjong Bah <tavmjong@free.fr>
16 * Abhishek Sharma
17 *
18 * Copyright (C) 2004 David Turner
19 * Copyright (C) 2003 MenTaLguY
20 * Copyright (C) 1999-2010 authors
21 * Copyright (C) 2001-2002 Ximian, Inc.
22 *
23 * Released under GNU GPL, read the file 'COPYING' for more information
24 */
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
30 #include <cstring>
31 #include <string>
33 #include <gtkmm.h>
34 #include <gtk/gtk.h>
35 #include <iostream>
36 #include <sstream>
37 #include <glibmm/i18n.h>
39 #include "../box3d-context.h"
40 #include "../box3d.h"
41 #include "../conn-avoid-ref.h"
42 #include "../connection-pool.h"
43 #include "../connector-context.h"
44 #include "../desktop.h"
45 #include "../desktop-handles.h"
46 #include "../desktop-style.h"
47 #include "../dialogs/dialog-events.h"
48 #include "../dialogs/text-edit.h"
49 #include "../document-private.h"
50 #include "../ege-adjustment-action.h"
51 #include "../ege-output-action.h"
52 #include "../ege-select-one-action.h"
53 #include "../flood-context.h"
54 #include "gradient-toolbar.h"
55 #include "../graphlayout.h"
56 #include "../helper/unit-menu.h"
57 #include "../helper/units.h"
58 #include "../helper/unit-tracker.h"
59 #include "icon.h"
60 #include "../ink-action.h"
61 #include "../ink-comboboxentry-action.h"
62 #include "../inkscape.h"
63 #include "../interface.h"
64 #include "../libnrtype/font-instance.h"
65 #include "../libnrtype/font-lister.h"
66 #include "../live_effects/effect.h"
67 #include "../live_effects/lpe-angle_bisector.h"
68 #include "../live_effects/lpe-line_segment.h"
69 #include "../lpe-tool-context.h"
70 #include "../mod360.h"
71 #include "../pen-context.h"
72 #include "../preferences.h"
73 #include "../selection-chemistry.h"
74 #include "../selection.h"
75 #include "select-toolbar.h"
76 #include "../shape-editor.h"
77 #include "../shortcuts.h"
78 #include "../sp-clippath.h"
79 #include "../sp-ellipse.h"
80 #include "../sp-flowtext.h"
81 #include "../sp-mask.h"
82 #include "../sp-namedview.h"
83 #include "../sp-rect.h"
84 #include "../sp-spiral.h"
85 #include "../sp-star.h"
86 #include "../sp-text.h"
87 #include "../style.h"
88 #include "../svg/css-ostringstream.h"
89 #include "../text-context.h"
90 #include "../text-editing.h"
91 #include "../tools-switch.h"
92 #include "../tweak-context.h"
93 #include "../spray-context.h"
94 #include "../ui/dialog/calligraphic-profile-rename.h"
95 #include "../ui/icon-names.h"
96 #include "../ui/tool/control-point-selection.h"
97 #include "../ui/tool/node-tool.h"
98 #include "../ui/tool/multi-path-manipulator.h"
99 #include "../ui/widget/style-swatch.h"
100 #include "../verbs.h"
101 #include "../widgets/button.h"
102 #include "../widgets/spinbutton-events.h"
103 #include "../widgets/spw-utilities.h"
104 #include "../widgets/widget-sizes.h"
105 #include "../xml/attribute-record.h"
106 #include "../xml/node-event-vector.h"
107 #include "../xml/repr.h"
108 #include "ui/uxmanager.h"
110 #include "toolbox.h"
112 //#define DEBUG_TEXT
114 using Inkscape::UnitTracker;
115 using Inkscape::UI::UXManager;
116 using Inkscape::DocumentUndo;
118 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
119 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
121 enum BarId {
122 BAR_TOOL = 0,
123 BAR_AUX,
124 BAR_COMMANDS,
125 BAR_SNAP,
126 };
128 #define BAR_ID_KEY "BarIdValue"
129 #define HANDLE_POS_MARK "x-inkscape-pos"
131 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
132 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
133 static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
134 static void sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
135 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
136 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
137 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
138 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
139 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
140 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
141 static void sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
142 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
143 static void sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
144 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
145 static void sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
146 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
147 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
148 static void sp_text_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
149 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
151 using Inkscape::UI::ToolboxFactory;
154 Inkscape::IconSize ToolboxFactory::prefToSize( Glib::ustring const &path, int base ) {
155 static Inkscape::IconSize sizeChoices[] = {
156 Inkscape::ICON_SIZE_LARGE_TOOLBAR,
157 Inkscape::ICON_SIZE_SMALL_TOOLBAR,
158 Inkscape::ICON_SIZE_MENU
159 };
160 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
161 int index = prefs->getIntLimited( path, base, 0, G_N_ELEMENTS(sizeChoices) );
162 return sizeChoices[index];
163 }
165 static struct {
166 gchar const *type_name;
167 gchar const *data_name;
168 sp_verb_t verb;
169 sp_verb_t doubleclick_verb;
170 } const tools[] = {
171 { "SPSelectContext", "select_tool", SP_VERB_CONTEXT_SELECT, SP_VERB_CONTEXT_SELECT_PREFS},
172 { "InkNodeTool", "node_tool", SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
173 { "SPTweakContext", "tweak_tool", SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
174 { "SPSprayContext", "spray_tool", SP_VERB_CONTEXT_SPRAY, SP_VERB_CONTEXT_SPRAY_PREFS },
175 { "SPZoomContext", "zoom_tool", SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
176 { "SPRectContext", "rect_tool", SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
177 { "Box3DContext", "3dbox_tool", SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
178 { "SPArcContext", "arc_tool", SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
179 { "SPStarContext", "star_tool", SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
180 { "SPSpiralContext", "spiral_tool", SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
181 { "SPPencilContext", "pencil_tool", SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
182 { "SPPenContext", "pen_tool", SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
183 { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
184 { "SPLPEToolContext", "lpetool_tool", SP_VERB_CONTEXT_LPETOOL, SP_VERB_CONTEXT_LPETOOL_PREFS },
185 { "SPEraserContext", "eraser_tool", SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
186 { "SPFloodContext", "paintbucket_tool", SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
187 { "SPTextContext", "text_tool", SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
188 { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
189 { "SPGradientContext", "gradient_tool", SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
190 { "SPDropperContext", "dropper_tool", SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
191 { NULL, NULL, 0, 0 }
192 };
194 static struct {
195 gchar const *type_name;
196 gchar const *data_name;
197 GtkWidget *(*create_func)(SPDesktop *desktop);
198 void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
199 gchar const *ui_name;
200 gint swatch_verb_id;
201 gchar const *swatch_tool;
202 gchar const *swatch_tip;
203 } const aux_toolboxes[] = {
204 { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep, "SelectToolbar",
205 SP_VERB_INVALID, 0, 0},
206 { "InkNodeTool", "node_toolbox", 0, sp_node_toolbox_prep, "NodeToolbar",
207 SP_VERB_INVALID, 0, 0},
208 { "SPTweakContext", "tweak_toolbox", 0, sp_tweak_toolbox_prep, "TweakToolbar",
209 SP_VERB_CONTEXT_TWEAK_PREFS, "/tools/tweak", N_("Color/opacity used for color tweaking")},
210 { "SPSprayContext", "spray_toolbox", 0, sp_spray_toolbox_prep, "SprayToolbar",
211 SP_VERB_INVALID, 0, 0},
212 { "SPZoomContext", "zoom_toolbox", 0, sp_zoom_toolbox_prep, "ZoomToolbar",
213 SP_VERB_INVALID, 0, 0},
214 { "SPStarContext", "star_toolbox", 0, sp_star_toolbox_prep, "StarToolbar",
215 SP_VERB_CONTEXT_STAR_PREFS, "/tools/shapes/star", N_("Style of new stars")},
216 { "SPRectContext", "rect_toolbox", 0, sp_rect_toolbox_prep, "RectToolbar",
217 SP_VERB_CONTEXT_RECT_PREFS, "/tools/shapes/rect", N_("Style of new rectangles")},
218 { "Box3DContext", "3dbox_toolbox", 0, box3d_toolbox_prep, "3DBoxToolbar",
219 SP_VERB_CONTEXT_3DBOX_PREFS, "/tools/shapes/3dbox", N_("Style of new 3D boxes")},
220 { "SPArcContext", "arc_toolbox", 0, sp_arc_toolbox_prep, "ArcToolbar",
221 SP_VERB_CONTEXT_ARC_PREFS, "/tools/shapes/arc", N_("Style of new ellipses")},
222 { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep, "SpiralToolbar",
223 SP_VERB_CONTEXT_SPIRAL_PREFS, "/tools/shapes/spiral", N_("Style of new spirals")},
224 { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep, "PencilToolbar",
225 SP_VERB_CONTEXT_PENCIL_PREFS, "/tools/freehand/pencil", N_("Style of new paths created by Pencil")},
226 { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep, "PenToolbar",
227 SP_VERB_CONTEXT_PEN_PREFS, "/tools/freehand/pen", N_("Style of new paths created by Pen")},
228 { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
229 SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "/tools/calligraphic", N_("Style of new calligraphic strokes")},
230 { "SPEraserContext", "eraser_toolbox", 0, sp_eraser_toolbox_prep,"EraserToolbar",
231 SP_VERB_CONTEXT_ERASER_PREFS, "/tools/eraser", _("TBD")},
232 { "SPLPEToolContext", "lpetool_toolbox", 0, sp_lpetool_toolbox_prep, "LPEToolToolbar",
233 SP_VERB_CONTEXT_LPETOOL_PREFS, "/tools/lpetool", _("TBD")},
234 { "SPTextContext", "text_toolbox", 0, sp_text_toolbox_prep, "TextToolbar",
235 SP_VERB_INVALID, 0, 0},
236 { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep, "DropperToolbar",
237 SP_VERB_INVALID, 0, 0},
238 { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0, 0,
239 SP_VERB_INVALID, 0, 0},
240 { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep, "ConnectorToolbar",
241 SP_VERB_INVALID, 0, 0},
242 { "SPFloodContext", "paintbucket_toolbox", 0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
243 SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "/tools/paintbucket", N_("Style of Paint Bucket fill objects")},
244 { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
245 };
247 #define TOOLBAR_SLIDER_HINT "full"
249 static gchar const * ui_descr =
250 "<ui>"
251 " <toolbar name='SelectToolbar'>"
252 " <toolitem action='EditSelectAll' />"
253 " <toolitem action='EditSelectAllInAllLayers' />"
254 " <toolitem action='EditDeselect' />"
255 " <separator />"
256 " <toolitem action='ObjectRotate90CCW' />"
257 " <toolitem action='ObjectRotate90' />"
258 " <toolitem action='ObjectFlipHorizontally' />"
259 " <toolitem action='ObjectFlipVertically' />"
260 " <separator />"
261 " <toolitem action='SelectionToBack' />"
262 " <toolitem action='SelectionLower' />"
263 " <toolitem action='SelectionRaise' />"
264 " <toolitem action='SelectionToFront' />"
265 " <separator />"
266 " <toolitem action='XAction' />"
267 " <toolitem action='YAction' />"
268 " <toolitem action='WidthAction' />"
269 " <toolitem action='LockAction' />"
270 " <toolitem action='HeightAction' />"
271 " <toolitem action='UnitsAction' />"
272 " <separator />"
273 " <toolitem action='transform_affect_label' />"
274 " <toolitem action='transform_stroke' />"
275 " <toolitem action='transform_corners' />"
276 " <toolitem action='transform_gradient' />"
277 " <toolitem action='transform_pattern' />"
278 " </toolbar>"
280 " <toolbar name='NodeToolbar'>"
281 " <separator />"
282 " <toolitem action='NodeInsertAction' />"
283 " <toolitem action='NodeDeleteAction' />"
284 " <separator />"
285 " <toolitem action='NodeJoinAction' />"
286 " <toolitem action='NodeBreakAction' />"
287 " <separator />"
288 " <toolitem action='NodeJoinSegmentAction' />"
289 " <toolitem action='NodeDeleteSegmentAction' />"
290 " <separator />"
291 " <toolitem action='NodeCuspAction' />"
292 " <toolitem action='NodeSmoothAction' />"
293 " <toolitem action='NodeSymmetricAction' />"
294 " <toolitem action='NodeAutoAction' />"
295 " <separator />"
296 " <toolitem action='NodeLineAction' />"
297 " <toolitem action='NodeCurveAction' />"
298 " <separator />"
299 " <toolitem action='ObjectToPath' />"
300 " <toolitem action='StrokeToPath' />"
301 " <separator />"
302 " <toolitem action='NodeXAction' />"
303 " <toolitem action='NodeYAction' />"
304 " <toolitem action='NodeUnitsAction' />"
305 " <separator />"
306 " <toolitem action='ObjectEditClipPathAction' />"
307 " <toolitem action='ObjectEditMaskPathAction' />"
308 " <toolitem action='EditNextPathEffectParameter' />"
309 " <separator />"
310 " <toolitem action='NodesShowTransformHandlesAction' />"
311 " <toolitem action='NodesShowHandlesAction' />"
312 " <toolitem action='NodesShowHelperpath' />"
313 " </toolbar>"
315 " <toolbar name='TweakToolbar'>"
316 " <toolitem action='TweakWidthAction' />"
317 " <separator />"
318 " <toolitem action='TweakForceAction' />"
319 " <toolitem action='TweakPressureAction' />"
320 " <separator />"
321 " <toolitem action='TweakModeAction' />"
322 " <separator />"
323 " <toolitem action='TweakFidelityAction' />"
324 " <separator />"
325 " <toolitem action='TweakChannelsLabel' />"
326 " <toolitem action='TweakDoH' />"
327 " <toolitem action='TweakDoS' />"
328 " <toolitem action='TweakDoL' />"
329 " <toolitem action='TweakDoO' />"
330 " </toolbar>"
332 " <toolbar name='SprayToolbar'>"
333 " <toolitem action='SprayModeAction' />"
334 " <separator />"
335 " <separator />"
336 " <toolitem action='SprayWidthAction' />"
337 " <toolitem action='SprayPressureAction' />"
338 " <toolitem action='SprayPopulationAction' />"
339 " <separator />"
340 " <toolitem action='SprayRotationAction' />"
341 " <toolitem action='SprayScaleAction' />"
342 " <separator />"
343 " <toolitem action='SprayStandard_deviationAction' />"
344 " <toolitem action='SprayMeanAction' />"
345 " </toolbar>"
347 " <toolbar name='ZoomToolbar'>"
348 " <toolitem action='ZoomIn' />"
349 " <toolitem action='ZoomOut' />"
350 " <separator />"
351 " <toolitem action='Zoom1:0' />"
352 " <toolitem action='Zoom1:2' />"
353 " <toolitem action='Zoom2:1' />"
354 " <separator />"
355 " <toolitem action='ZoomSelection' />"
356 " <toolitem action='ZoomDrawing' />"
357 " <toolitem action='ZoomPage' />"
358 " <toolitem action='ZoomPageWidth' />"
359 " <separator />"
360 " <toolitem action='ZoomPrev' />"
361 " <toolitem action='ZoomNext' />"
362 " </toolbar>"
364 " <toolbar name='StarToolbar'>"
365 " <separator />"
366 " <toolitem action='StarStateAction' />"
367 " <separator />"
368 " <toolitem action='FlatAction' />"
369 " <separator />"
370 " <toolitem action='MagnitudeAction' />"
371 " <toolitem action='SpokeAction' />"
372 " <toolitem action='RoundednessAction' />"
373 " <toolitem action='RandomizationAction' />"
374 " <separator />"
375 " <toolitem action='StarResetAction' />"
376 " </toolbar>"
378 " <toolbar name='RectToolbar'>"
379 " <toolitem action='RectStateAction' />"
380 " <toolitem action='RectWidthAction' />"
381 " <toolitem action='RectHeightAction' />"
382 " <toolitem action='RadiusXAction' />"
383 " <toolitem action='RadiusYAction' />"
384 " <toolitem action='RectUnitsAction' />"
385 " <separator />"
386 " <toolitem action='RectResetAction' />"
387 " </toolbar>"
389 " <toolbar name='3DBoxToolbar'>"
390 " <toolitem action='3DBoxAngleXAction' />"
391 " <toolitem action='3DBoxVPXStateAction' />"
392 " <separator />"
393 " <toolitem action='3DBoxAngleYAction' />"
394 " <toolitem action='3DBoxVPYStateAction' />"
395 " <separator />"
396 " <toolitem action='3DBoxAngleZAction' />"
397 " <toolitem action='3DBoxVPZStateAction' />"
398 " </toolbar>"
400 " <toolbar name='SpiralToolbar'>"
401 " <toolitem action='SpiralStateAction' />"
402 " <toolitem action='SpiralRevolutionAction' />"
403 " <toolitem action='SpiralExpansionAction' />"
404 " <toolitem action='SpiralT0Action' />"
405 " <separator />"
406 " <toolitem action='SpiralResetAction' />"
407 " </toolbar>"
409 " <toolbar name='PenToolbar'>"
410 " <toolitem action='FreehandModeActionPen' />"
411 " <separator />"
412 " <toolitem action='SetPenShapeAction'/>"
413 " </toolbar>"
415 " <toolbar name='PencilToolbar'>"
416 " <toolitem action='FreehandModeActionPencil' />"
417 " <separator />"
418 " <toolitem action='PencilToleranceAction' />"
419 " <separator />"
420 " <toolitem action='PencilResetAction' />"
421 " <separator />"
422 " <toolitem action='SetPencilShapeAction'/>"
423 " </toolbar>"
425 " <toolbar name='CalligraphyToolbar'>"
426 " <separator />"
427 " <toolitem action='SetProfileAction'/>"
428 " <separator />"
429 " <toolitem action='CalligraphyWidthAction' />"
430 " <toolitem action='PressureAction' />"
431 " <toolitem action='TraceAction' />"
432 " <toolitem action='ThinningAction' />"
433 " <separator />"
434 " <toolitem action='AngleAction' />"
435 " <toolitem action='TiltAction' />"
436 " <toolitem action='FixationAction' />"
437 " <separator />"
438 " <toolitem action='CapRoundingAction' />"
439 " <separator />"
440 " <toolitem action='TremorAction' />"
441 " <toolitem action='WiggleAction' />"
442 " <toolitem action='MassAction' />"
443 " <separator />"
444 " </toolbar>"
446 " <toolbar name='ArcToolbar'>"
447 " <toolitem action='ArcStateAction' />"
448 " <separator />"
449 " <toolitem action='ArcStartAction' />"
450 " <toolitem action='ArcEndAction' />"
451 " <separator />"
452 " <toolitem action='ArcOpenAction' />"
453 " <separator />"
454 " <toolitem action='ArcResetAction' />"
455 " <separator />"
456 " </toolbar>"
458 " <toolbar name='PaintbucketToolbar'>"
459 " <toolitem action='ChannelsAction' />"
460 " <separator />"
461 " <toolitem action='ThresholdAction' />"
462 " <separator />"
463 " <toolitem action='OffsetAction' />"
464 " <toolitem action='PaintbucketUnitsAction' />"
465 " <separator />"
466 " <toolitem action='AutoGapAction' />"
467 " <separator />"
468 " <toolitem action='PaintbucketResetAction' />"
469 " </toolbar>"
471 " <toolbar name='EraserToolbar'>"
472 " <toolitem action='EraserWidthAction' />"
473 " <separator />"
474 " <toolitem action='EraserModeAction' />"
475 " </toolbar>"
477 " <toolbar name='TextToolbar'>"
478 " <toolitem action='TextFontFamilyAction' />"
479 " <toolitem action='TextFontSizeAction' />"
480 " <toolitem action='TextBoldAction' />"
481 " <toolitem action='TextItalicAction' />"
482 " <separator />"
483 " <toolitem action='TextAlignAction' />"
484 " <separator />"
485 " <toolitem action='TextSuperscriptAction' />"
486 " <toolitem action='TextSubscriptAction' />"
487 " <separator />"
488 " <toolitem action='TextLineHeightAction' />"
489 " <toolitem action='TextLetterSpacingAction' />"
490 " <toolitem action='TextWordSpacingAction' />"
491 " <toolitem action='TextDxAction' />"
492 " <toolitem action='TextDyAction' />"
493 " <toolitem action='TextRotationAction' />"
494 " <separator />"
495 " <toolitem action='TextOrientationAction' />"
496 " </toolbar>"
498 " <toolbar name='LPEToolToolbar'>"
499 " <toolitem action='LPEToolModeAction' />"
500 " <separator />"
501 " <toolitem action='LPEShowBBoxAction' />"
502 " <toolitem action='LPEBBoxFromSelectionAction' />"
503 " <separator />"
504 " <toolitem action='LPELineSegmentAction' />"
505 " <separator />"
506 " <toolitem action='LPEMeasuringAction' />"
507 " <toolitem action='LPEToolUnitsAction' />"
508 " <separator />"
509 " <toolitem action='LPEOpenLPEDialogAction' />"
510 " </toolbar>"
512 " <toolbar name='DropperToolbar'>"
513 " <toolitem action='DropperOpacityAction' />"
514 " <toolitem action='DropperPickAlphaAction' />"
515 " <toolitem action='DropperSetAlphaAction' />"
516 " </toolbar>"
518 " <toolbar name='ConnectorToolbar'>"
519 // " <toolitem action='ConnectorEditModeAction' />"
520 " <toolitem action='ConnectorAvoidAction' />"
521 " <toolitem action='ConnectorIgnoreAction' />"
522 " <toolitem action='ConnectorOrthogonalAction' />"
523 " <toolitem action='ConnectorCurvatureAction' />"
524 " <toolitem action='ConnectorSpacingAction' />"
525 " <toolitem action='ConnectorGraphAction' />"
526 " <toolitem action='ConnectorLengthAction' />"
527 " <toolitem action='ConnectorDirectedAction' />"
528 " <toolitem action='ConnectorOverlapAction' />"
529 // " <toolitem action='ConnectorNewConnPointAction' />"
530 // " <toolitem action='ConnectorRemoveConnPointAction' />"
531 " </toolbar>"
533 "</ui>"
534 ;
536 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
538 static void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop);
540 static void setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop);
541 static void update_tool_toolbox(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
543 static void setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop);
544 static void update_aux_toolbox(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
546 static void setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop);
547 static void update_commands_toolbox(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
549 static GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick( GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
550 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
551 Inkscape::UI::View::View *view, GtkTooltips *tt);
553 class VerbAction : public Gtk::Action {
554 public:
555 static Glib::RefPtr<VerbAction> create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
557 virtual ~VerbAction();
558 virtual void set_active(bool active = true);
560 protected:
561 virtual Gtk::Widget* create_menu_item_vfunc();
562 virtual Gtk::Widget* create_tool_item_vfunc();
564 virtual void connect_proxy_vfunc(Gtk::Widget* proxy);
565 virtual void disconnect_proxy_vfunc(Gtk::Widget* proxy);
567 virtual void on_activate();
569 private:
570 Inkscape::Verb* verb;
571 Inkscape::Verb* verb2;
572 Inkscape::UI::View::View *view;
573 GtkTooltips *tooltips;
574 bool active;
576 VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
577 };
580 Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips)
581 {
582 Glib::RefPtr<VerbAction> result;
583 SPAction *action = verb->get_action(view);
584 if ( action ) {
585 //SPAction* action2 = verb2 ? verb2->get_action(view) : 0;
586 result = Glib::RefPtr<VerbAction>(new VerbAction(verb, verb2, view, tooltips));
587 }
589 return result;
590 }
592 VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips) :
593 Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(verb->get_image()), Glib::ustring(_(verb->get_name())), Glib::ustring(_(verb->get_tip()))),
594 verb(verb),
595 verb2(verb2),
596 view(view),
597 tooltips(tooltips),
598 active(false)
599 {
600 }
602 VerbAction::~VerbAction()
603 {
604 }
606 Gtk::Widget* VerbAction::create_menu_item_vfunc()
607 {
608 // First call in to get the icon rendered if present in SVG
609 Gtk::Widget *widget = sp_icon_get_icon( property_stock_id().get_value().get_string(), Inkscape::ICON_SIZE_MENU );
610 delete widget;
611 widget = 0;
613 Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
614 // g_message("create_menu_item_vfunc() = %p for '%s'", widg, verb->get_id());
615 return widg;
616 }
618 Gtk::Widget* VerbAction::create_tool_item_vfunc()
619 {
620 // Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
621 Inkscape::IconSize toolboxSize = ToolboxFactory::prefToSize("/toolbox/tools/small");
622 GtkWidget* toolbox = 0;
623 GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
624 SP_BUTTON_TYPE_TOGGLE,
625 verb,
626 verb2,
627 view,
628 tooltips );
629 if ( active ) {
630 sp_button_toggle_set_down( SP_BUTTON(button), active);
631 }
632 gtk_widget_show_all( button );
633 Gtk::Widget* wrapped = Glib::wrap(button);
634 Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
635 holder->add(*wrapped);
637 // g_message("create_tool_item_vfunc() = %p for '%s'", holder, verb->get_id());
638 return holder;
639 }
641 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
642 {
643 // g_message("connect_proxy_vfunc(%p) for '%s'", proxy, verb->get_id());
644 Gtk::Action::connect_proxy_vfunc(proxy);
645 }
647 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
648 {
649 // g_message("disconnect_proxy_vfunc(%p) for '%s'", proxy, verb->get_id());
650 Gtk::Action::disconnect_proxy_vfunc(proxy);
651 }
653 void VerbAction::set_active(bool active)
654 {
655 this->active = active;
656 Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
657 for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
658 Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
659 if (ti) {
660 // *should* have one child that is the SPButton
661 Gtk::Widget* child = ti->get_child();
662 if ( child && SP_IS_BUTTON(child->gobj()) ) {
663 SPButton* button = SP_BUTTON(child->gobj());
664 sp_button_toggle_set_down( button, active );
665 }
666 }
667 }
668 }
670 void VerbAction::on_activate()
671 {
672 if ( verb ) {
673 SPAction *action = verb->get_action(view);
674 if ( action ) {
675 sp_action_perform(action, 0);
676 }
677 }
678 }
680 /* Global text entry widgets necessary for update */
681 /* GtkWidget *dropper_rgb_entry,
682 *dropper_opacity_entry ; */
683 // should be made a private member once this is converted to class
685 static void delete_connection(GObject * /*obj*/, sigc::connection *connection)
686 {
687 connection->disconnect();
688 delete connection;
689 }
691 static void purge_repr_listener( GObject* /*obj*/, GObject* tbl )
692 {
693 Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
694 if (oldrepr) { // remove old listener
695 sp_repr_remove_listener_by_data(oldrepr, tbl);
696 Inkscape::GC::release(oldrepr);
697 oldrepr = 0;
698 g_object_set_data( tbl, "repr", NULL );
699 }
700 }
702 // ------------------------------------------------------
704 /**
705 * A simple mediator class that keeps UI controls matched to the preference values they set.
706 */
707 class PrefPusher : public Inkscape::Preferences::Observer
708 {
709 public:
710 /**
711 * Constructor for a boolean value that syncs to the supplied path.
712 * Initializes the widget to the current preference stored state and registers callbacks
713 * for widget changes and preference changes.
714 *
715 * @param act the widget to synchronize preference with.
716 * @param path the path to the preference the widget is synchronized with.
717 * @param callback function to invoke when changes are pushed.
718 * @param cbData data to be passed on to the callback function.
719 */
720 PrefPusher( GtkToggleAction *act, Glib::ustring const &path, void (*callback)(GObject*) = 0, GObject *cbData = 0 );
722 /**
723 * Destructor that unregisters the preference callback.
724 */
725 virtual ~PrefPusher();
727 /**
728 * Callback method invoked when the preference setting changes.
729 */
730 virtual void notify(Inkscape::Preferences::Entry const &new_val);
732 private:
733 /**
734 * Callback hook invoked when the widget changes.
735 *
736 * @param act the toggle action widget that was changed.
737 * @param self the PrefPusher instance the callback was registered to.
738 */
739 static void toggleCB( GtkToggleAction *act, PrefPusher *self );
741 /**
742 * Method to handle the widget change.
743 */
744 void handleToggled();
746 GtkToggleAction *act;
747 void (*callback)(GObject*);
748 GObject *cbData;
749 bool freeze;
750 };
752 PrefPusher::PrefPusher( GtkToggleAction *act, Glib::ustring const &path, void (*callback)(GObject*), GObject *cbData ) :
753 Observer(path),
754 act(act),
755 callback(callback),
756 cbData(cbData),
757 freeze(false)
758 {
759 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggleCB), this);
760 freeze = true;
761 gtk_toggle_action_set_active( act, Inkscape::Preferences::get()->getBool(observed_path) );
762 freeze = false;
764 Inkscape::Preferences::get()->addObserver(*this);
765 }
767 PrefPusher::~PrefPusher()
768 {
769 Inkscape::Preferences::get()->removeObserver(*this);
770 }
772 void PrefPusher::toggleCB( GtkToggleAction * /*act*/, PrefPusher *self )
773 {
774 if (self) {
775 self->handleToggled();
776 }
777 }
779 void PrefPusher::handleToggled()
780 {
781 if (!freeze) {
782 freeze = true;
783 Inkscape::Preferences::get()->setBool(observed_path, gtk_toggle_action_get_active( act ));
784 if (callback) {
785 (*callback)(cbData);
786 }
787 freeze = false;
788 }
789 }
791 void PrefPusher::notify(Inkscape::Preferences::Entry const &newVal)
792 {
793 bool newBool = newVal.getBool();
794 bool oldBool = gtk_toggle_action_get_active(act);
796 if (!freeze && (newBool != oldBool)) {
797 gtk_toggle_action_set_active( act, newBool );
798 }
799 }
801 static void delete_prefspusher(GtkObject * /*obj*/, PrefPusher *watcher )
802 {
803 delete watcher;
804 }
806 // ------------------------------------------------------
809 GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
810 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
811 Inkscape::UI::View::View *view, GtkTooltips *tt)
812 {
813 SPAction *action = verb->get_action(view);
814 if (!action) {
815 return NULL;
816 }
818 SPAction *doubleclick_action;
819 if (doubleclick_verb) {
820 doubleclick_action = doubleclick_verb->get_action(view);
821 } else {
822 doubleclick_action = NULL;
823 }
825 /* fixme: Handle sensitive/unsensitive */
826 /* fixme: Implement sp_button_new_from_action */
827 GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
828 gtk_widget_show(b);
831 unsigned int shortcut = sp_shortcut_get_primary(verb);
832 if (shortcut != GDK_VoidSymbol) {
833 gchar *key = sp_shortcut_get_label(shortcut);
834 gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
835 if ( t ) {
836 gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
837 }
838 g_free(tip);
839 g_free(key);
840 } else {
841 if ( t ) {
842 gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
843 }
844 }
846 return b;
847 }
850 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
851 {
852 SPAction* targetAction = SP_ACTION(user_data);
853 if ( targetAction ) {
854 sp_action_perform( targetAction, NULL );
855 }
856 }
858 static void sp_action_action_set_sensitive(SPAction * /*action*/, unsigned int sensitive, void *data)
859 {
860 if ( data ) {
861 GtkAction* act = GTK_ACTION(data);
862 gtk_action_set_sensitive( act, sensitive );
863 }
864 }
866 static SPActionEventVector action_event_vector = {
867 {NULL},
868 NULL,
869 NULL,
870 sp_action_action_set_sensitive,
871 NULL,
872 NULL
873 };
875 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
876 {
877 GtkAction* act = 0;
879 SPAction* targetAction = verb->get_action(view);
880 InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size );
881 act = GTK_ACTION(inky);
882 gtk_action_set_sensitive( act, targetAction->sensitive );
884 g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
886 SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
887 nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
889 return act;
890 }
892 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
893 {
894 Inkscape::UI::View::View *view = desktop;
895 gint verbsToUse[] = {
896 // disabled until we have icons for them:
897 //find
898 //SP_VERB_EDIT_TILE,
899 //SP_VERB_EDIT_UNTILE,
900 SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
901 SP_VERB_DIALOG_DISPLAY,
902 SP_VERB_DIALOG_FILL_STROKE,
903 SP_VERB_DIALOG_NAMEDVIEW,
904 SP_VERB_DIALOG_TEXT,
905 SP_VERB_DIALOG_XML_EDITOR,
906 SP_VERB_DIALOG_LAYERS,
907 SP_VERB_EDIT_CLONE,
908 SP_VERB_EDIT_COPY,
909 SP_VERB_EDIT_CUT,
910 SP_VERB_EDIT_DUPLICATE,
911 SP_VERB_EDIT_PASTE,
912 SP_VERB_EDIT_REDO,
913 SP_VERB_EDIT_UNDO,
914 SP_VERB_EDIT_UNLINK_CLONE,
915 SP_VERB_FILE_EXPORT,
916 SP_VERB_FILE_IMPORT,
917 SP_VERB_FILE_NEW,
918 SP_VERB_FILE_OPEN,
919 SP_VERB_FILE_PRINT,
920 SP_VERB_FILE_SAVE,
921 SP_VERB_OBJECT_TO_CURVE,
922 SP_VERB_SELECTION_GROUP,
923 SP_VERB_SELECTION_OUTLINE,
924 SP_VERB_SELECTION_UNGROUP,
925 SP_VERB_ZOOM_1_1,
926 SP_VERB_ZOOM_1_2,
927 SP_VERB_ZOOM_2_1,
928 SP_VERB_ZOOM_DRAWING,
929 SP_VERB_ZOOM_IN,
930 SP_VERB_ZOOM_NEXT,
931 SP_VERB_ZOOM_OUT,
932 SP_VERB_ZOOM_PAGE,
933 SP_VERB_ZOOM_PAGE_WIDTH,
934 SP_VERB_ZOOM_PREV,
935 SP_VERB_ZOOM_SELECTION,
936 };
938 Inkscape::IconSize toolboxSize = ToolboxFactory::prefToSize("/toolbox/small");
940 static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
941 Glib::RefPtr<Gtk::ActionGroup> mainActions;
942 if ( groups.find(desktop) != groups.end() ) {
943 mainActions = groups[desktop];
944 }
946 if ( !mainActions ) {
947 mainActions = Gtk::ActionGroup::create("main");
948 groups[desktop] = mainActions;
949 }
951 for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
952 Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
953 if ( verb ) {
954 if (!mainActions->get_action(verb->get_id())) {
955 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
956 mainActions->add(Glib::wrap(act));
957 }
958 }
959 }
961 if ( !mainActions->get_action("ToolZoom") ) {
962 GtkTooltips *tt = gtk_tooltips_new();
963 for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
964 Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
965 if ( va ) {
966 mainActions->add(va);
967 if ( i == 0 ) {
968 va->set_active(true);
969 }
970 }
971 }
972 }
974 return mainActions;
975 }
978 static void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
979 {
980 gtk_widget_set_size_request( widget,
981 widget->allocation.width,
982 widget->allocation.height );
983 }
985 static void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
986 {
987 gtk_widget_set_size_request( widget, -1, -1 );
988 }
990 static GtkWidget* toolboxNewCommon( GtkWidget* tb, BarId id, GtkPositionType handlePos )
991 {
992 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
994 gtk_widget_set_sensitive(tb, FALSE);
996 GtkWidget *hb = 0;
997 gboolean forceFloatAllowed = Inkscape::Preferences::get()->getBool("/options/workarounds/floatallowed", false);
998 if ( UXManager::getInstance()->isFloatWindowProblem() && !forceFloatAllowed ) {
999 hb = gtk_event_box_new(); // A simple, neutral container.
1000 } else {
1001 hb = gtk_handle_box_new();
1002 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), handlePos);
1003 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
1004 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
1005 }
1007 gtk_container_add(GTK_CONTAINER(hb), tb);
1008 gtk_widget_show(GTK_WIDGET(tb));
1010 sigc::connection* conn = new sigc::connection;
1011 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
1013 if ( GTK_IS_HANDLE_BOX(hb) ) {
1014 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
1015 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
1016 }
1018 gpointer val = GINT_TO_POINTER(id);
1019 g_object_set_data(G_OBJECT(hb), BAR_ID_KEY, val);
1021 return hb;
1022 }
1024 GtkWidget *ToolboxFactory::createToolToolbox()
1025 {
1026 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
1028 return toolboxNewCommon( tb, BAR_TOOL, GTK_POS_TOP );
1029 }
1031 GtkWidget *ToolboxFactory::createAuxToolbox()
1032 {
1033 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
1035 return toolboxNewCommon( tb, BAR_AUX, GTK_POS_LEFT );
1036 }
1038 //####################################
1039 //# Commands Bar
1040 //####################################
1042 GtkWidget *ToolboxFactory::createCommandsToolbox()
1043 {
1044 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
1046 return toolboxNewCommon( tb, BAR_COMMANDS, GTK_POS_LEFT );
1047 }
1049 GtkWidget *ToolboxFactory::createSnapToolbox()
1050 {
1051 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
1053 return toolboxNewCommon( tb, BAR_SNAP, GTK_POS_LEFT );
1054 }
1056 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
1057 gchar const *label, gchar const *shortLabel, gchar const *tooltip,
1058 Glib::ustring const &path, gdouble def,
1059 GtkWidget *focusTarget,
1060 GtkWidget *us,
1061 GObject *dataKludge,
1062 gboolean altx, gchar const *altx_mark,
1063 gdouble lower, gdouble upper, gdouble step, gdouble page,
1064 gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
1065 void (*callback)(GtkAdjustment *, GObject *),
1066 gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
1067 {
1068 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1069 GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs->getDouble(path, def) * factor,
1070 lower, upper, step, page, 0 ) );
1071 if (us) {
1072 sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
1073 }
1075 gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
1077 EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
1078 if ( shortLabel ) {
1079 g_object_set( act, "short_label", shortLabel, NULL );
1080 }
1082 if ( (descrCount > 0) && descrLabels && descrValues ) {
1083 ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
1084 }
1086 if ( focusTarget ) {
1087 ege_adjustment_action_set_focuswidget( act, focusTarget );
1088 }
1090 if ( altx && altx_mark ) {
1091 g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
1092 }
1094 if ( dataKludge ) {
1095 // Rather lame, but it's the only place where we need to get the entry name
1096 // but we don't have an Entry
1097 g_object_set_data( dataKludge, prefs->getEntry(path).getEntryName().data(), adj );
1098 }
1100 // Using a cast just to make sure we pass in the right kind of function pointer
1101 g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
1103 return act;
1104 }
1107 //####################################
1108 //# node editing callbacks
1109 //####################################
1111 /** Temporary hack: Returns the node tool in the active desktop.
1112 * Will go away during tool refactoring. */
1113 static InkNodeTool *get_node_tool()
1114 {
1115 InkNodeTool *tool = 0;
1116 if (SP_ACTIVE_DESKTOP ) {
1117 SPEventContext *ec = SP_ACTIVE_DESKTOP->event_context;
1118 if (INK_IS_NODE_TOOL(ec)) {
1119 tool = static_cast<InkNodeTool*>(ec);
1120 }
1121 }
1122 return tool;
1123 }
1125 static void sp_node_path_edit_add(void)
1126 {
1127 InkNodeTool *nt = get_node_tool();
1128 if (nt) {
1129 nt->_multipath->insertNodes();
1130 }
1131 }
1133 static void sp_node_path_edit_delete(void)
1134 {
1135 InkNodeTool *nt = get_node_tool();
1136 if (nt) {
1137 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1138 nt->_multipath->deleteNodes(prefs->getBool("/tools/nodes/delete_preserves_shape", true));
1139 }
1140 }
1142 static void sp_node_path_edit_delete_segment(void)
1143 {
1144 InkNodeTool *nt = get_node_tool();
1145 if (nt) {
1146 nt->_multipath->deleteSegments();
1147 }
1148 }
1150 static void sp_node_path_edit_break(void)
1151 {
1152 InkNodeTool *nt = get_node_tool();
1153 if (nt) {
1154 nt->_multipath->breakNodes();
1155 }
1156 }
1158 static void sp_node_path_edit_join(void)
1159 {
1160 InkNodeTool *nt = get_node_tool();
1161 if (nt) {
1162 nt->_multipath->joinNodes();
1163 }
1164 }
1166 static void sp_node_path_edit_join_segment(void)
1167 {
1168 InkNodeTool *nt = get_node_tool();
1169 if (nt) {
1170 nt->_multipath->joinSegments();
1171 }
1172 }
1174 static void sp_node_path_edit_toline(void)
1175 {
1176 InkNodeTool *nt = get_node_tool();
1177 if (nt) {
1178 nt->_multipath->setSegmentType(Inkscape::UI::SEGMENT_STRAIGHT);
1179 }
1180 }
1182 static void sp_node_path_edit_tocurve(void)
1183 {
1184 InkNodeTool *nt = get_node_tool();
1185 if (nt) {
1186 nt->_multipath->setSegmentType(Inkscape::UI::SEGMENT_CUBIC_BEZIER);
1187 }
1188 }
1190 static void sp_node_path_edit_cusp(void)
1191 {
1192 InkNodeTool *nt = get_node_tool();
1193 if (nt) {
1194 nt->_multipath->setNodeType(Inkscape::UI::NODE_CUSP);
1195 }
1196 }
1198 static void sp_node_path_edit_smooth(void)
1199 {
1200 InkNodeTool *nt = get_node_tool();
1201 if (nt) {
1202 nt->_multipath->setNodeType(Inkscape::UI::NODE_SMOOTH);
1203 }
1204 }
1206 static void sp_node_path_edit_symmetrical(void)
1207 {
1208 InkNodeTool *nt = get_node_tool();
1209 if (nt) {
1210 nt->_multipath->setNodeType(Inkscape::UI::NODE_SYMMETRIC);
1211 }
1212 }
1214 static void sp_node_path_edit_auto(void)
1215 {
1216 InkNodeTool *nt = get_node_tool();
1217 if (nt) {
1218 nt->_multipath->setNodeType(Inkscape::UI::NODE_AUTO);
1219 }
1220 }
1222 static void sp_node_path_edit_nextLPEparam(GtkAction * /*act*/, gpointer data) {
1223 sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1224 }
1226 /* is called when the node selection is modified */
1227 static void sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1228 {
1229 GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1230 GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1231 GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1232 GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1234 // quit if run by the attr_changed listener
1235 if (g_object_get_data( tbl, "freeze" )) {
1236 return;
1237 }
1239 // in turn, prevent listener from responding
1240 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1242 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1243 SPUnit const *unit = tracker->getActiveUnit();
1245 InkNodeTool *nt = get_node_tool();
1246 if (!nt || nt->_selected_nodes->empty()) {
1247 // no path selected
1248 gtk_action_set_sensitive(xact, FALSE);
1249 gtk_action_set_sensitive(yact, FALSE);
1250 } else {
1251 gtk_action_set_sensitive(xact, TRUE);
1252 gtk_action_set_sensitive(yact, TRUE);
1253 Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1254 Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1255 Geom::Point mid = nt->_selected_nodes->pointwiseBounds()->midpoint();
1257 if (oldx != mid[Geom::X]) {
1258 gtk_adjustment_set_value(xadj, sp_pixels_get_units(mid[Geom::X], *unit));
1259 }
1260 if (oldy != mid[Geom::Y]) {
1261 gtk_adjustment_set_value(yadj, sp_pixels_get_units(mid[Geom::Y], *unit));
1262 }
1263 }
1265 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1266 }
1268 static void sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, Geom::Dim2 d)
1269 {
1270 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1271 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1273 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1274 SPUnit const *unit = tracker->getActiveUnit();
1276 if (DocumentUndo::getUndoSensitive(sp_desktop_document(desktop))) {
1277 prefs->setDouble(Glib::ustring("/tools/nodes/") + (d == Geom::X ? "x" : "y"),
1278 sp_units_get_pixels(adj->value, *unit));
1279 }
1281 // quit if run by the attr_changed listener
1282 if (g_object_get_data( tbl, "freeze" )) {
1283 return;
1284 }
1286 // in turn, prevent listener from responding
1287 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1289 InkNodeTool *nt = get_node_tool();
1290 if (nt && !nt->_selected_nodes->empty()) {
1291 double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1292 double oldval = nt->_selected_nodes->pointwiseBounds()->midpoint()[d];
1293 Geom::Point delta(0,0);
1294 delta[d] = val - oldval;
1295 nt->_multipath->move(delta);
1296 }
1298 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1299 }
1301 static void sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1302 {
1303 sp_node_path_value_changed(adj, tbl, Geom::X);
1304 }
1306 static void sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1307 {
1308 sp_node_path_value_changed(adj, tbl, Geom::Y);
1309 }
1311 static void sp_node_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
1312 {
1313 {
1314 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1315 SPItem *item = selection->singleItem();
1316 if (item && SP_IS_LPE_ITEM(item)) {
1317 if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1318 gtk_action_set_sensitive(w, TRUE);
1319 } else {
1320 gtk_action_set_sensitive(w, FALSE);
1321 }
1322 } else {
1323 gtk_action_set_sensitive(w, FALSE);
1324 }
1325 }
1326 }
1328 static void sp_node_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1329 {
1330 sp_node_toolbox_sel_changed (selection, tbl);
1331 }
1335 //################################
1336 //## Node Editing Toolbox ##
1337 //################################
1339 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1340 {
1341 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1342 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1343 g_object_set_data( holder, "tracker", tracker );
1345 Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
1347 {
1348 InkAction* inky = ink_action_new( "NodeInsertAction",
1349 _("Insert node"),
1350 _("Insert new nodes into selected segments"),
1351 INKSCAPE_ICON_NODE_ADD,
1352 secondarySize );
1353 g_object_set( inky, "short_label", _("Insert"), NULL );
1354 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1355 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1356 }
1358 {
1359 InkAction* inky = ink_action_new( "NodeDeleteAction",
1360 _("Delete node"),
1361 _("Delete selected nodes"),
1362 INKSCAPE_ICON_NODE_DELETE,
1363 secondarySize );
1364 g_object_set( inky, "short_label", _("Delete"), NULL );
1365 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1366 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1367 }
1369 {
1370 InkAction* inky = ink_action_new( "NodeJoinAction",
1371 _("Join nodes"),
1372 _("Join selected nodes"),
1373 INKSCAPE_ICON_NODE_JOIN,
1374 secondarySize );
1375 g_object_set( inky, "short_label", _("Join"), NULL );
1376 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1377 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1378 }
1380 {
1381 InkAction* inky = ink_action_new( "NodeBreakAction",
1382 _("Break nodes"),
1383 _("Break path at selected nodes"),
1384 INKSCAPE_ICON_NODE_BREAK,
1385 secondarySize );
1386 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1387 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1388 }
1391 {
1392 InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1393 _("Join with segment"),
1394 _("Join selected endnodes with a new segment"),
1395 INKSCAPE_ICON_NODE_JOIN_SEGMENT,
1396 secondarySize );
1397 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1398 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1399 }
1401 {
1402 InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1403 _("Delete segment"),
1404 _("Delete segment between two non-endpoint nodes"),
1405 INKSCAPE_ICON_NODE_DELETE_SEGMENT,
1406 secondarySize );
1407 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1408 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1409 }
1411 {
1412 InkAction* inky = ink_action_new( "NodeCuspAction",
1413 _("Node Cusp"),
1414 _("Make selected nodes corner"),
1415 INKSCAPE_ICON_NODE_TYPE_CUSP,
1416 secondarySize );
1417 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1418 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1419 }
1421 {
1422 InkAction* inky = ink_action_new( "NodeSmoothAction",
1423 _("Node Smooth"),
1424 _("Make selected nodes smooth"),
1425 INKSCAPE_ICON_NODE_TYPE_SMOOTH,
1426 secondarySize );
1427 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1428 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1429 }
1431 {
1432 InkAction* inky = ink_action_new( "NodeSymmetricAction",
1433 _("Node Symmetric"),
1434 _("Make selected nodes symmetric"),
1435 INKSCAPE_ICON_NODE_TYPE_SYMMETRIC,
1436 secondarySize );
1437 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1438 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1439 }
1441 {
1442 InkAction* inky = ink_action_new( "NodeAutoAction",
1443 _("Node Auto"),
1444 _("Make selected nodes auto-smooth"),
1445 INKSCAPE_ICON_NODE_TYPE_AUTO_SMOOTH,
1446 secondarySize );
1447 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_auto), 0 );
1448 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1449 }
1451 {
1452 InkAction* inky = ink_action_new( "NodeLineAction",
1453 _("Node Line"),
1454 _("Make selected segments lines"),
1455 INKSCAPE_ICON_NODE_SEGMENT_LINE,
1456 secondarySize );
1457 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1458 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1459 }
1461 {
1462 InkAction* inky = ink_action_new( "NodeCurveAction",
1463 _("Node Curve"),
1464 _("Make selected segments curves"),
1465 INKSCAPE_ICON_NODE_SEGMENT_CURVE,
1466 secondarySize );
1467 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1468 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1469 }
1471 {
1472 InkToggleAction* act = ink_toggle_action_new( "NodesShowTransformHandlesAction",
1473 _("Show Transform Handles"),
1474 _("Show transformation handles for selected nodes"),
1475 "node-transform",
1476 secondarySize );
1477 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1478 PrefPusher *pusher = new PrefPusher(GTK_TOGGLE_ACTION(act), "/tools/nodes/show_transform_handles");
1479 g_signal_connect( holder, "destroy", G_CALLBACK(delete_prefspusher), pusher);
1480 }
1482 {
1483 InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1484 _("Show Handles"),
1485 _("Show Bezier handles of selected nodes"),
1486 INKSCAPE_ICON_SHOW_NODE_HANDLES,
1487 secondarySize );
1488 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1489 PrefPusher *pusher = new PrefPusher(GTK_TOGGLE_ACTION(act), "/tools/nodes/show_handles");
1490 g_signal_connect( holder, "destroy", G_CALLBACK(delete_prefspusher), pusher);
1491 }
1493 {
1494 InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1495 _("Show Outline"),
1496 _("Show path outline (without path effects)"),
1497 INKSCAPE_ICON_SHOW_PATH_OUTLINE,
1498 secondarySize );
1499 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1500 PrefPusher *pusher = new PrefPusher(GTK_TOGGLE_ACTION(act), "/tools/nodes/show_outline");
1501 g_signal_connect( holder, "destroy", G_CALLBACK(delete_prefspusher), pusher);
1502 }
1504 {
1505 Inkscape::Verb* verb = Inkscape::Verb::get(SP_VERB_EDIT_NEXT_PATHEFFECT_PARAMETER);
1506 InkAction* inky = ink_action_new( verb->get_id(),
1507 verb->get_name(),
1508 verb->get_tip(),
1509 INKSCAPE_ICON_PATH_EFFECT_PARAMETER_NEXT,
1510 secondarySize );
1511 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1512 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1513 g_object_set_data( holder, "nodes_lpeedit", inky);
1514 }
1516 {
1517 InkToggleAction* inky = ink_toggle_action_new( "ObjectEditClipPathAction",
1518 _("Edit clipping paths"),
1519 _("Show clipping path(s) of selected object(s)"),
1520 INKSCAPE_ICON_PATH_CLIP_EDIT,
1521 secondarySize );
1522 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1523 PrefPusher *pusher = new PrefPusher(GTK_TOGGLE_ACTION(inky), "/tools/nodes/edit_clipping_paths");
1524 g_signal_connect( holder, "destroy", G_CALLBACK(delete_prefspusher), pusher);
1525 }
1527 {
1528 InkToggleAction* inky = ink_toggle_action_new( "ObjectEditMaskPathAction",
1529 _("Edit masks"),
1530 _("Show mask(s) of selected object(s)"),
1531 INKSCAPE_ICON_PATH_MASK_EDIT,
1532 secondarySize );
1533 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1534 PrefPusher *pusher = new PrefPusher(GTK_TOGGLE_ACTION(inky), "/tools/nodes/edit_masks");
1535 g_signal_connect( holder, "destroy", G_CALLBACK(delete_prefspusher), pusher);
1536 }
1538 /* X coord of selected node(s) */
1539 {
1540 EgeAdjustmentAction* eact = 0;
1541 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1542 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1543 eact = create_adjustment_action( "NodeXAction",
1544 _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1545 "/tools/nodes/Xcoord", 0,
1546 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1547 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1548 labels, values, G_N_ELEMENTS(labels),
1549 sp_node_path_x_value_changed );
1550 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1551 g_object_set_data( holder, "nodes_x_action", eact );
1552 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1553 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1554 }
1556 /* Y coord of selected node(s) */
1557 {
1558 EgeAdjustmentAction* eact = 0;
1559 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1560 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1561 eact = create_adjustment_action( "NodeYAction",
1562 _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1563 "/tools/nodes/Ycoord", 0,
1564 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1565 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1566 labels, values, G_N_ELEMENTS(labels),
1567 sp_node_path_y_value_changed );
1568 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1569 g_object_set_data( holder, "nodes_y_action", eact );
1570 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1571 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1572 }
1574 // add the units menu
1575 {
1576 GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1577 gtk_action_group_add_action( mainActions, act );
1578 }
1581 sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1583 //watch selection
1584 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1586 sigc::connection *c_selection_changed =
1587 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1588 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1589 pool->add_connection ("selection-changed", c_selection_changed);
1591 sigc::connection *c_selection_modified =
1592 new sigc::connection (sp_desktop_selection (desktop)->connectModified
1593 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1594 pool->add_connection ("selection-modified", c_selection_modified);
1596 sigc::connection *c_subselection_changed =
1597 new sigc::connection (desktop->connectToolSubselectionChanged
1598 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1599 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1601 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1603 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1604 } // end of sp_node_toolbox_prep()
1607 //########################
1608 //## Zoom Toolbox ##
1609 //########################
1611 static void sp_zoom_toolbox_prep(SPDesktop * /*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1612 {
1613 // no custom GtkAction setup needed
1614 } // end of sp_zoom_toolbox_prep()
1616 void ToolboxFactory::setToolboxDesktop(GtkWidget *toolbox, SPDesktop *desktop)
1617 {
1618 sigc::connection *conn = static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1619 "event_context_connection"));
1621 BarId id = static_cast<BarId>( GPOINTER_TO_INT(g_object_get_data(G_OBJECT(toolbox), BAR_ID_KEY)) );
1623 SetupFunction setup_func = 0;
1624 UpdateFunction update_func = 0;
1626 switch (id) {
1627 case BAR_TOOL:
1628 setup_func = setup_tool_toolbox;
1629 update_func = update_tool_toolbox;
1630 break;
1632 case BAR_AUX:
1633 toolbox = gtk_bin_get_child(GTK_BIN(toolbox));
1634 setup_func = setup_aux_toolbox;
1635 update_func = update_aux_toolbox;
1636 break;
1638 case BAR_COMMANDS:
1639 setup_func = setup_commands_toolbox;
1640 update_func = update_commands_toolbox;
1641 break;
1643 case BAR_SNAP:
1644 setup_func = setup_snap_toolbox;
1645 update_func = updateSnapToolbox;
1646 break;
1647 default:
1648 g_warning("Unexpected toolbox id encountered.");
1649 }
1651 gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1652 SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1654 if (old_desktop) {
1655 GList *children, *iter;
1657 children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1658 for ( iter = children ; iter ; iter = iter->next ) {
1659 gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1660 }
1661 g_list_free(children);
1662 }
1664 g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1666 if (desktop && setup_func && update_func) {
1667 gtk_widget_set_sensitive(toolbox, TRUE);
1668 setup_func(toolbox, desktop);
1669 update_func(desktop, desktop->event_context, toolbox);
1670 *conn = desktop->connectEventContextChanged(sigc::bind (sigc::ptr_fun(update_func), toolbox));
1671 } else {
1672 gtk_widget_set_sensitive(toolbox, FALSE);
1673 }
1675 } // end of sp_toolbox_set_desktop()
1678 static void setupToolboxCommon( GtkWidget *toolbox,
1679 SPDesktop *desktop,
1680 gchar const *descr,
1681 gchar const* toolbarName,
1682 gchar const* sizePref )
1683 {
1684 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1685 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1687 GtkUIManager* mgr = gtk_ui_manager_new();
1688 GError* errVal = 0;
1690 GtkOrientation orientation = GTK_ORIENTATION_HORIZONTAL;
1692 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1693 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1695 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, toolbarName );
1696 if ( prefs->getBool("/toolbox/icononly", true) ) {
1697 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1698 }
1700 Inkscape::IconSize toolboxSize = ToolboxFactory::prefToSize(sizePref);
1701 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1703 if (GTK_IS_HANDLE_BOX(toolbox)) {
1704 // g_message("GRABBING ORIENTATION [%s]", toolbarName);
1705 GtkPositionType pos = gtk_handle_box_get_handle_position(GTK_HANDLE_BOX(toolbox));
1706 orientation = ((pos == GTK_POS_LEFT) || (pos == GTK_POS_RIGHT)) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
1707 } else {
1708 GtkPositionType pos = static_cast<GtkPositionType>(GPOINTER_TO_INT(g_object_get_data( G_OBJECT(toolbox), HANDLE_POS_MARK )));
1709 orientation = ((pos == GTK_POS_LEFT) || (pos == GTK_POS_RIGHT)) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
1710 }
1711 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), orientation);
1712 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1714 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1716 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1717 if ( child ) {
1718 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1719 }
1721 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1722 }
1724 #define noDUMP_DETAILS 1
1726 void ToolboxFactory::setOrientation(GtkWidget* toolbox, GtkOrientation orientation)
1727 {
1728 #if DUMP_DETAILS
1729 g_message("Set orientation for %p to be %d", toolbox, orientation);
1730 GType type = GTK_WIDGET_TYPE(toolbox);
1731 g_message(" [%s]", g_type_name(type));
1732 g_message(" %p", g_object_get_data(G_OBJECT(toolbox), BAR_ID_KEY));
1733 #endif
1735 GtkPositionType pos = (orientation == GTK_ORIENTATION_HORIZONTAL) ? GTK_POS_LEFT : GTK_POS_TOP;
1736 GtkHandleBox* handleBox = 0;
1738 if (GTK_IS_BIN(toolbox)) {
1739 #if DUMP_DETAILS
1740 g_message(" is a BIN");
1741 #endif // DUMP_DETAILS
1742 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1743 if (child) {
1744 #if DUMP_DETAILS
1745 GType type2 = GTK_WIDGET_TYPE(child);
1746 g_message(" child [%s]", g_type_name(type2));
1747 #endif // DUMP_DETAILS
1749 if (GTK_IS_BOX(child)) {
1750 #if DUMP_DETAILS
1751 g_message(" is a BOX");
1752 #endif // DUMP_DETAILS
1754 GList* children = gtk_container_get_children(GTK_CONTAINER(child));
1755 if (children) {
1756 for (GList* curr = children; curr; curr = g_list_next(curr)) {
1757 GtkWidget* child2 = GTK_WIDGET(curr->data);
1758 #if DUMP_DETAILS
1759 GType type3 = GTK_WIDGET_TYPE(child2);
1760 g_message(" child2 [%s]", g_type_name(type3));
1761 #endif // DUMP_DETAILS
1763 if (GTK_IS_CONTAINER(child2)) {
1764 GList* children2 = gtk_container_get_children(GTK_CONTAINER(child2));
1765 if (children2) {
1766 for (GList* curr2 = children2; curr2; curr2 = g_list_next(curr2)) {
1767 GtkWidget* child3 = GTK_WIDGET(curr2->data);
1768 #if DUMP_DETAILS
1769 GType type4 = GTK_WIDGET_TYPE(child3);
1770 g_message(" child3 [%s]", g_type_name(type4));
1771 #endif // DUMP_DETAILS
1772 if (GTK_IS_TOOLBAR(child3)) {
1773 GtkToolbar* childBar = GTK_TOOLBAR(child3);
1774 gtk_toolbar_set_orientation(childBar, orientation);
1775 }
1776 }
1777 g_list_free(children2);
1778 }
1779 }
1782 if (GTK_IS_TOOLBAR(child2)) {
1783 GtkToolbar* childBar = GTK_TOOLBAR(child2);
1784 gtk_toolbar_set_orientation(childBar, orientation);
1785 if (GTK_IS_HANDLE_BOX(toolbox)) {
1786 handleBox = GTK_HANDLE_BOX(toolbox);
1787 }
1788 } else {
1789 g_message("need to add dynamic switch");
1790 }
1791 }
1792 g_list_free(children);
1793 } else {
1794 // The call is being made before the toolbox proper has been setup.
1795 if (GTK_IS_HANDLE_BOX(toolbox)) {
1796 handleBox = GTK_HANDLE_BOX(toolbox);
1797 } else {
1798 g_object_set_data(G_OBJECT(toolbox), HANDLE_POS_MARK, GINT_TO_POINTER(pos));
1799 }
1800 }
1801 } else if (GTK_IS_TOOLBAR(child)) {
1802 GtkToolbar* toolbar = GTK_TOOLBAR(child);
1803 gtk_toolbar_set_orientation( toolbar, orientation );
1804 if (GTK_IS_HANDLE_BOX(toolbox)) {
1805 handleBox = GTK_HANDLE_BOX(toolbox);
1806 }
1807 }
1808 }
1809 }
1811 if (handleBox) {
1812 gtk_handle_box_set_handle_position(handleBox, pos);
1813 }
1814 }
1816 void setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1817 {
1818 gchar const * descr =
1819 "<ui>"
1820 " <toolbar name='ToolToolbar'>"
1822 " <!-- Basics -->"
1823 " <toolitem action='ToolSelector' />"
1824 " <toolitem action='ToolNode' />"
1825 " <toolitem action='ToolTweak' />"
1826 " <toolitem action='ToolZoom' />"
1828 " <!-- Shapes -->"
1829 " <toolitem action='ToolRect' />"
1830 " <toolitem action='Tool3DBox' />"
1831 " <toolitem action='ToolArc' />"
1832 " <toolitem action='ToolStar' />"
1833 " <toolitem action='ToolSpiral' />"
1835 " <!-- Paths -->"
1836 " <toolitem action='ToolPencil' />"
1837 " <toolitem action='ToolPen' />"
1838 " <toolitem action='ToolCalligraphic' />"
1840 " <!-- Text -->"
1841 " <toolitem action='ToolText' />"
1843 " <!-- Paint large areas -->"
1844 " <toolitem action='ToolSpray' />"
1845 " <toolitem action='ToolEraser' />"
1847 " <!-- Fill -->"
1848 " <toolitem action='ToolPaintBucket' />"
1849 " <toolitem action='ToolGradient' />"
1850 " <toolitem action='ToolDropper' />"
1852 " <toolitem action='ToolConnector' />"
1854 // " <toolitem action='ToolLPETool' />"
1855 " </toolbar>"
1856 "</ui>";
1858 setupToolboxCommon( toolbox, desktop, descr,
1859 "/ui/ToolToolbar",
1860 "/toolbox/tools/small");
1861 }
1863 void update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget * /*toolbox*/ )
1864 {
1865 gchar const *const tname = ( eventcontext
1866 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1867 : NULL );
1868 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1870 for (int i = 0 ; tools[i].type_name ; i++ ) {
1871 Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1872 if ( act ) {
1873 bool setActive = tname && !strcmp(tname, tools[i].type_name);
1874 Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1875 if ( verbAct ) {
1876 verbAct->set_active(setActive);
1877 }
1878 }
1879 }
1880 }
1882 void setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1883 {
1884 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1885 GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1886 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1887 GtkUIManager* mgr = gtk_ui_manager_new();
1888 GError* errVal = 0;
1889 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1890 gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1892 std::map<std::string, GtkWidget*> dataHolders;
1894 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1895 if ( aux_toolboxes[i].prep_func ) {
1896 // converted to GtkActions and UIManager
1898 GtkWidget* kludge = gtk_toolbar_new();
1899 g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1900 g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1901 dataHolders[aux_toolboxes[i].type_name] = kludge;
1902 aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1903 } else {
1905 GtkWidget *sub_toolbox = 0;
1906 if (aux_toolboxes[i].create_func == NULL) {
1907 sub_toolbox = sp_empty_toolbox_new(desktop);
1908 } else {
1909 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1910 }
1912 gtk_size_group_add_widget( grouper, sub_toolbox );
1914 gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1915 g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1917 }
1918 }
1920 // Second pass to create toolbars *after* all GtkActions are created
1921 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1922 if ( aux_toolboxes[i].prep_func ) {
1923 // converted to GtkActions and UIManager
1925 GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1927 GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1928 gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1930 gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1931 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1932 g_free( tmp );
1933 tmp = 0;
1935 if ( prefs->getBool( "/toolbox/icononly", true) ) {
1936 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1937 }
1939 Inkscape::IconSize toolboxSize = ToolboxFactory::prefToSize("/toolbox/small");
1940 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1942 gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1944 if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1945 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1946 swatch->setDesktop( desktop );
1947 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1948 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1949 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1950 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 );
1951 }
1953 gtk_widget_show_all( holder );
1954 sp_set_font_size_smaller( holder );
1956 gtk_size_group_add_widget( grouper, holder );
1958 gtk_container_add( GTK_CONTAINER(toolbox), holder );
1959 g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1960 }
1961 }
1963 g_object_unref( G_OBJECT(grouper) );
1964 }
1966 void update_aux_toolbox(SPDesktop * /*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1967 {
1968 gchar const *tname = ( eventcontext
1969 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1970 : NULL );
1971 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1972 GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1973 if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1974 gtk_widget_show_all(sub_toolbox);
1975 g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1976 } else {
1977 gtk_widget_hide(sub_toolbox);
1978 }
1979 }
1980 }
1982 void setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1983 {
1984 gchar const * descr =
1985 "<ui>"
1986 " <toolbar name='CommandsToolbar'>"
1987 " <toolitem action='FileNew' />"
1988 " <toolitem action='FileOpen' />"
1989 " <toolitem action='FileSave' />"
1990 " <toolitem action='FilePrint' />"
1991 " <separator />"
1992 " <toolitem action='FileImport' />"
1993 " <toolitem action='FileExport' />"
1994 " <separator />"
1995 " <toolitem action='EditUndo' />"
1996 " <toolitem action='EditRedo' />"
1997 " <separator />"
1998 " <toolitem action='EditCopy' />"
1999 " <toolitem action='EditCut' />"
2000 " <toolitem action='EditPaste' />"
2001 " <separator />"
2002 " <toolitem action='ZoomSelection' />"
2003 " <toolitem action='ZoomDrawing' />"
2004 " <toolitem action='ZoomPage' />"
2005 " <separator />"
2006 " <toolitem action='EditDuplicate' />"
2007 " <toolitem action='EditClone' />"
2008 " <toolitem action='EditUnlinkClone' />"
2009 " <separator />"
2010 " <toolitem action='SelectionGroup' />"
2011 " <toolitem action='SelectionUnGroup' />"
2012 " <separator />"
2013 " <toolitem action='DialogFillStroke' />"
2014 " <toolitem action='DialogText' />"
2015 " <toolitem action='DialogLayers' />"
2016 " <toolitem action='DialogXMLEditor' />"
2017 " <toolitem action='DialogAlignDistribute' />"
2018 " <separator />"
2019 " <toolitem action='DialogPreferences' />"
2020 " <toolitem action='DialogDocumentProperties' />"
2021 " </toolbar>"
2022 "</ui>";
2024 setupToolboxCommon( toolbox, desktop, descr,
2025 "/ui/CommandsToolbar",
2026 "/toolbox/small" );
2027 }
2029 void update_commands_toolbox(SPDesktop * /*desktop*/, SPEventContext * /*eventcontext*/, GtkWidget * /*toolbox*/)
2030 {
2031 }
2033 static void toggle_snap_callback(GtkToggleAction *act, gpointer data) //data points to the toolbox
2034 {
2035 if (g_object_get_data(G_OBJECT(data), "freeze" )) {
2036 return;
2037 }
2039 gpointer ptr = g_object_get_data(G_OBJECT(data), "desktop");
2040 g_assert(ptr != NULL);
2042 SPDesktop *dt = reinterpret_cast<SPDesktop*>(ptr);
2043 SPNamedView *nv = sp_desktop_namedview(dt);
2044 SPDocument *doc = SP_OBJECT_DOCUMENT(nv);
2046 if (dt == NULL || nv == NULL) {
2047 g_warning("No desktop or namedview specified (in toggle_snap_callback)!");
2048 return;
2049 }
2051 Inkscape::XML::Node *repr = SP_OBJECT_REPR(nv);
2053 if (repr == NULL) {
2054 g_warning("This namedview doesn't have a xml representation attached!");
2055 return;
2056 }
2058 bool saved = DocumentUndo::getUndoSensitive(doc);
2059 DocumentUndo::setUndoSensitive(doc, false);
2061 bool v = false;
2062 SPAttributeEnum attr = (SPAttributeEnum) GPOINTER_TO_INT(g_object_get_data(G_OBJECT(act), "SP_ATTR_INKSCAPE"));
2064 switch (attr) {
2065 case SP_ATTR_INKSCAPE_SNAP_GLOBAL:
2066 dt->toggleSnapGlobal();
2067 break;
2068 case SP_ATTR_INKSCAPE_SNAP_BBOX:
2069 v = nv->snap_manager.snapprefs.getSnapModeBBox();
2070 sp_repr_set_boolean(repr, "inkscape:snap-bbox", !v);
2071 break;
2072 case SP_ATTR_INKSCAPE_BBOX_PATHS:
2073 v = nv->snap_manager.snapprefs.getSnapToBBoxPath();
2074 sp_repr_set_boolean(repr, "inkscape:bbox-paths", !v);
2075 break;
2076 case SP_ATTR_INKSCAPE_BBOX_NODES:
2077 v = nv->snap_manager.snapprefs.getSnapToBBoxNode();
2078 sp_repr_set_boolean(repr, "inkscape:bbox-nodes", !v);
2079 break;
2080 case SP_ATTR_INKSCAPE_SNAP_NODES:
2081 v = nv->snap_manager.snapprefs.getSnapModeNode();
2082 sp_repr_set_boolean(repr, "inkscape:snap-nodes", !v);
2083 break;
2084 case SP_ATTR_INKSCAPE_OBJECT_PATHS:
2085 v = nv->snap_manager.snapprefs.getSnapToItemPath();
2086 sp_repr_set_boolean(repr, "inkscape:object-paths", !v);
2087 break;
2088 case SP_ATTR_INKSCAPE_OBJECT_NODES:
2089 v = nv->snap_manager.snapprefs.getSnapToItemNode();
2090 sp_repr_set_boolean(repr, "inkscape:object-nodes", !v);
2091 break;
2092 case SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES:
2093 v = nv->snap_manager.snapprefs.getSnapSmoothNodes();
2094 sp_repr_set_boolean(repr, "inkscape:snap-smooth-nodes", !v);
2095 break;
2096 case SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS:
2097 v = nv->snap_manager.snapprefs.getSnapIntersectionCS();
2098 sp_repr_set_boolean(repr, "inkscape:snap-intersection-paths", !v);
2099 break;
2100 case SP_ATTR_INKSCAPE_SNAP_CENTER:
2101 v = nv->snap_manager.snapprefs.getIncludeItemCenter();
2102 sp_repr_set_boolean(repr, "inkscape:snap-center", !v);
2103 break;
2104 case SP_ATTR_INKSCAPE_SNAP_GRIDS:
2105 v = nv->snap_manager.snapprefs.getSnapToGrids();
2106 sp_repr_set_boolean(repr, "inkscape:snap-grids", !v);
2107 break;
2108 case SP_ATTR_INKSCAPE_SNAP_TO_GUIDES:
2109 v = nv->snap_manager.snapprefs.getSnapToGuides();
2110 sp_repr_set_boolean(repr, "inkscape:snap-to-guides", !v);
2111 break;
2112 case SP_ATTR_INKSCAPE_SNAP_PAGE:
2113 v = nv->snap_manager.snapprefs.getSnapToPageBorder();
2114 sp_repr_set_boolean(repr, "inkscape:snap-page", !v);
2115 break;
2116 /*case SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE:
2117 v = nv->snap_manager.snapprefs.getSnapIntersectionGG();
2118 sp_repr_set_boolean(repr, "inkscape:snap-intersection-grid-guide", !v);
2119 break;*/
2120 case SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS:
2121 v = nv->snap_manager.snapprefs.getSnapLineMidpoints();
2122 sp_repr_set_boolean(repr, "inkscape:snap-midpoints", !v);
2123 break;
2124 case SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS:
2125 v = nv->snap_manager.snapprefs.getSnapObjectMidpoints();
2126 sp_repr_set_boolean(repr, "inkscape:snap-object-midpoints", !v);
2127 break;
2128 case SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS:
2129 v = nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints();
2130 sp_repr_set_boolean(repr, "inkscape:snap-bbox-edge-midpoints", !v);
2131 break;
2132 case SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS:
2133 v = nv->snap_manager.snapprefs.getSnapBBoxMidpoints();
2134 sp_repr_set_boolean(repr, "inkscape:snap-bbox-midpoints", !v);
2135 break;
2136 default:
2137 g_warning("toggle_snap_callback has been called with an ID for which no action has been defined");
2138 break;
2139 }
2141 // The snapping preferences are stored in the document, and therefore toggling makes the document dirty
2142 doc->setModifiedSinceSave();
2144 DocumentUndo::setUndoSensitive(doc, saved);
2145 }
2147 void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
2148 {
2149 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2151 gchar const * descr =
2152 "<ui>"
2153 " <toolbar name='SnapToolbar'>"
2154 " <toolitem action='ToggleSnapGlobal' />"
2155 " <separator />"
2156 " <toolitem action='ToggleSnapFromBBoxCorner' />"
2157 " <toolitem action='ToggleSnapToBBoxPath' />"
2158 " <toolitem action='ToggleSnapToBBoxNode' />"
2159 " <toolitem action='ToggleSnapToFromBBoxEdgeMidpoints' />"
2160 " <toolitem action='ToggleSnapToFromBBoxCenters' />"
2161 " <separator />"
2162 " <toolitem action='ToggleSnapFromNode' />"
2163 " <toolitem action='ToggleSnapToItemPath' />"
2164 " <toolitem action='ToggleSnapToPathIntersections' />"
2165 " <toolitem action='ToggleSnapToItemNode' />"
2166 " <toolitem action='ToggleSnapToSmoothNodes' />"
2167 " <toolitem action='ToggleSnapToFromLineMidpoints' />"
2168 " <toolitem action='ToggleSnapToFromObjectCenters' />"
2169 " <toolitem action='ToggleSnapToFromRotationCenter' />"
2170 " <separator />"
2171 " <toolitem action='ToggleSnapToPageBorder' />"
2172 " <toolitem action='ToggleSnapToGrids' />"
2173 " <toolitem action='ToggleSnapToGuides' />"
2174 //" <toolitem action='ToggleSnapToGridGuideIntersections' />"
2175 " </toolbar>"
2176 "</ui>";
2178 Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
2180 {
2181 // TODO: This is a cludge. On the one hand we have verbs+actions,
2182 // on the other we have all these explicit callbacks specified here.
2183 // We should really unify these (should save some lines of code as well).
2184 // For example, this action could be based on the verb(+action) + PrefsPusher.
2185 Inkscape::Verb* verb = Inkscape::Verb::get(SP_VERB_TOGGLE_SNAPPING);
2186 InkToggleAction* act = ink_toggle_action_new(verb->get_id(),
2187 verb->get_name(), verb->get_tip(), INKSCAPE_ICON_SNAP, secondarySize,
2188 SP_ATTR_INKSCAPE_SNAP_GLOBAL);
2190 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2191 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2192 }
2194 {
2195 InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromBBoxCorner",
2196 _("Bounding box"), _("Snap bounding box corners"), INKSCAPE_ICON_SNAP_BOUNDING_BOX,
2197 secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX);
2199 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2200 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2201 }
2203 {
2204 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxPath",
2205 _("Bounding box edges"), _("Snap to edges of a bounding box"),
2206 INKSCAPE_ICON_SNAP_BOUNDING_BOX_EDGES, secondarySize, SP_ATTR_INKSCAPE_BBOX_PATHS);
2208 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2209 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2210 }
2212 {
2213 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxNode",
2214 _("Bounding box corners"), _("Snap to bounding box corners"),
2215 INKSCAPE_ICON_SNAP_BOUNDING_BOX_CORNERS, secondarySize, SP_ATTR_INKSCAPE_BBOX_NODES);
2217 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2218 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2219 }
2221 {
2222 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxEdgeMidpoints",
2223 _("BBox Edge Midpoints"), _("Snap from and to midpoints of bounding box edges"),
2224 INKSCAPE_ICON_SNAP_BOUNDING_BOX_MIDPOINTS, secondarySize,
2225 SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS);
2227 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2228 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2229 }
2231 {
2232 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxCenters",
2233 _("BBox Centers"), _("Snapping from and to centers of bounding boxes"),
2234 INKSCAPE_ICON_SNAP_BOUNDING_BOX_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS);
2236 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2237 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2238 }
2240 {
2241 InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromNode",
2242 _("Nodes"), _("Snap nodes or handles"), INKSCAPE_ICON_SNAP_NODES, secondarySize, SP_ATTR_INKSCAPE_SNAP_NODES);
2244 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2245 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2246 }
2248 {
2249 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemPath",
2250 _("Paths"), _("Snap to paths"), INKSCAPE_ICON_SNAP_NODES_PATH, secondarySize,
2251 SP_ATTR_INKSCAPE_OBJECT_PATHS);
2253 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2254 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2255 }
2257 {
2258 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPathIntersections",
2259 _("Path intersections"), _("Snap to path intersections"),
2260 INKSCAPE_ICON_SNAP_NODES_INTERSECTION, secondarySize, SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS);
2262 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2263 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2264 }
2266 {
2267 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemNode",
2268 _("To nodes"), _("Snap to cusp nodes"), INKSCAPE_ICON_SNAP_NODES_CUSP, secondarySize,
2269 SP_ATTR_INKSCAPE_OBJECT_NODES);
2271 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2272 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2273 }
2275 {
2276 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToSmoothNodes",
2277 _("Smooth nodes"), _("Snap to smooth nodes"), INKSCAPE_ICON_SNAP_NODES_SMOOTH,
2278 secondarySize, SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES);
2280 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2281 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2282 }
2284 {
2285 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromLineMidpoints",
2286 _("Line Midpoints"), _("Snap from and to midpoints of line segments"),
2287 INKSCAPE_ICON_SNAP_NODES_MIDPOINT, secondarySize, SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS);
2289 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2290 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2291 }
2293 {
2294 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromObjectCenters",
2295 _("Object Centers"), _("Snap from and to centers of objects"),
2296 INKSCAPE_ICON_SNAP_NODES_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS);
2298 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2299 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2300 }
2302 {
2303 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromRotationCenter",
2304 _("Rotation Centers"), _("Snap from and to an item's rotation center"),
2305 INKSCAPE_ICON_SNAP_NODES_ROTATION_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_CENTER);
2307 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2308 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2309 }
2311 {
2312 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPageBorder",
2313 _("Page border"), _("Snap to the page border"), INKSCAPE_ICON_SNAP_PAGE,
2314 secondarySize, SP_ATTR_INKSCAPE_SNAP_PAGE);
2316 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2317 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2318 }
2320 {
2321 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGrids",
2322 _("Grids"), _("Snap to grids"), INKSCAPE_ICON_GRID_RECTANGULAR, secondarySize,
2323 SP_ATTR_INKSCAPE_SNAP_GRIDS);
2325 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2326 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2327 }
2329 {
2330 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGuides",
2331 _("Guides"), _("Snap to guides"), INKSCAPE_ICON_GUIDES, secondarySize,
2332 SP_ATTR_INKSCAPE_SNAP_TO_GUIDES);
2334 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2335 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2336 }
2338 /*{
2339 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGridGuideIntersections",
2340 _("Grid/guide intersections"), _("Snap to intersections of a grid with a guide"),
2341 INKSCAPE_ICON_SNAP_GRID_GUIDE_INTERSECTIONS, secondarySize,
2342 SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE);
2344 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2345 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2346 }*/
2348 setupToolboxCommon( toolbox, desktop, descr,
2349 "/ui/SnapToolbar",
2350 "/toolbox/secondary" );
2351 }
2353 Glib::ustring ToolboxFactory::getToolboxName(GtkWidget* toolbox)
2354 {
2355 Glib::ustring name;
2356 BarId id = static_cast<BarId>( GPOINTER_TO_INT(g_object_get_data(G_OBJECT(toolbox), BAR_ID_KEY)) );
2357 switch(id) {
2358 case BAR_TOOL:
2359 name = "ToolToolbar";
2360 break;
2361 case BAR_AUX:
2362 name = "AuxToolbar";
2363 break;
2364 case BAR_COMMANDS:
2365 name = "CommandsToolbar";
2366 break;
2367 case BAR_SNAP:
2368 name = "SnapToolbar";
2369 break;
2370 }
2372 return name;
2373 }
2375 void ToolboxFactory::updateSnapToolbox(SPDesktop *desktop, SPEventContext * /*eventcontext*/, GtkWidget *toolbox)
2376 {
2377 g_assert(desktop != NULL);
2378 g_assert(toolbox != NULL);
2380 SPNamedView *nv = sp_desktop_namedview(desktop);
2381 if (nv == NULL) {
2382 g_warning("Namedview cannot be retrieved (in updateSnapToolbox)!");
2383 return;
2384 }
2386 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2388 Glib::RefPtr<Gtk::Action> act1 = mainActions->get_action("ToggleSnapGlobal");
2389 Glib::RefPtr<Gtk::Action> act2 = mainActions->get_action("ToggleSnapFromBBoxCorner");
2390 Glib::RefPtr<Gtk::Action> act3 = mainActions->get_action("ToggleSnapToBBoxPath");
2391 Glib::RefPtr<Gtk::Action> act4 = mainActions->get_action("ToggleSnapToBBoxNode");
2392 Glib::RefPtr<Gtk::Action> act4b = mainActions->get_action("ToggleSnapToFromBBoxEdgeMidpoints");
2393 Glib::RefPtr<Gtk::Action> act4c = mainActions->get_action("ToggleSnapToFromBBoxCenters");
2394 Glib::RefPtr<Gtk::Action> act5 = mainActions->get_action("ToggleSnapFromNode");
2395 Glib::RefPtr<Gtk::Action> act6 = mainActions->get_action("ToggleSnapToItemPath");
2396 Glib::RefPtr<Gtk::Action> act6b = mainActions->get_action("ToggleSnapToPathIntersections");
2397 Glib::RefPtr<Gtk::Action> act7 = mainActions->get_action("ToggleSnapToItemNode");
2398 Glib::RefPtr<Gtk::Action> act8 = mainActions->get_action("ToggleSnapToSmoothNodes");
2399 Glib::RefPtr<Gtk::Action> act9 = mainActions->get_action("ToggleSnapToFromLineMidpoints");
2400 Glib::RefPtr<Gtk::Action> act10 = mainActions->get_action("ToggleSnapToFromObjectCenters");
2401 Glib::RefPtr<Gtk::Action> act11 = mainActions->get_action("ToggleSnapToFromRotationCenter");
2402 Glib::RefPtr<Gtk::Action> act12 = mainActions->get_action("ToggleSnapToPageBorder");
2403 //Glib::RefPtr<Gtk::Action> act13 = mainActions->get_action("ToggleSnapToGridGuideIntersections");
2404 Glib::RefPtr<Gtk::Action> act14 = mainActions->get_action("ToggleSnapToGrids");
2405 Glib::RefPtr<Gtk::Action> act15 = mainActions->get_action("ToggleSnapToGuides");
2408 if (!act1) {
2409 return; // The snap actions haven't been defined yet (might be the case during startup)
2410 }
2412 // The ..._set_active calls below will toggle the buttons, but this shouldn't lead to
2413 // changes in our document because we're only updating the UI;
2414 // Setting the "freeze" parameter to true will block the code in toggle_snap_callback()
2415 g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(TRUE));
2417 bool const c1 = nv->snap_manager.snapprefs.getSnapEnabledGlobally();
2418 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act1->gobj()), c1);
2420 bool const c2 = nv->snap_manager.snapprefs.getSnapModeBBox();
2421 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act2->gobj()), c2);
2422 gtk_action_set_sensitive(GTK_ACTION(act2->gobj()), c1);
2424 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act3->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxPath());
2425 gtk_action_set_sensitive(GTK_ACTION(act3->gobj()), c1 && c2);
2426 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxNode());
2427 gtk_action_set_sensitive(GTK_ACTION(act4->gobj()), c1 && c2);
2428 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4b->gobj()), nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints());
2429 gtk_action_set_sensitive(GTK_ACTION(act4b->gobj()), c1 && c2);
2430 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4c->gobj()), nv->snap_manager.snapprefs.getSnapBBoxMidpoints());
2431 gtk_action_set_sensitive(GTK_ACTION(act4c->gobj()), c1 && c2);
2433 bool const c3 = nv->snap_manager.snapprefs.getSnapModeNode();
2434 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act5->gobj()), c3);
2435 gtk_action_set_sensitive(GTK_ACTION(act5->gobj()), c1);
2437 bool const c4 = nv->snap_manager.snapprefs.getSnapToItemPath();
2438 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6->gobj()), c4);
2439 gtk_action_set_sensitive(GTK_ACTION(act6->gobj()), c1 && c3);
2440 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6b->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionCS());
2441 gtk_action_set_sensitive(GTK_ACTION(act6b->gobj()), c1 && c3 && c4);
2442 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act7->gobj()), nv->snap_manager.snapprefs.getSnapToItemNode());
2443 gtk_action_set_sensitive(GTK_ACTION(act7->gobj()), c1 && c3);
2444 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act8->gobj()), nv->snap_manager.snapprefs.getSnapSmoothNodes());
2445 gtk_action_set_sensitive(GTK_ACTION(act8->gobj()), c1 && c3);
2446 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act9->gobj()), nv->snap_manager.snapprefs.getSnapLineMidpoints());
2447 gtk_action_set_sensitive(GTK_ACTION(act9->gobj()), c1 && c3);
2448 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act10->gobj()), nv->snap_manager.snapprefs.getSnapObjectMidpoints());
2449 gtk_action_set_sensitive(GTK_ACTION(act10->gobj()), c1 && c3);
2450 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act11->gobj()), nv->snap_manager.snapprefs.getIncludeItemCenter());
2451 gtk_action_set_sensitive(GTK_ACTION(act11->gobj()), c1 && c3);
2453 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act12->gobj()), nv->snap_manager.snapprefs.getSnapToPageBorder());
2454 gtk_action_set_sensitive(GTK_ACTION(act12->gobj()), c1);
2455 //gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act13->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionGG());
2456 //gtk_action_set_sensitive(GTK_ACTION(act13->gobj()), c1);
2458 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act14->gobj()), nv->snap_manager.snapprefs.getSnapToGrids());
2459 gtk_action_set_sensitive(GTK_ACTION(act14->gobj()), c1);
2460 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act15->gobj()), nv->snap_manager.snapprefs.getSnapToGuides());
2461 gtk_action_set_sensitive(GTK_ACTION(act15->gobj()), c1);
2464 g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(FALSE)); // unfreeze (see above)
2465 }
2467 void ToolboxFactory::showAuxToolbox(GtkWidget *toolbox_toplevel)
2468 {
2469 gtk_widget_show(toolbox_toplevel);
2470 GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
2472 GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
2473 if (!shown_toolbox) {
2474 return;
2475 }
2476 gtk_widget_show(toolbox);
2478 gtk_widget_show_all(shown_toolbox);
2479 }
2481 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop)
2482 {
2483 GtkWidget *tbl = gtk_toolbar_new();
2484 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
2485 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
2487 gtk_widget_show_all(tbl);
2488 sp_set_font_size_smaller (tbl);
2490 return tbl;
2491 }
2493 #define MODE_LABEL_WIDTH 70
2495 //########################
2496 //## Star ##
2497 //########################
2499 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2500 {
2501 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2503 if (DocumentUndo::getUndoSensitive(sp_desktop_document(desktop))) {
2504 // do not remember prefs if this call is initiated by an undo change, because undoing object
2505 // creation sets bogus values to its attributes before it is deleted
2506 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2507 prefs->setInt("/tools/shapes/star/magnitude", (gint)adj->value);
2508 }
2510 // quit if run by the attr_changed listener
2511 if (g_object_get_data( dataKludge, "freeze" )) {
2512 return;
2513 }
2515 // in turn, prevent listener from responding
2516 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2518 bool modmade = false;
2520 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2521 GSList const *items = selection->itemList();
2522 for (; items != NULL; items = items->next) {
2523 if (SP_IS_STAR((SPItem *) items->data)) {
2524 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2525 sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
2526 sp_repr_set_svg_double(repr, "sodipodi:arg2",
2527 (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
2528 + M_PI / (gint)adj->value));
2529 SP_OBJECT((SPItem *) items->data)->updateRepr();
2530 modmade = true;
2531 }
2532 }
2533 if (modmade) {
2534 DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2535 _("Star: Change number of corners"));
2536 }
2538 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2539 }
2541 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2542 {
2543 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2545 if (DocumentUndo::getUndoSensitive(sp_desktop_document(desktop))) {
2546 if (!IS_NAN(adj->value)) {
2547 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2548 prefs->setDouble("/tools/shapes/star/proportion", adj->value);
2549 }
2550 }
2552 // quit if run by the attr_changed listener
2553 if (g_object_get_data( dataKludge, "freeze" )) {
2554 return;
2555 }
2557 // in turn, prevent listener from responding
2558 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2560 bool modmade = false;
2561 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2562 GSList const *items = selection->itemList();
2563 for (; items != NULL; items = items->next) {
2564 if (SP_IS_STAR((SPItem *) items->data)) {
2565 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2567 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2568 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2569 if (r2 < r1) {
2570 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
2571 } else {
2572 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
2573 }
2575 SP_OBJECT((SPItem *) items->data)->updateRepr();
2576 modmade = true;
2577 }
2578 }
2580 if (modmade) {
2581 DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2582 _("Star: Change spoke ratio"));
2583 }
2585 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2586 }
2588 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
2589 {
2590 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2591 bool flat = ege_select_one_action_get_active( act ) == 0;
2593 if (DocumentUndo::getUndoSensitive(sp_desktop_document(desktop))) {
2594 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2595 prefs->setBool( "/tools/shapes/star/isflatsided", flat);
2596 }
2598 // quit if run by the attr_changed listener
2599 if (g_object_get_data( dataKludge, "freeze" )) {
2600 return;
2601 }
2603 // in turn, prevent listener from responding
2604 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2606 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2607 GSList const *items = selection->itemList();
2608 GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2609 bool modmade = false;
2611 if ( prop_action ) {
2612 gtk_action_set_sensitive( prop_action, !flat );
2613 }
2615 for (; items != NULL; items = items->next) {
2616 if (SP_IS_STAR((SPItem *) items->data)) {
2617 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2618 repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
2619 SP_OBJECT((SPItem *) items->data)->updateRepr();
2620 modmade = true;
2621 }
2622 }
2624 if (modmade) {
2625 DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2626 flat ? _("Make polygon") : _("Make star"));
2627 }
2629 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2630 }
2632 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2633 {
2634 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2636 if (DocumentUndo::getUndoSensitive(sp_desktop_document(desktop))) {
2637 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2638 prefs->setDouble("/tools/shapes/star/rounded", (gdouble) adj->value);
2639 }
2641 // quit if run by the attr_changed listener
2642 if (g_object_get_data( dataKludge, "freeze" )) {
2643 return;
2644 }
2646 // in turn, prevent listener from responding
2647 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2649 bool modmade = false;
2651 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2652 GSList const *items = selection->itemList();
2653 for (; items != NULL; items = items->next) {
2654 if (SP_IS_STAR((SPItem *) items->data)) {
2655 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2656 sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2657 SP_OBJECT(items->data)->updateRepr();
2658 modmade = true;
2659 }
2660 }
2661 if (modmade) {
2662 DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2663 _("Star: Change rounding"));
2664 }
2666 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2667 }
2669 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2670 {
2671 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2673 if (DocumentUndo::getUndoSensitive(sp_desktop_document(desktop))) {
2674 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2675 prefs->setDouble("/tools/shapes/star/randomized", (gdouble) adj->value);
2676 }
2678 // quit if run by the attr_changed listener
2679 if (g_object_get_data( dataKludge, "freeze" )) {
2680 return;
2681 }
2683 // in turn, prevent listener from responding
2684 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2686 bool modmade = false;
2688 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2689 GSList const *items = selection->itemList();
2690 for (; items != NULL; items = items->next) {
2691 if (SP_IS_STAR((SPItem *) items->data)) {
2692 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2693 sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2694 SP_OBJECT(items->data)->updateRepr();
2695 modmade = true;
2696 }
2697 }
2698 if (modmade) {
2699 DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2700 _("Star: Change randomization"));
2701 }
2703 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2704 }
2707 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2708 gchar const * /*old_value*/, gchar const * /*new_value*/,
2709 bool /*is_interactive*/, gpointer data)
2710 {
2711 GtkWidget *tbl = GTK_WIDGET(data);
2713 // quit if run by the _changed callbacks
2714 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2715 return;
2716 }
2718 // in turn, prevent callbacks from responding
2719 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2721 GtkAdjustment *adj = 0;
2723 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2724 bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2726 if (!strcmp(name, "inkscape:randomized")) {
2727 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2728 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2729 } else if (!strcmp(name, "inkscape:rounded")) {
2730 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2731 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2732 } else if (!strcmp(name, "inkscape:flatsided")) {
2733 GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2734 char const *flatsides = repr->attribute("inkscape:flatsided");
2735 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2736 if ( flatsides && !strcmp(flatsides,"false") ) {
2737 ege_select_one_action_set_active( flat_action, 1 );
2738 gtk_action_set_sensitive( prop_action, TRUE );
2739 } else {
2740 ege_select_one_action_set_active( flat_action, 0 );
2741 gtk_action_set_sensitive( prop_action, FALSE );
2742 }
2743 } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2744 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2745 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2746 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2747 if (r2 < r1) {
2748 gtk_adjustment_set_value(adj, r2/r1);
2749 } else {
2750 gtk_adjustment_set_value(adj, r1/r2);
2751 }
2752 } else if (!strcmp(name, "sodipodi:sides")) {
2753 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2754 gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2755 }
2757 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2758 }
2761 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2762 {
2763 NULL, /* child_added */
2764 NULL, /* child_removed */
2765 star_tb_event_attr_changed,
2766 NULL, /* content_changed */
2767 NULL /* order_changed */
2768 };
2771 /**
2772 * \param selection Should not be NULL.
2773 */
2774 static void
2775 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2776 {
2777 int n_selected = 0;
2778 Inkscape::XML::Node *repr = NULL;
2780 purge_repr_listener( tbl, tbl );
2782 for (GSList const *items = selection->itemList();
2783 items != NULL;
2784 items = items->next)
2785 {
2786 if (SP_IS_STAR((SPItem *) items->data)) {
2787 n_selected++;
2788 repr = SP_OBJECT_REPR((SPItem *) items->data);
2789 }
2790 }
2792 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2794 if (n_selected == 0) {
2795 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2796 } else if (n_selected == 1) {
2797 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2799 if (repr) {
2800 g_object_set_data( tbl, "repr", repr );
2801 Inkscape::GC::anchor(repr);
2802 sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2803 sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2804 }
2805 } else {
2806 // FIXME: implement averaging of all parameters for multiple selected stars
2807 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2808 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2809 }
2810 }
2813 static void sp_stb_defaults( GtkWidget * /*widget*/, GObject *dataKludge )
2814 {
2815 // FIXME: in this and all other _default functions, set some flag telling the value_changed
2816 // callbacks to lump all the changes for all selected objects in one undo step
2818 GtkAdjustment *adj = 0;
2820 // fixme: make settable in prefs!
2821 gint mag = 5;
2822 gdouble prop = 0.5;
2823 gboolean flat = FALSE;
2824 gdouble randomized = 0;
2825 gdouble rounded = 0;
2827 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2828 ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2830 GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2831 gtk_action_set_sensitive( sb2, !flat );
2833 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2834 gtk_adjustment_set_value(adj, mag);
2835 gtk_adjustment_value_changed(adj);
2837 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2838 gtk_adjustment_set_value(adj, prop);
2839 gtk_adjustment_value_changed(adj);
2841 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2842 gtk_adjustment_set_value(adj, rounded);
2843 gtk_adjustment_value_changed(adj);
2845 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2846 gtk_adjustment_set_value(adj, randomized);
2847 gtk_adjustment_value_changed(adj);
2848 }
2851 // public:
2852 void sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2853 {
2854 GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2855 if (wide) {
2856 gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2857 }
2858 GtkWidget *l = gtk_label_new(NULL);
2859 gtk_label_set_markup(GTK_LABEL(l), title);
2860 gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2861 if ( GTK_IS_TOOLBAR(tbl) ) {
2862 gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2863 } else {
2864 gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2865 }
2866 gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2867 }
2870 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2871 {
2872 Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
2874 {
2875 EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2876 ege_output_action_set_use_markup( act, TRUE );
2877 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2878 g_object_set_data( holder, "mode_action", act );
2879 }
2881 {
2882 EgeAdjustmentAction* eact = 0;
2883 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2884 bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2886 /* Flatsided checkbox */
2887 {
2888 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2890 GtkTreeIter iter;
2891 gtk_list_store_append( model, &iter );
2892 gtk_list_store_set( model, &iter,
2893 0, _("Polygon"),
2894 1, _("Regular polygon (with one handle) instead of a star"),
2895 2, INKSCAPE_ICON_DRAW_POLYGON,
2896 -1 );
2898 gtk_list_store_append( model, &iter );
2899 gtk_list_store_set( model, &iter,
2900 0, _("Star"),
2901 1, _("Star instead of a regular polygon (with one handle)"),
2902 2, INKSCAPE_ICON_DRAW_STAR,
2903 -1 );
2905 EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2906 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2907 g_object_set_data( holder, "flat_action", act );
2909 ege_select_one_action_set_appearance( act, "full" );
2910 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2911 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2912 ege_select_one_action_set_icon_column( act, 2 );
2913 ege_select_one_action_set_icon_size( act, secondarySize );
2914 ege_select_one_action_set_tooltip_column( act, 1 );
2916 ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2917 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2918 }
2920 /* Magnitude */
2921 {
2922 gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2923 gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2924 eact = create_adjustment_action( "MagnitudeAction",
2925 _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2926 "/tools/shapes/star/magnitude", 3,
2927 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2928 3, 1024, 1, 5,
2929 labels, values, G_N_ELEMENTS(labels),
2930 sp_stb_magnitude_value_changed,
2931 1.0, 0 );
2932 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2933 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2934 }
2936 /* Spoke ratio */
2937 {
2938 gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2939 gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2940 eact = create_adjustment_action( "SpokeAction",
2941 _("Spoke ratio"), _("Spoke ratio:"),
2942 // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2943 // Base radius is the same for the closest handle.
2944 _("Base radius to tip radius ratio"),
2945 "/tools/shapes/star/proportion", 0.5,
2946 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2947 0.01, 1.0, 0.01, 0.1,
2948 labels, values, G_N_ELEMENTS(labels),
2949 sp_stb_proportion_value_changed );
2950 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2951 g_object_set_data( holder, "prop_action", eact );
2952 }
2954 if ( !isFlatSided ) {
2955 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2956 } else {
2957 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2958 }
2960 /* Roundedness */
2961 {
2962 gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2963 gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2964 eact = create_adjustment_action( "RoundednessAction",
2965 _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2966 "/tools/shapes/star/rounded", 0.0,
2967 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2968 -10.0, 10.0, 0.01, 0.1,
2969 labels, values, G_N_ELEMENTS(labels),
2970 sp_stb_rounded_value_changed );
2971 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2972 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2973 }
2975 /* Randomization */
2976 {
2977 gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2978 gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2979 eact = create_adjustment_action( "RandomizationAction",
2980 _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2981 "/tools/shapes/star/randomized", 0.0,
2982 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2983 -10.0, 10.0, 0.001, 0.01,
2984 labels, values, G_N_ELEMENTS(labels),
2985 sp_stb_randomized_value_changed, 0.1, 3 );
2986 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2987 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2988 }
2989 }
2991 {
2992 /* Reset */
2993 {
2994 GtkAction* act = gtk_action_new( "StarResetAction",
2995 _("Defaults"),
2996 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2997 GTK_STOCK_CLEAR );
2998 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2999 gtk_action_group_add_action( mainActions, act );
3000 gtk_action_set_sensitive( act, TRUE );
3001 }
3002 }
3004 sigc::connection *connection = new sigc::connection(
3005 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
3006 );
3007 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3008 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3009 }
3012 //########################
3013 //## Rect ##
3014 //########################
3016 static void sp_rtb_sensitivize( GObject *tbl )
3017 {
3018 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
3019 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
3020 GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
3022 if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
3023 gtk_action_set_sensitive( not_rounded, FALSE );
3024 } else {
3025 gtk_action_set_sensitive( not_rounded, TRUE );
3026 }
3027 }
3030 static void sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
3031 void (*setter)(SPRect *, gdouble))
3032 {
3033 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3035 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
3036 SPUnit const *unit = tracker->getActiveUnit();
3038 if (DocumentUndo::getUndoSensitive(sp_desktop_document(desktop))) {
3039 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3040 prefs->setDouble(Glib::ustring("/tools/shapes/rect/") + value_name, sp_units_get_pixels(adj->value, *unit));
3041 }
3043 // quit if run by the attr_changed listener
3044 if (g_object_get_data( tbl, "freeze" )) {
3045 return;
3046 }
3048 // in turn, prevent listener from responding
3049 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
3051 bool modmade = false;
3052 Inkscape::Selection *selection = sp_desktop_selection(desktop);
3053 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
3054 if (SP_IS_RECT(items->data)) {
3055 if (adj->value != 0) {
3056 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
3057 } else {
3058 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
3059 }
3060 modmade = true;
3061 }
3062 }
3064 sp_rtb_sensitivize( tbl );
3066 if (modmade) {
3067 DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
3068 _("Change rectangle"));
3069 }
3071 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3072 }
3074 static void sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
3075 {
3076 sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
3077 }
3079 static void sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
3080 {
3081 sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
3082 }
3084 static void sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
3085 {
3086 sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
3087 }
3089 static void sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
3090 {
3091 sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
3092 }
3096 static void sp_rtb_defaults( GtkWidget * /*widget*/, GObject *obj)
3097 {
3098 GtkAdjustment *adj = 0;
3100 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
3101 gtk_adjustment_set_value(adj, 0.0);
3102 // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
3103 gtk_adjustment_value_changed(adj);
3105 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
3106 gtk_adjustment_set_value(adj, 0.0);
3107 gtk_adjustment_value_changed(adj);
3109 sp_rtb_sensitivize( obj );
3110 }
3112 static void rect_tb_event_attr_changed(Inkscape::XML::Node * /*repr*/, gchar const * /*name*/,
3113 gchar const * /*old_value*/, gchar const * /*new_value*/,
3114 bool /*is_interactive*/, gpointer data)
3115 {
3116 GObject *tbl = G_OBJECT(data);
3118 // quit if run by the _changed callbacks
3119 if (g_object_get_data( tbl, "freeze" )) {
3120 return;
3121 }
3123 // in turn, prevent callbacks from responding
3124 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3126 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
3127 SPUnit const *unit = tracker->getActiveUnit();
3129 gpointer item = g_object_get_data( tbl, "item" );
3130 if (item && SP_IS_RECT(item)) {
3131 {
3132 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
3133 gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
3134 gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
3135 }
3137 {
3138 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
3139 gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
3140 gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
3141 }
3143 {
3144 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
3145 gdouble width = sp_rect_get_visible_width (SP_RECT(item));
3146 gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
3147 }
3149 {
3150 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
3151 gdouble height = sp_rect_get_visible_height (SP_RECT(item));
3152 gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
3153 }
3154 }
3156 sp_rtb_sensitivize( tbl );
3158 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3159 }
3162 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
3163 NULL, /* child_added */
3164 NULL, /* child_removed */
3165 rect_tb_event_attr_changed,
3166 NULL, /* content_changed */
3167 NULL /* order_changed */
3168 };
3170 /**
3171 * \param selection should not be NULL.
3172 */
3173 static void sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3174 {
3175 int n_selected = 0;
3176 Inkscape::XML::Node *repr = NULL;
3177 SPItem *item = NULL;
3179 if ( g_object_get_data( tbl, "repr" ) ) {
3180 g_object_set_data( tbl, "item", NULL );
3181 }
3182 purge_repr_listener( tbl, tbl );
3184 for (GSList const *items = selection->itemList();
3185 items != NULL;
3186 items = items->next) {
3187 if (SP_IS_RECT((SPItem *) items->data)) {
3188 n_selected++;
3189 item = (SPItem *) items->data;
3190 repr = SP_OBJECT_REPR(item);
3191 }
3192 }
3194 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3196 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3198 if (n_selected == 0) {
3199 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3201 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3202 gtk_action_set_sensitive(w, FALSE);
3203 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3204 gtk_action_set_sensitive(h, FALSE);
3206 } else if (n_selected == 1) {
3207 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3208 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3210 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3211 gtk_action_set_sensitive(w, TRUE);
3212 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3213 gtk_action_set_sensitive(h, TRUE);
3215 if (repr) {
3216 g_object_set_data( tbl, "repr", repr );
3217 g_object_set_data( tbl, "item", item );
3218 Inkscape::GC::anchor(repr);
3219 sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
3220 sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
3221 }
3222 } else {
3223 // FIXME: implement averaging of all parameters for multiple selected
3224 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3225 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3226 sp_rtb_sensitivize( tbl );
3227 }
3228 }
3231 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3232 {
3233 EgeAdjustmentAction* eact = 0;
3234 Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
3236 {
3237 EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
3238 ege_output_action_set_use_markup( act, TRUE );
3239 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3240 g_object_set_data( holder, "mode_action", act );
3241 }
3243 // rx/ry units menu: create
3244 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
3245 //tracker->addUnit( SP_UNIT_PERCENT, 0 );
3246 // fixme: add % meaning per cent of the width/height
3247 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
3248 g_object_set_data( holder, "tracker", tracker );
3250 /* W */
3251 {
3252 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3253 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3254 eact = create_adjustment_action( "RectWidthAction",
3255 _("Width"), _("W:"), _("Width of rectangle"),
3256 "/tools/shapes/rect/width", 0,
3257 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
3258 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3259 labels, values, G_N_ELEMENTS(labels),
3260 sp_rtb_width_value_changed );
3261 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3262 g_object_set_data( holder, "width_action", eact );
3263 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3264 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3265 }
3267 /* H */
3268 {
3269 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3270 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3271 eact = create_adjustment_action( "RectHeightAction",
3272 _("Height"), _("H:"), _("Height of rectangle"),
3273 "/tools/shapes/rect/height", 0,
3274 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3275 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3276 labels, values, G_N_ELEMENTS(labels),
3277 sp_rtb_height_value_changed );
3278 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3279 g_object_set_data( holder, "height_action", eact );
3280 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3281 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3282 }
3284 /* rx */
3285 {
3286 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3287 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3288 eact = create_adjustment_action( "RadiusXAction",
3289 _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
3290 "/tools/shapes/rect/rx", 0,
3291 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3292 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3293 labels, values, G_N_ELEMENTS(labels),
3294 sp_rtb_rx_value_changed);
3295 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3296 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3297 }
3299 /* ry */
3300 {
3301 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3302 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3303 eact = create_adjustment_action( "RadiusYAction",
3304 _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
3305 "/tools/shapes/rect/ry", 0,
3306 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3307 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3308 labels, values, G_N_ELEMENTS(labels),
3309 sp_rtb_ry_value_changed);
3310 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3311 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3312 }
3314 // add the units menu
3315 {
3316 GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
3317 gtk_action_group_add_action( mainActions, act );
3318 }
3320 /* Reset */
3321 {
3322 InkAction* inky = ink_action_new( "RectResetAction",
3323 _("Not rounded"),
3324 _("Make corners sharp"),
3325 INKSCAPE_ICON_RECTANGLE_MAKE_CORNERS_SHARP,
3326 secondarySize );
3327 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
3328 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3329 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3330 g_object_set_data( holder, "not_rounded", inky );
3331 }
3333 g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
3334 sp_rtb_sensitivize( holder );
3336 sigc::connection *connection = new sigc::connection(
3337 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
3338 );
3339 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3340 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3341 }
3343 //########################
3344 //## 3D Box ##
3345 //########################
3347 // normalize angle so that it lies in the interval [0,360]
3348 static double box3d_normalize_angle (double a) {
3349 double angle = a + ((int) (a/360.0))*360;
3350 if (angle < 0) {
3351 angle += 360.0;
3352 }
3353 return angle;
3354 }
3356 static void box3d_set_button_and_adjustment(Persp3D *persp,
3357 Proj::Axis axis,
3358 GtkAdjustment *adj,
3359 GtkAction *act,
3360 GtkToggleAction *tact)
3361 {
3362 // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
3363 // have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
3364 // are reset).
3365 bool is_infinite = !persp3d_VP_is_finite(persp->perspective_impl, axis);
3367 if (is_infinite) {
3368 gtk_toggle_action_set_active(tact, TRUE);
3369 gtk_action_set_sensitive(act, TRUE);
3371 double angle = persp3d_get_infinite_angle(persp, axis);
3372 if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
3373 gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
3374 }
3375 } else {
3376 gtk_toggle_action_set_active(tact, FALSE);
3377 gtk_action_set_sensitive(act, FALSE);
3378 }
3379 }
3381 static void box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data)
3382 {
3383 if (!persp_repr) {
3384 g_print ("No perspective given to box3d_resync_toolbar().\n");
3385 return;
3386 }
3388 GtkWidget *tbl = GTK_WIDGET(data);
3389 GtkAdjustment *adj = 0;
3390 GtkAction *act = 0;
3391 GtkToggleAction *tact = 0;
3392 Persp3D *persp = persp3d_get_from_repr(persp_repr);
3393 if (!persp) {
3394 // Hmm, is it an error if this happens?
3395 return;
3396 }
3397 {
3398 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
3399 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
3400 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
3402 box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
3403 }
3404 {
3405 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
3406 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
3407 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
3409 box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
3410 }
3411 {
3412 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
3413 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
3414 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
3416 box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
3417 }
3418 }
3420 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr,
3421 gchar const * /*name*/,
3422 gchar const * /*old_value*/,
3423 gchar const * /*new_value*/,
3424 bool /*is_interactive*/,
3425 gpointer data)
3426 {
3427 GtkWidget *tbl = GTK_WIDGET(data);
3429 // quit if run by the attr_changed or selection changed listener
3430 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3431 return;
3432 }
3434 // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
3435 // SPDocumentUndo::maybeDone() when the document is undo insensitive)
3436 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3438 // TODO: Only update the appropriate part of the toolbar
3439 // if (!strcmp(name, "inkscape:vp_z")) {
3440 box3d_resync_toolbar(repr, G_OBJECT(tbl));
3441 // }
3443 Persp3D *persp = persp3d_get_from_repr(repr);
3444 persp3d_update_box_reprs(persp);
3446 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3447 }
3449 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
3450 {
3451 NULL, /* child_added */
3452 NULL, /* child_removed */
3453 box3d_persp_tb_event_attr_changed,
3454 NULL, /* content_changed */
3455 NULL /* order_changed */
3456 };
3458 /**
3459 * \param selection Should not be NULL.
3460 */
3461 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
3462 // Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
3463 static void box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3464 {
3465 // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
3466 // disable the angle entry fields for this direction (otherwise entering a value in them should only
3467 // update the perspectives with infinite VPs and leave the other ones untouched).
3469 Inkscape::XML::Node *persp_repr = NULL;
3470 purge_repr_listener(tbl, tbl);
3472 SPItem *item = selection->singleItem();
3473 if (item && SP_IS_BOX3D(item)) {
3474 // FIXME: Also deal with multiple selected boxes
3475 SPBox3D *box = SP_BOX3D(item);
3476 Persp3D *persp = box3d_get_perspective(box);
3477 persp_repr = SP_OBJECT_REPR(persp);
3478 if (persp_repr) {
3479 g_object_set_data(tbl, "repr", persp_repr);
3480 Inkscape::GC::anchor(persp_repr);
3481 sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
3482 sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
3483 }
3485 inkscape_active_document()->setCurrentPersp3D(persp3d_get_from_repr(persp_repr));
3486 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3487 prefs->setString("/tools/shapes/3dbox/persp", persp_repr->attribute("id"));
3489 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
3490 box3d_resync_toolbar(persp_repr, tbl);
3491 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
3492 }
3493 }
3495 static void box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
3496 {
3497 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3498 SPDocument *document = sp_desktop_document(desktop);
3500 // quit if run by the attr_changed or selection changed listener
3501 if (g_object_get_data( dataKludge, "freeze" )) {
3502 return;
3503 }
3505 // in turn, prevent listener from responding
3506 g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
3508 std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
3509 if (sel_persps.empty()) {
3510 // this can happen when the document is created; we silently ignore it
3511 return;
3512 }
3513 Persp3D *persp = sel_persps.front();
3515 persp->perspective_impl->tmat.set_infinite_direction (axis, adj->value);
3516 SP_OBJECT(persp)->updateRepr();
3518 // TODO: use the correct axis here, too
3519 DocumentUndo::maybeDone(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
3521 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
3522 }
3525 static void box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3526 {
3527 box3d_angle_value_changed(adj, dataKludge, Proj::X);
3528 }
3530 static void box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3531 {
3532 box3d_angle_value_changed(adj, dataKludge, Proj::Y);
3533 }
3535 static void box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3536 {
3537 box3d_angle_value_changed(adj, dataKludge, Proj::Z);
3538 }
3541 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction * /*box3d_angle*/, Proj::Axis axis )
3542 {
3543 // TODO: Take all selected perspectives into account
3544 std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
3545 if (sel_persps.empty()) {
3546 // this can happen when the document is created; we silently ignore it
3547 return;
3548 }
3549 Persp3D *persp = sel_persps.front();
3551 bool set_infinite = gtk_toggle_action_get_active(act);
3552 persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
3553 }
3555 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3556 {
3557 box3d_vp_state_changed(act, box3d_angle, Proj::X);
3558 }
3560 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3561 {
3562 box3d_vp_state_changed(act, box3d_angle, Proj::Y);
3563 }
3565 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3566 {
3567 box3d_vp_state_changed(act, box3d_angle, Proj::Z);
3568 }
3570 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3571 {
3572 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3573 EgeAdjustmentAction* eact = 0;
3574 SPDocument *document = sp_desktop_document (desktop);
3575 Persp3DImpl *persp_impl = document->getCurrentPersp3DImpl();
3577 EgeAdjustmentAction* box3d_angle_x = 0;
3578 EgeAdjustmentAction* box3d_angle_y = 0;
3579 EgeAdjustmentAction* box3d_angle_z = 0;
3581 /* Angle X */
3582 {
3583 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3584 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3585 eact = create_adjustment_action( "3DBoxAngleXAction",
3586 _("Angle in X direction"), _("Angle X:"),
3587 // Translators: PL is short for 'perspective line'
3588 _("Angle of PLs in X direction"),
3589 "/tools/shapes/3dbox/box3d_angle_x", 30,
3590 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
3591 -360.0, 360.0, 1.0, 10.0,
3592 labels, values, G_N_ELEMENTS(labels),
3593 box3d_angle_x_value_changed );
3594 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3595 g_object_set_data( holder, "box3d_angle_x_action", eact );
3596 box3d_angle_x = eact;
3597 }
3599 if (!persp_impl || !persp3d_VP_is_finite(persp_impl, Proj::X)) {
3600 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3601 } else {
3602 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3603 }
3606 /* VP X state */
3607 {
3608 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
3609 // Translators: VP is short for 'vanishing point'
3610 _("State of VP in X direction"),
3611 _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
3612 INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3613 Inkscape::ICON_SIZE_DECORATION );
3614 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3615 g_object_set_data( holder, "box3d_vp_x_state_action", act );
3616 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
3617 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3618 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3619 }
3621 /* Angle Y */
3622 {
3623 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3624 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3625 eact = create_adjustment_action( "3DBoxAngleYAction",
3626 _("Angle in Y direction"), _("Angle Y:"),
3627 // Translators: PL is short for 'perspective line'
3628 _("Angle of PLs in Y direction"),
3629 "/tools/shapes/3dbox/box3d_angle_y", 30,
3630 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3631 -360.0, 360.0, 1.0, 10.0,
3632 labels, values, G_N_ELEMENTS(labels),
3633 box3d_angle_y_value_changed );
3634 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3635 g_object_set_data( holder, "box3d_angle_y_action", eact );
3636 box3d_angle_y = eact;
3637 }
3639 if (!persp_impl || !persp3d_VP_is_finite(persp_impl, Proj::Y)) {
3640 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3641 } else {
3642 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3643 }
3645 /* VP Y state */
3646 {
3647 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
3648 // Translators: VP is short for 'vanishing point'
3649 _("State of VP in Y direction"),
3650 _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
3651 INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3652 Inkscape::ICON_SIZE_DECORATION );
3653 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3654 g_object_set_data( holder, "box3d_vp_y_state_action", act );
3655 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
3656 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3657 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3658 }
3660 /* Angle Z */
3661 {
3662 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3663 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3664 eact = create_adjustment_action( "3DBoxAngleZAction",
3665 _("Angle in Z direction"), _("Angle Z:"),
3666 // Translators: PL is short for 'perspective line'
3667 _("Angle of PLs in Z direction"),
3668 "/tools/shapes/3dbox/box3d_angle_z", 30,
3669 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3670 -360.0, 360.0, 1.0, 10.0,
3671 labels, values, G_N_ELEMENTS(labels),
3672 box3d_angle_z_value_changed );
3673 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3674 g_object_set_data( holder, "box3d_angle_z_action", eact );
3675 box3d_angle_z = eact;
3676 }
3678 if (!persp_impl || !persp3d_VP_is_finite(persp_impl, Proj::Z)) {
3679 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3680 } else {
3681 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3682 }
3684 /* VP Z state */
3685 {
3686 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3687 // Translators: VP is short for 'vanishing point'
3688 _("State of VP in Z direction"),
3689 _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3690 INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3691 Inkscape::ICON_SIZE_DECORATION );
3692 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3693 g_object_set_data( holder, "box3d_vp_z_state_action", act );
3694 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3695 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3696 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3697 }
3699 sigc::connection *connection = new sigc::connection(
3700 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3701 );
3702 g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3703 g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3704 }
3706 //########################
3707 //## Spiral ##
3708 //########################
3710 static void sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, Glib::ustring const &value_name)
3711 {
3712 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3714 if (DocumentUndo::getUndoSensitive(sp_desktop_document(desktop))) {
3715 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3716 prefs->setDouble("/tools/shapes/spiral/" + value_name, adj->value);
3717 }
3719 // quit if run by the attr_changed listener
3720 if (g_object_get_data( tbl, "freeze" )) {
3721 return;
3722 }
3724 // in turn, prevent listener from responding
3725 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3727 gchar* namespaced_name = g_strconcat("sodipodi:", value_name.data(), NULL);
3729 bool modmade = false;
3730 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3731 items != NULL;
3732 items = items->next)
3733 {
3734 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3735 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3736 sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3737 SP_OBJECT((SPItem *) items->data)->updateRepr();
3738 modmade = true;
3739 }
3740 }
3742 g_free(namespaced_name);
3744 if (modmade) {
3745 DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3746 _("Change spiral"));
3747 }
3749 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3750 }
3752 static void sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3753 {
3754 sp_spl_tb_value_changed(adj, tbl, "revolution");
3755 }
3757 static void sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3758 {
3759 sp_spl_tb_value_changed(adj, tbl, "expansion");
3760 }
3762 static void sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3763 {
3764 sp_spl_tb_value_changed(adj, tbl, "t0");
3765 }
3767 static void sp_spl_tb_defaults(GtkWidget * /*widget*/, GtkObject *obj)
3768 {
3769 GtkWidget *tbl = GTK_WIDGET(obj);
3771 GtkAdjustment *adj;
3773 // fixme: make settable
3774 gdouble rev = 5;
3775 gdouble exp = 1.0;
3776 gdouble t0 = 0.0;
3778 adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3779 gtk_adjustment_set_value(adj, rev);
3780 gtk_adjustment_value_changed(adj);
3782 adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3783 gtk_adjustment_set_value(adj, exp);
3784 gtk_adjustment_value_changed(adj);
3786 adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3787 gtk_adjustment_set_value(adj, t0);
3788 gtk_adjustment_value_changed(adj);
3790 spinbutton_defocus(GTK_OBJECT(tbl));
3791 }
3794 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr,
3795 gchar const * /*name*/,
3796 gchar const * /*old_value*/,
3797 gchar const * /*new_value*/,
3798 bool /*is_interactive*/,
3799 gpointer data)
3800 {
3801 GtkWidget *tbl = GTK_WIDGET(data);
3803 // quit if run by the _changed callbacks
3804 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3805 return;
3806 }
3808 // in turn, prevent callbacks from responding
3809 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3811 GtkAdjustment *adj;
3812 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3813 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3815 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3816 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3818 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3819 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3821 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3822 }
3825 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3826 NULL, /* child_added */
3827 NULL, /* child_removed */
3828 spiral_tb_event_attr_changed,
3829 NULL, /* content_changed */
3830 NULL /* order_changed */
3831 };
3833 static void sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3834 {
3835 int n_selected = 0;
3836 Inkscape::XML::Node *repr = NULL;
3838 purge_repr_listener( tbl, tbl );
3840 for (GSList const *items = selection->itemList();
3841 items != NULL;
3842 items = items->next)
3843 {
3844 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3845 n_selected++;
3846 repr = SP_OBJECT_REPR((SPItem *) items->data);
3847 }
3848 }
3850 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3852 if (n_selected == 0) {
3853 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3854 } else if (n_selected == 1) {
3855 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3857 if (repr) {
3858 g_object_set_data( tbl, "repr", repr );
3859 Inkscape::GC::anchor(repr);
3860 sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3861 sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3862 }
3863 } else {
3864 // FIXME: implement averaging of all parameters for multiple selected
3865 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3866 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3867 }
3868 }
3871 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3872 {
3873 EgeAdjustmentAction* eact = 0;
3874 Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
3876 {
3877 EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3878 ege_output_action_set_use_markup( act, TRUE );
3879 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3880 g_object_set_data( holder, "mode_action", act );
3881 }
3883 /* Revolution */
3884 {
3885 gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3886 gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3887 eact = create_adjustment_action( "SpiralRevolutionAction",
3888 _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3889 "/tools/shapes/spiral/revolution", 3.0,
3890 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3891 0.01, 1024.0, 0.1, 1.0,
3892 labels, values, G_N_ELEMENTS(labels),
3893 sp_spl_tb_revolution_value_changed, 1, 2);
3894 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3895 }
3897 /* Expansion */
3898 {
3899 gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3900 gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3901 eact = create_adjustment_action( "SpiralExpansionAction",
3902 _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3903 "/tools/shapes/spiral/expansion", 1.0,
3904 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3905 0.0, 1000.0, 0.01, 1.0,
3906 labels, values, G_N_ELEMENTS(labels),
3907 sp_spl_tb_expansion_value_changed);
3908 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3909 }
3911 /* T0 */
3912 {
3913 gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3914 gdouble values[] = {0, 0.5, 0.9};
3915 eact = create_adjustment_action( "SpiralT0Action",
3916 _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3917 "/tools/shapes/spiral/t0", 0.0,
3918 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3919 0.0, 0.999, 0.01, 1.0,
3920 labels, values, G_N_ELEMENTS(labels),
3921 sp_spl_tb_t0_value_changed);
3922 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3923 }
3925 /* Reset */
3926 {
3927 InkAction* inky = ink_action_new( "SpiralResetAction",
3928 _("Defaults"),
3929 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3930 GTK_STOCK_CLEAR,
3931 secondarySize );
3932 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3933 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3934 }
3937 sigc::connection *connection = new sigc::connection(
3938 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3939 );
3940 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3941 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3942 }
3944 //########################
3945 //## Pen/Pencil ##
3946 //########################
3948 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3949 static Glib::ustring const freehand_tool_name(GObject *dataKludge)
3950 {
3951 SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3952 return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3953 ? "/tools/freehand/pen"
3954 : "/tools/freehand/pencil" );
3955 }
3957 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3958 {
3959 gint mode = ege_select_one_action_get_active(act);
3961 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3962 prefs->setInt(freehand_tool_name(tbl) + "/freehand-mode", mode);
3964 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3966 // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3967 // preparatory work here
3968 if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3969 SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3970 sp_pen_context_set_polyline_mode(pc);
3971 }
3972 }
3974 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3975 {
3976 /* Freehand mode toggle buttons */
3977 {
3978 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3979 guint freehandMode = prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/freehand-mode" : "/tools/freehand/pen/freehand-mode" ), 0);
3980 Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
3982 {
3983 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3985 GtkTreeIter iter;
3986 gtk_list_store_append( model, &iter );
3987 gtk_list_store_set( model, &iter,
3988 0, _("Bezier"),
3989 1, _("Create regular Bezier path"),
3990 2, INKSCAPE_ICON_PATH_MODE_BEZIER,
3991 -1 );
3993 gtk_list_store_append( model, &iter );
3994 gtk_list_store_set( model, &iter,
3995 0, _("Spiro"),
3996 1, _("Create Spiro path"),
3997 2, INKSCAPE_ICON_PATH_MODE_SPIRO,
3998 -1 );
4000 if (!tool_is_pencil) {
4001 gtk_list_store_append( model, &iter );
4002 gtk_list_store_set( model, &iter,
4003 0, _("Zigzag"),
4004 1, _("Create a sequence of straight line segments"),
4005 2, INKSCAPE_ICON_PATH_MODE_POLYLINE,
4006 -1 );
4008 gtk_list_store_append( model, &iter );
4009 gtk_list_store_set( model, &iter,
4010 0, _("Paraxial"),
4011 1, _("Create a sequence of paraxial line segments"),
4012 2, INKSCAPE_ICON_PATH_MODE_POLYLINE_PARAXIAL,
4013 -1 );
4014 }
4016 EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
4017 "FreehandModeActionPencil" :
4018 "FreehandModeActionPen",
4019 (_("Mode:")), (_("Mode of new lines drawn by this tool")), NULL, GTK_TREE_MODEL(model) );
4020 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4022 ege_select_one_action_set_appearance( act, "full" );
4023 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4024 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4025 ege_select_one_action_set_icon_column( act, 2 );
4026 ege_select_one_action_set_icon_size( act, secondarySize );
4027 ege_select_one_action_set_tooltip_column( act, 1 );
4029 ege_select_one_action_set_active( act, freehandMode);
4030 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
4031 }
4032 }
4033 }
4035 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
4036 gint shape = ege_select_one_action_get_active( act );
4037 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4038 prefs->setInt(freehand_tool_name(dataKludge) + "/shape", shape);
4039 }
4041 /**
4042 * \brief Generate the list of freehand advanced shape option entries.
4043 */
4044 static GList * freehand_shape_dropdown_items_list() {
4045 GList *glist = NULL;
4047 glist = g_list_append (glist, _("None"));
4048 glist = g_list_append (glist, _("Triangle in"));
4049 glist = g_list_append (glist, _("Triangle out"));
4050 glist = g_list_append (glist, _("Ellipse"));
4051 glist = g_list_append (glist, _("From clipboard"));
4053 return glist;
4054 }
4056 static void freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
4057 {
4058 /*advanced shape options */
4059 {
4060 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4061 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
4063 GList* items = 0;
4064 gint count = 0;
4065 for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
4066 {
4067 GtkTreeIter iter;
4068 gtk_list_store_append( model, &iter );
4069 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
4070 count++;
4071 }
4072 g_list_free( items );
4073 items = 0;
4074 EgeSelectOneAction* act1 = ege_select_one_action_new(
4075 tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
4076 _("Shape:"), (_("Shape of new paths drawn by this tool")), NULL, GTK_TREE_MODEL(model));
4077 g_object_set( act1, "short_label", _("Shape:"), NULL );
4078 ege_select_one_action_set_appearance( act1, "compact" );
4079 ege_select_one_action_set_active( act1, prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/shape" : "/tools/freehand/pen/shape" ), 0) );
4080 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
4081 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
4082 g_object_set_data( holder, "shape_action", act1 );
4083 }
4084 }
4086 static void sp_pen_toolbox_prep(SPDesktop * /*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4087 {
4088 sp_add_freehand_mode_toggle(mainActions, holder, false);
4089 freehand_add_advanced_shape_options(mainActions, holder, false);
4090 }
4093 static void sp_pencil_tb_defaults(GtkWidget * /*widget*/, GtkObject *obj)
4094 {
4095 GtkWidget *tbl = GTK_WIDGET(obj);
4097 GtkAdjustment *adj;
4099 // fixme: make settable
4100 gdouble tolerance = 4;
4102 adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
4103 gtk_adjustment_set_value(adj, tolerance);
4104 gtk_adjustment_value_changed(adj);
4106 spinbutton_defocus(GTK_OBJECT(tbl));
4107 }
4109 static void sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
4110 {
4111 // quit if run by the attr_changed listener
4112 if (g_object_get_data( tbl, "freeze" )) {
4113 return;
4114 }
4115 // in turn, prevent listener from responding
4116 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4117 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4118 prefs->setDouble("/tools/freehand/pencil/tolerance", adj->value);
4119 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4120 }
4122 /*
4123 class PencilToleranceObserver : public Inkscape::Preferences::Observer {
4124 public:
4125 PencilToleranceObserver(Glib::ustring const &path, GObject *x) : Observer(path), _obj(x)
4126 {
4127 g_object_set_data(_obj, "prefobserver", this);
4128 }
4129 virtual ~PencilToleranceObserver() {
4130 if (g_object_get_data(_obj, "prefobserver") == this) {
4131 g_object_set_data(_obj, "prefobserver", NULL);
4132 }
4133 }
4134 virtual void notify(Inkscape::Preferences::Entry const &val) {
4135 GObject* tbl = _obj;
4136 if (g_object_get_data( tbl, "freeze" )) {
4137 return;
4138 }
4139 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4141 GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl, "tolerance");
4143 double v = val.getDouble(adj->value);
4144 gtk_adjustment_set_value(adj, v);
4145 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4146 }
4147 private:
4148 GObject *_obj;
4149 };
4150 */
4152 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4153 {
4154 sp_add_freehand_mode_toggle(mainActions, holder, true);
4156 EgeAdjustmentAction* eact = 0;
4158 /* Tolerance */
4159 {
4160 gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
4161 gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
4162 eact = create_adjustment_action( "PencilToleranceAction",
4163 _("Smoothing:"), _("Smoothing: "),
4164 _("How much smoothing (simplifying) is applied to the line"),
4165 "/tools/freehand/pencil/tolerance",
4166 3.0,
4167 GTK_WIDGET(desktop->canvas), NULL,
4168 holder, TRUE, "altx-pencil",
4169 1, 100.0, 0.5, 1.0,
4170 labels, values, G_N_ELEMENTS(labels),
4171 sp_pencil_tb_tolerance_value_changed,
4172 1, 2);
4173 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4174 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4175 }
4177 /* advanced shape options */
4178 freehand_add_advanced_shape_options(mainActions, holder, true);
4180 /* Reset */
4181 {
4182 InkAction* inky = ink_action_new( "PencilResetAction",
4183 _("Defaults"),
4184 _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
4185 GTK_STOCK_CLEAR,
4186 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4187 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
4188 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4189 }
4191 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4193 }
4196 //########################
4197 //## Tweak ##
4198 //########################
4200 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject * /*tbl*/ )
4201 {
4202 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4203 prefs->setDouble( "/tools/tweak/width", adj->value * 0.01 );
4204 }
4206 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject * /*tbl*/ )
4207 {
4208 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4209 prefs->setDouble( "/tools/tweak/force", adj->value * 0.01 );
4210 }
4212 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4213 {
4214 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4215 prefs->setBool("/tools/tweak/usepressure", gtk_toggle_action_get_active(act));
4216 }
4218 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4219 {
4220 int mode = ege_select_one_action_get_active( act );
4221 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4222 prefs->setInt("/tools/tweak/mode", mode);
4224 static gchar const* names[] = {"tweak_doh", "tweak_dos", "tweak_dol", "tweak_doo", "tweak_channels_label"};
4225 bool flag = ((mode == TWEAK_MODE_COLORPAINT) || (mode == TWEAK_MODE_COLORJITTER));
4226 for (size_t i = 0; i < G_N_ELEMENTS(names); ++i) {
4227 GtkAction *act = GTK_ACTION(g_object_get_data( tbl, names[i] ));
4228 if (act) {
4229 gtk_action_set_sensitive(act, flag);
4230 }
4231 }
4232 GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
4233 if (fid) {
4234 gtk_action_set_sensitive(fid, !flag);
4235 }
4236 }
4238 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject * /*tbl*/ )
4239 {
4240 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4241 prefs->setDouble( "/tools/tweak/fidelity", adj->value * 0.01 );
4242 }
4244 static void tweak_toggle_doh(GtkToggleAction *act, gpointer /*data*/) {
4245 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4246 prefs->setBool("/tools/tweak/doh", gtk_toggle_action_get_active(act));
4247 }
4248 static void tweak_toggle_dos(GtkToggleAction *act, gpointer /*data*/) {
4249 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4250 prefs->setBool("/tools/tweak/dos", gtk_toggle_action_get_active(act));
4251 }
4252 static void tweak_toggle_dol(GtkToggleAction *act, gpointer /*data*/) {
4253 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4254 prefs->setBool("/tools/tweak/dol", gtk_toggle_action_get_active(act));
4255 }
4256 static void tweak_toggle_doo(GtkToggleAction *act, gpointer /*data*/) {
4257 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4258 prefs->setBool("/tools/tweak/doo", gtk_toggle_action_get_active(act));
4259 }
4261 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4262 {
4263 Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
4264 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4266 {
4267 /* Width */
4268 gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
4269 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4270 EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
4271 _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
4272 "/tools/tweak/width", 15,
4273 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
4274 1, 100, 1.0, 10.0,
4275 labels, values, G_N_ELEMENTS(labels),
4276 sp_tweak_width_value_changed, 0.01, 0, 100 );
4277 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4278 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4279 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4280 }
4283 {
4284 /* Force */
4285 gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
4286 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4287 EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
4288 _("Force"), _("Force:"), _("The force of the tweak action"),
4289 "/tools/tweak/force", 20,
4290 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
4291 1, 100, 1.0, 10.0,
4292 labels, values, G_N_ELEMENTS(labels),
4293 sp_tweak_force_value_changed, 0.01, 0, 100 );
4294 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4295 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4296 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4297 }
4299 /* Mode */
4300 {
4301 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4303 GtkTreeIter iter;
4304 gtk_list_store_append( model, &iter );
4305 gtk_list_store_set( model, &iter,
4306 0, _("Move mode"),
4307 1, _("Move objects in any direction"),
4308 2, INKSCAPE_ICON_OBJECT_TWEAK_PUSH,
4309 -1 );
4311 gtk_list_store_append( model, &iter );
4312 gtk_list_store_set( model, &iter,
4313 0, _("Move in/out mode"),
4314 1, _("Move objects towards cursor; with Shift from cursor"),
4315 2, INKSCAPE_ICON_OBJECT_TWEAK_ATTRACT,
4316 -1 );
4318 gtk_list_store_append( model, &iter );
4319 gtk_list_store_set( model, &iter,
4320 0, _("Move jitter mode"),
4321 1, _("Move objects in random directions"),
4322 2, INKSCAPE_ICON_OBJECT_TWEAK_RANDOMIZE,
4323 -1 );
4325 gtk_list_store_append( model, &iter );
4326 gtk_list_store_set( model, &iter,
4327 0, _("Scale mode"),
4328 1, _("Shrink objects, with Shift enlarge"),
4329 2, INKSCAPE_ICON_OBJECT_TWEAK_SHRINK,
4330 -1 );
4332 gtk_list_store_append( model, &iter );
4333 gtk_list_store_set( model, &iter,
4334 0, _("Rotate mode"),
4335 1, _("Rotate objects, with Shift counterclockwise"),
4336 2, INKSCAPE_ICON_OBJECT_TWEAK_ROTATE,
4337 -1 );
4339 gtk_list_store_append( model, &iter );
4340 gtk_list_store_set( model, &iter,
4341 0, _("Duplicate/delete mode"),
4342 1, _("Duplicate objects, with Shift delete"),
4343 2, INKSCAPE_ICON_OBJECT_TWEAK_DUPLICATE,
4344 -1 );
4346 gtk_list_store_append( model, &iter );
4347 gtk_list_store_set( model, &iter,
4348 0, _("Push mode"),
4349 1, _("Push parts of paths in any direction"),
4350 2, INKSCAPE_ICON_PATH_TWEAK_PUSH,
4351 -1 );
4353 gtk_list_store_append( model, &iter );
4354 gtk_list_store_set( model, &iter,
4355 0, _("Shrink/grow mode"),
4356 1, _("Shrink (inset) parts of paths; with Shift grow (outset)"),
4357 2, INKSCAPE_ICON_PATH_TWEAK_SHRINK,
4358 -1 );
4360 gtk_list_store_append( model, &iter );
4361 gtk_list_store_set( model, &iter,
4362 0, _("Attract/repel mode"),
4363 1, _("Attract parts of paths towards cursor; with Shift from cursor"),
4364 2, INKSCAPE_ICON_PATH_TWEAK_ATTRACT,
4365 -1 );
4367 gtk_list_store_append( model, &iter );
4368 gtk_list_store_set( model, &iter,
4369 0, _("Roughen mode"),
4370 1, _("Roughen parts of paths"),
4371 2, INKSCAPE_ICON_PATH_TWEAK_ROUGHEN,
4372 -1 );
4374 gtk_list_store_append( model, &iter );
4375 gtk_list_store_set( model, &iter,
4376 0, _("Color paint mode"),
4377 1, _("Paint the tool's color upon selected objects"),
4378 2, INKSCAPE_ICON_OBJECT_TWEAK_PAINT,
4379 -1 );
4381 gtk_list_store_append( model, &iter );
4382 gtk_list_store_set( model, &iter,
4383 0, _("Color jitter mode"),
4384 1, _("Jitter the colors of selected objects"),
4385 2, INKSCAPE_ICON_OBJECT_TWEAK_JITTER_COLOR,
4386 -1 );
4388 gtk_list_store_append( model, &iter );
4389 gtk_list_store_set( model, &iter,
4390 0, _("Blur mode"),
4391 1, _("Blur selected objects more; with Shift, blur less"),
4392 2, INKSCAPE_ICON_OBJECT_TWEAK_BLUR,
4393 -1 );
4396 EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4397 g_object_set( act, "short_label", _("Mode:"), NULL );
4398 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4399 g_object_set_data( holder, "mode_action", act );
4401 ege_select_one_action_set_appearance( act, "full" );
4402 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4403 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4404 ege_select_one_action_set_icon_column( act, 2 );
4405 ege_select_one_action_set_icon_size( act, secondarySize );
4406 ege_select_one_action_set_tooltip_column( act, 1 );
4408 gint mode = prefs->getInt("/tools/tweak/mode", 0);
4409 ege_select_one_action_set_active( act, mode );
4410 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
4412 g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
4413 }
4415 guint mode = prefs->getInt("/tools/tweak/mode", 0);
4417 {
4418 EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
4419 ege_output_action_set_use_markup( act, TRUE );
4420 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4421 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER) {
4422 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4423 }
4424 g_object_set_data( holder, "tweak_channels_label", act);
4425 }
4427 {
4428 InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
4429 _("Hue"),
4430 _("In color mode, act on objects' hue"),
4431 NULL,
4432 Inkscape::ICON_SIZE_DECORATION );
4433 //TRANSLATORS: "H" here stands for hue
4434 g_object_set( act, "short_label", _("H"), NULL );
4435 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4436 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
4437 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doh", true) );
4438 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER) {
4439 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4440 }
4441 g_object_set_data( holder, "tweak_doh", act);
4442 }
4443 {
4444 InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
4445 _("Saturation"),
4446 _("In color mode, act on objects' saturation"),
4447 NULL,
4448 Inkscape::ICON_SIZE_DECORATION );
4449 //TRANSLATORS: "S" here stands for Saturation
4450 g_object_set( act, "short_label", _("S"), NULL );
4451 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4452 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
4453 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dos", true) );
4454 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER) {
4455 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4456 }
4457 g_object_set_data( holder, "tweak_dos", act );
4458 }
4459 {
4460 InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
4461 _("Lightness"),
4462 _("In color mode, act on objects' lightness"),
4463 NULL,
4464 Inkscape::ICON_SIZE_DECORATION );
4465 //TRANSLATORS: "L" here stands for Lightness
4466 g_object_set( act, "short_label", _("L"), NULL );
4467 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4468 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
4469 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dol", true) );
4470 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER) {
4471 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4472 }
4473 g_object_set_data( holder, "tweak_dol", act );
4474 }
4475 {
4476 InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
4477 _("Opacity"),
4478 _("In color mode, act on objects' opacity"),
4479 NULL,
4480 Inkscape::ICON_SIZE_DECORATION );
4481 //TRANSLATORS: "O" here stands for Opacity
4482 g_object_set( act, "short_label", _("O"), NULL );
4483 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4484 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
4485 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doo", true) );
4486 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER) {
4487 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4488 }
4489 g_object_set_data( holder, "tweak_doo", act );
4490 }
4492 { /* Fidelity */
4493 gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
4494 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4495 EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
4496 _("Fidelity"), _("Fidelity:"),
4497 _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
4498 "/tools/tweak/fidelity", 50,
4499 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
4500 1, 100, 1.0, 10.0,
4501 labels, values, G_N_ELEMENTS(labels),
4502 sp_tweak_fidelity_value_changed, 0.01, 0, 100 );
4503 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4504 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4505 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
4506 gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
4507 }
4508 g_object_set_data( holder, "tweak_fidelity", eact );
4509 }
4512 /* Use Pressure button */
4513 {
4514 InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
4515 _("Pressure"),
4516 _("Use the pressure of the input device to alter the force of tweak action"),
4517 INKSCAPE_ICON_DRAW_USE_PRESSURE,
4518 Inkscape::ICON_SIZE_DECORATION );
4519 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4520 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
4521 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/usepressure", true) );
4522 }
4524 }
4527 //########################
4528 //## Spray ##
4529 //########################
4531 static void sp_spray_width_value_changed( GtkAdjustment *adj, GObject * /*tbl*/ )
4532 {
4533 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4534 prefs->setDouble( "/tools/spray/width", adj->value );
4535 }
4537 static void sp_spray_mean_value_changed( GtkAdjustment *adj, GObject * /*tbl*/ )
4538 {
4539 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4540 prefs->setDouble( "/tools/spray/mean", adj->value );
4541 }
4543 static void sp_spray_standard_deviation_value_changed( GtkAdjustment *adj, GObject * /*tbl*/ )
4544 {
4545 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4546 prefs->setDouble( "/tools/spray/standard_deviation", adj->value );
4547 }
4549 static void sp_spray_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4550 {
4551 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4552 prefs->setBool("/tools/spray/usepressure", gtk_toggle_action_get_active(act));
4553 }
4555 static void sp_spray_mode_changed( EgeSelectOneAction *act, GObject * /*tbl*/ )
4556 {
4557 int mode = ege_select_one_action_get_active( act );
4558 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4559 prefs->setInt("/tools/spray/mode", mode);
4560 }
4562 static void sp_spray_population_value_changed( GtkAdjustment *adj, GObject * /*tbl*/ )
4563 {
4564 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4565 prefs->setDouble( "/tools/spray/population", adj->value );
4566 }
4568 static void sp_spray_rotation_value_changed( GtkAdjustment *adj, GObject * /*tbl*/ )
4569 {
4570 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4571 prefs->setDouble( "/tools/spray/rotation_variation", adj->value );
4572 }
4574 static void sp_spray_scale_value_changed( GtkAdjustment *adj, GObject * /*tbl*/ )
4575 {
4576 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4577 prefs->setDouble( "/tools/spray/scale_variation", adj->value );
4578 }
4581 static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4582 {
4583 Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
4584 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4586 {
4587 /* Width */
4588 gchar const* labels[] = {_("(narrow spray)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad spray)")};
4589 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4590 EgeAdjustmentAction *eact = create_adjustment_action( "SprayWidthAction",
4591 _("Width"), _("Width:"), _("The width of the spray area (relative to the visible canvas area)"),
4592 "/tools/spray/width", 15,
4593 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spray",
4594 1, 100, 1.0, 10.0,
4595 labels, values, G_N_ELEMENTS(labels),
4596 sp_spray_width_value_changed, 1, 0 );
4597 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4598 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4599 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4600 }
4602 {
4603 /* Mean */
4604 gchar const* labels[] = {_("(minimum mean)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum mean)")};
4605 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4606 EgeAdjustmentAction *eact = create_adjustment_action( "SprayMeanAction",
4607 _("Focus"), _("Focus:"), _("0 to spray a spot. Increase to enlarge the ring radius."),
4608 "/tools/spray/mean", 0,
4609 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-mean",
4610 0, 100, 1.0, 10.0,
4611 labels, values, G_N_ELEMENTS(labels),
4612 sp_spray_mean_value_changed, 1, 0 );
4613 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4614 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4615 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4616 }
4618 {
4619 /* Standard_deviation */
4620 gchar const* labels[] = {_("(minimum scatter)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum scatter)")};
4621 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4623 //TRANSLATORS: only translate "string" in "context|string".
4624 // For more details, see http://developer.gnome.org/doc/API/2.0/glib/glib-I18N.html#Q-:CAPS
4625 EgeAdjustmentAction *eact = create_adjustment_action( "SprayStandard_deviationAction",
4626 Q_("Toolbox|Scatter"), Q_("Toolbox|Scatter:"), _("Increase to scatter sprayed objects."),
4627 "/tools/spray/standard_deviation", 70,
4628 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-standard_deviation",
4629 1, 100, 1.0, 10.0,
4630 labels, values, G_N_ELEMENTS(labels),
4631 sp_spray_standard_deviation_value_changed, 1, 0 );
4632 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4633 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4634 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4635 }
4637 /* Mode */
4638 {
4639 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4641 GtkTreeIter iter;
4642 gtk_list_store_append( model, &iter );
4643 gtk_list_store_set( model, &iter,
4644 0, _("Spray with copies"),
4645 1, _("Spray copies of the initial selection"),
4646 2, INKSCAPE_ICON_SPRAY_COPY_MODE,
4647 -1 );
4649 gtk_list_store_append( model, &iter );
4650 gtk_list_store_set( model, &iter,
4651 0, _("Spray with clones"),
4652 1, _("Spray clones of the initial selection"),
4653 2, INKSCAPE_ICON_SPRAY_CLONE_MODE,
4654 -1 );
4656 gtk_list_store_append( model, &iter );
4657 gtk_list_store_set( model, &iter,
4658 0, _("Spray single path"),
4659 1, _("Spray objects in a single path"),
4660 2, INKSCAPE_ICON_SPRAY_UNION_MODE,
4661 -1 );
4663 EgeSelectOneAction* act = ege_select_one_action_new( "SprayModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4664 g_object_set( act, "short_label", _("Mode:"), NULL );
4665 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4666 g_object_set_data( holder, "mode_action", act );
4668 ege_select_one_action_set_appearance( act, "full" );
4669 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4670 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4671 ege_select_one_action_set_icon_column( act, 2 );
4672 ege_select_one_action_set_icon_size( act, secondarySize );
4673 ege_select_one_action_set_tooltip_column( act, 1 );
4675 gint mode = prefs->getInt("/tools/spray/mode", 1);
4676 ege_select_one_action_set_active( act, mode );
4677 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_spray_mode_changed), holder );
4679 g_object_set_data( G_OBJECT(holder), "spray_tool_mode", act);
4680 }
4682 { /* Population */
4683 gchar const* labels[] = {_("(low population)"), 0, 0, _("(default)"), 0, 0, _("(high population)")};
4684 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4685 EgeAdjustmentAction *eact = create_adjustment_action( "SprayPopulationAction",
4686 _("Amount"), _("Amount:"),
4687 _("Adjusts the number of items sprayed per clic."),
4688 "/tools/spray/population", 70,
4689 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-population",
4690 1, 100, 1.0, 10.0,
4691 labels, values, G_N_ELEMENTS(labels),
4692 sp_spray_population_value_changed, 1, 0 );
4693 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4694 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4695 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4696 g_object_set_data( holder, "spray_population", eact );
4697 }
4699 /* Use Pressure button */
4700 {
4701 InkToggleAction* act = ink_toggle_action_new( "SprayPressureAction",
4702 _("Pressure"),
4703 _("Use the pressure of the input device to alter the amount of sprayed objects."),
4704 "use_pressure",
4705 Inkscape::ICON_SIZE_DECORATION );
4706 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4707 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_spray_pressure_state_changed), NULL);
4708 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/spray/usepressure", true) );
4709 }
4711 { /* Rotation */
4712 gchar const* labels[] = {_("(low rotation variation)"), 0, 0, _("(default)"), 0, 0, _("(high rotation variation)")};
4713 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4714 EgeAdjustmentAction *eact = create_adjustment_action( "SprayRotationAction",
4715 _("Rotation"), _("Rotation:"),
4716 // xgettext:no-c-format
4717 _("Variation of the rotation of the sprayed objects. 0% for the same rotation than the original object."),
4718 "/tools/spray/rotation_variation", 0,
4719 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-rotation",
4720 0, 100, 1.0, 10.0,
4721 labels, values, G_N_ELEMENTS(labels),
4722 sp_spray_rotation_value_changed, 1, 0 );
4723 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4724 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4725 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4726 g_object_set_data( holder, "spray_rotation", eact );
4727 }
4729 { /* Scale */
4730 gchar const* labels[] = {_("(low scale variation)"), 0, 0, _("(default)"), 0, 0, _("(high scale variation)")};
4731 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4733 //TRANSLATORS: only translate "string" in "context|string".
4734 // For more details, see http://developer.gnome.org/doc/API/2.0/glib/glib-I18N.html#Q-:CAPS
4735 EgeAdjustmentAction *eact = create_adjustment_action( "SprayScaleAction",
4736 Q_("Toolbox|Scale"), Q_("Toolbox|Scale:"),
4737 // xgettext:no-c-format
4738 _("Variation in the scale of the sprayed objects. 0% for the same scale than the original object."),
4739 "/tools/spray/scale_variation", 0,
4740 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-scale",
4741 0, 100, 1.0, 10.0,
4742 labels, values, G_N_ELEMENTS(labels),
4743 sp_spray_scale_value_changed, 1, 0 );
4744 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4745 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4746 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4747 g_object_set_data( holder, "spray_scale", eact );
4748 }
4752 }
4755 //########################
4756 //## Calligraphy ##
4757 //########################
4758 static void update_presets_list(GObject *tbl)
4759 {
4760 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4761 if (g_object_get_data(tbl, "presets_blocked")) {
4762 return;
4763 }
4765 EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4766 if (!sel) {
4767 // WTF!? This will cause a segfault if ever reached
4768 //ege_select_one_action_set_active(sel, 0);
4769 return;
4770 }
4772 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4774 int ege_index = 1;
4775 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++ege_index) {
4776 bool match = true;
4778 std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(*i);
4779 for (std::vector<Inkscape::Preferences::Entry>::iterator j = preset.begin(); j != preset.end(); ++j) {
4780 Glib::ustring entry_name = j->getEntryName();
4781 if (entry_name == "id" || entry_name == "name") {
4782 continue;
4783 }
4785 void *widget = g_object_get_data(tbl, entry_name.data());
4786 if (widget) {
4787 if (GTK_IS_ADJUSTMENT(widget)) {
4788 double v = j->getDouble();
4789 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4790 //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
4791 if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
4792 match = false;
4793 break;
4794 }
4795 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4796 bool v = j->getBool();
4797 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4798 //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
4799 if ( static_cast<bool>(gtk_toggle_action_get_active(toggle)) != v ) {
4800 match = false;
4801 break;
4802 }
4803 }
4804 }
4805 }
4807 if (match) {
4808 // newly added item is at the same index as the
4809 // save command, so we need to change twice for it to take effect
4810 ege_select_one_action_set_active(sel, 0);
4811 ege_select_one_action_set_active(sel, ege_index); // one-based index
4812 return;
4813 }
4814 }
4816 // no match found
4817 ege_select_one_action_set_active(sel, 0);
4818 }
4820 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
4821 {
4822 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4823 prefs->setDouble( "/tools/calligraphic/mass", adj->value );
4824 update_presets_list(tbl);
4825 }
4827 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
4828 {
4829 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4830 prefs->setDouble( "/tools/calligraphic/wiggle", adj->value );
4831 update_presets_list(tbl);
4832 }
4834 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
4835 {
4836 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4837 prefs->setDouble( "/tools/calligraphic/angle", adj->value );
4838 update_presets_list(tbl);
4839 }
4841 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
4842 {
4843 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4844 prefs->setDouble( "/tools/calligraphic/width", adj->value );
4845 update_presets_list(tbl);
4846 }
4848 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
4849 {
4850 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4851 prefs->setDouble("/tools/calligraphic/thinning", adj->value );
4852 update_presets_list(tbl);
4853 }
4855 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
4856 {
4857 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4858 prefs->setDouble( "/tools/calligraphic/flatness", adj->value );
4859 update_presets_list(tbl);
4860 }
4862 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
4863 {
4864 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4865 prefs->setDouble( "/tools/calligraphic/tremor", adj->value );
4866 update_presets_list(tbl);
4867 }
4869 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
4870 {
4871 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4872 prefs->setDouble( "/tools/calligraphic/cap_rounding", adj->value );
4873 update_presets_list(tbl);
4874 }
4876 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject* tbl )
4877 {
4878 // TODO merge into PrefPusher
4879 GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
4880 if (calligraphy_angle ) {
4881 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
4882 }
4883 }
4886 static gchar const *const widget_names[] = {
4887 "width",
4888 "mass",
4889 "wiggle",
4890 "angle",
4891 "thinning",
4892 "tremor",
4893 "flatness",
4894 "cap_rounding",
4895 "usepressure",
4896 "tracebackground",
4897 "usetilt"
4898 };
4901 static void sp_dcc_build_presets_list(GObject *tbl)
4902 {
4903 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4905 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4906 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4907 gtk_list_store_clear (model);
4909 {
4910 GtkTreeIter iter;
4911 gtk_list_store_append( model, &iter );
4912 gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4913 }
4915 // iterate over all presets to populate the list
4916 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4917 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4918 int ii=1;
4920 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i) {
4921 GtkTreeIter iter;
4922 Glib::ustring preset_name = prefs->getString(*i + "/name");
4923 gtk_list_store_append( model, &iter );
4924 gtk_list_store_set( model, &iter, 0, _(preset_name.data()), 1, ii++, -1 );
4925 }
4927 {
4928 GtkTreeIter iter;
4929 gtk_list_store_append( model, &iter );
4930 gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4931 g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4932 }
4934 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4936 update_presets_list (tbl);
4937 }
4939 static void sp_dcc_save_profile(GtkWidget * /*widget*/, GObject *tbl)
4940 {
4941 using Inkscape::UI::Dialog::CalligraphicProfileRename;
4942 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4943 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4944 if (! desktop) {
4945 return;
4946 }
4948 if (g_object_get_data(tbl, "presets_blocked")) {
4949 return;
4950 }
4952 CalligraphicProfileRename::show(desktop);
4953 if ( !CalligraphicProfileRename::applied()) {
4954 // dialog cancelled
4955 update_presets_list (tbl);
4956 return;
4957 }
4958 Glib::ustring profile_name = CalligraphicProfileRename::getProfileName();
4960 if (profile_name.empty()) {
4961 // empty name entered
4962 update_presets_list (tbl);
4963 return;
4964 }
4966 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4968 // If there's a preset with the given name, find it and set save_path appropriately
4969 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4970 int total_presets = presets.size();
4971 int new_index = -1;
4972 Glib::ustring save_path; // profile pref path without a trailing slash
4974 int temp_index = 0;
4975 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++temp_index) {
4976 Glib::ustring name = prefs->getString(*i + "/name");
4977 if (!name.empty() && profile_name == name) {
4978 new_index = temp_index;
4979 save_path = *i;
4980 break;
4981 }
4982 }
4984 if (new_index == -1) {
4985 // no preset with this name, create
4986 new_index = total_presets + 1;
4987 gchar *profile_id = g_strdup_printf("/dcc%d", new_index);
4988 save_path = Glib::ustring("/tools/calligraphic/preset") + profile_id;
4989 g_free(profile_id);
4990 }
4992 for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4993 gchar const *const widget_name = widget_names[i];
4994 void *widget = g_object_get_data(tbl, widget_name);
4995 if (widget) {
4996 if (GTK_IS_ADJUSTMENT(widget)) {
4997 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4998 prefs->setDouble(save_path + "/" + widget_name, gtk_adjustment_get_value(adj));
4999 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
5000 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
5001 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
5002 prefs->setBool(save_path + "/" + widget_name, gtk_toggle_action_get_active(toggle));
5003 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
5004 } else {
5005 g_warning("Unknown widget type for preset: %s\n", widget_name);
5006 }
5007 } else {
5008 g_warning("Bad key when writing preset: %s\n", widget_name);
5009 }
5010 }
5011 prefs->setString(save_path + "/name", profile_name);
5013 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
5014 sp_dcc_build_presets_list (tbl);
5015 }
5018 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl)
5019 {
5020 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5022 gint preset_index = ege_select_one_action_get_active( act );
5023 // This is necessary because EgeSelectOneAction spams us with GObject "changed" signal calls
5024 // even when the preset is not changed. It would be good to replace it with something more
5025 // modern. Index 0 means "No preset", so we don't do anything.
5026 if (preset_index == 0) {
5027 return;
5028 }
5030 gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
5032 if (preset_index == save_presets_index) {
5033 // this is the Save command
5034 sp_dcc_save_profile(NULL, tbl);
5035 return;
5036 }
5038 if (g_object_get_data(tbl, "presets_blocked")) {
5039 return;
5040 }
5042 // preset_index is one-based so we subtract 1
5043 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
5044 Glib::ustring preset_path = presets.at(preset_index - 1);
5046 if (!preset_path.empty()) {
5047 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
5049 std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(preset_path);
5051 // Shouldn't this be std::map?
5052 for (std::vector<Inkscape::Preferences::Entry>::iterator i = preset.begin(); i != preset.end(); ++i) {
5053 Glib::ustring entry_name = i->getEntryName();
5054 if (entry_name == "id" || entry_name == "name") {
5055 continue;
5056 }
5057 void *widget = g_object_get_data(tbl, entry_name.data());
5058 if (widget) {
5059 if (GTK_IS_ADJUSTMENT(widget)) {
5060 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
5061 gtk_adjustment_set_value(adj, i->getDouble());
5062 //std::cout << "set adj " << attr_name << " to " << v << "\n";
5063 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
5064 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
5065 gtk_toggle_action_set_active(toggle, i->getBool());
5066 //std::cout << "set toggle " << attr_name << " to " << v << "\n";
5067 } else {
5068 g_warning("Unknown widget type for preset: %s\n", entry_name.data());
5069 }
5070 } else {
5071 g_warning("Bad key found in a preset record: %s\n", entry_name.data());
5072 }
5073 }
5074 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
5075 }
5076 }
5078 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5079 {
5080 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5081 {
5082 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
5084 EgeAdjustmentAction* calligraphy_angle = 0;
5086 {
5087 /* Width */
5088 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5089 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5090 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
5091 _("Pen Width"), _("Width:"),
5092 _("The width of the calligraphic pen (relative to the visible canvas area)"),
5093 "/tools/calligraphic/width", 15,
5094 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
5095 1, 100, 1.0, 10.0,
5096 labels, values, G_N_ELEMENTS(labels),
5097 sp_ddc_width_value_changed, 1, 0 );
5098 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5099 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5100 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5101 }
5103 {
5104 /* Thinning */
5105 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
5106 gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
5107 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
5108 _("Stroke Thinning"), _("Thinning:"),
5109 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
5110 "/tools/calligraphic/thinning", 10,
5111 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5112 -100, 100, 1, 10.0,
5113 labels, values, G_N_ELEMENTS(labels),
5114 sp_ddc_velthin_value_changed, 1, 0);
5115 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5116 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5117 }
5119 {
5120 /* Angle */
5121 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
5122 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
5123 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
5124 _("Pen Angle"), _("Angle:"),
5125 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
5126 "/tools/calligraphic/angle", 30,
5127 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
5128 -90.0, 90.0, 1.0, 10.0,
5129 labels, values, G_N_ELEMENTS(labels),
5130 sp_ddc_angle_value_changed, 1, 0 );
5131 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5132 g_object_set_data( holder, "angle_action", eact );
5133 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5134 calligraphy_angle = eact;
5135 }
5137 {
5138 /* Fixation */
5139 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
5140 gdouble values[] = {0, 20, 40, 60, 90, 100};
5141 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
5142 _("Fixation"), _("Fixation:"),
5143 _("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"),
5144 "/tools/calligraphic/flatness", 90,
5145 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5146 0.0, 100, 1.0, 10.0,
5147 labels, values, G_N_ELEMENTS(labels),
5148 sp_ddc_flatness_value_changed, 1, 0);
5149 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5150 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5151 }
5153 {
5154 /* Cap Rounding */
5155 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
5156 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
5157 // TRANSLATORS: "cap" means "end" (both start and finish) here
5158 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
5159 _("Cap rounding"), _("Caps:"),
5160 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
5161 "/tools/calligraphic/cap_rounding", 0.0,
5162 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5163 0.0, 5.0, 0.01, 0.1,
5164 labels, values, G_N_ELEMENTS(labels),
5165 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
5166 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5167 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5168 }
5170 {
5171 /* Tremor */
5172 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
5173 gdouble values[] = {0, 10, 20, 40, 60, 100};
5174 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
5175 _("Stroke Tremor"), _("Tremor:"),
5176 _("Increase to make strokes rugged and trembling"),
5177 "/tools/calligraphic/tremor", 0.0,
5178 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5179 0.0, 100, 1, 10.0,
5180 labels, values, G_N_ELEMENTS(labels),
5181 sp_ddc_tremor_value_changed, 1, 0);
5183 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5184 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5185 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5186 }
5188 {
5189 /* Wiggle */
5190 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
5191 gdouble values[] = {0, 20, 40, 60, 100};
5192 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
5193 _("Pen Wiggle"), _("Wiggle:"),
5194 _("Increase to make the pen waver and wiggle"),
5195 "/tools/calligraphic/wiggle", 0.0,
5196 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5197 0.0, 100, 1, 10.0,
5198 labels, values, G_N_ELEMENTS(labels),
5199 sp_ddc_wiggle_value_changed, 1, 0);
5200 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5201 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5202 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5203 }
5205 {
5206 /* Mass */
5207 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
5208 gdouble values[] = {0.0, 2, 10, 20, 50, 100};
5209 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
5210 _("Pen Mass"), _("Mass:"),
5211 _("Increase to make the pen drag behind, as if slowed by inertia"),
5212 "/tools/calligraphic/mass", 2.0,
5213 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5214 0.0, 100, 1, 10.0,
5215 labels, values, G_N_ELEMENTS(labels),
5216 sp_ddc_mass_value_changed, 1, 0);
5217 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5218 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5219 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5220 }
5223 /* Trace Background button */
5224 {
5225 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
5226 _("Trace Background"),
5227 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
5228 INKSCAPE_ICON_DRAW_TRACE_BACKGROUND,
5229 Inkscape::ICON_SIZE_DECORATION );
5230 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5231 PrefPusher *pusher = new PrefPusher(GTK_TOGGLE_ACTION(act), "/tools/calligraphic/tracebackground", update_presets_list, holder);
5232 g_signal_connect( holder, "destroy", G_CALLBACK(delete_prefspusher), pusher);
5233 g_object_set_data( holder, "tracebackground", act );
5234 }
5236 /* Use Pressure button */
5237 {
5238 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
5239 _("Pressure"),
5240 _("Use the pressure of the input device to alter the width of the pen"),
5241 INKSCAPE_ICON_DRAW_USE_PRESSURE,
5242 Inkscape::ICON_SIZE_DECORATION );
5243 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5244 PrefPusher *pusher = new PrefPusher(GTK_TOGGLE_ACTION(act), "/tools/calligraphic/usepressure", update_presets_list, holder);
5245 g_signal_connect( holder, "destroy", G_CALLBACK(delete_prefspusher), pusher);
5246 g_object_set_data( holder, "usepressure", act );
5247 }
5249 /* Use Tilt button */
5250 {
5251 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
5252 _("Tilt"),
5253 _("Use the tilt of the input device to alter the angle of the pen's nib"),
5254 INKSCAPE_ICON_DRAW_USE_TILT,
5255 Inkscape::ICON_SIZE_DECORATION );
5256 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5257 PrefPusher *pusher = new PrefPusher(GTK_TOGGLE_ACTION(act), "/tools/calligraphic/usetilt", update_presets_list, holder);
5258 g_signal_connect( holder, "destroy", G_CALLBACK(delete_prefspusher), pusher);
5259 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
5260 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs->getBool("/tools/calligraphic/usetilt", true) );
5261 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usetilt", true) );
5262 g_object_set_data( holder, "usetilt", act );
5263 }
5265 /*calligraphic profile */
5266 {
5267 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5268 EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
5269 ege_select_one_action_set_appearance (act1, "compact");
5270 g_object_set_data (holder, "profile_selector", act1 );
5272 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
5274 sp_dcc_build_presets_list (holder);
5276 g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
5277 gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
5278 }
5279 }
5280 }
5283 //########################
5284 //## Circle / Arc ##
5285 //########################
5287 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
5288 {
5289 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
5290 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
5292 if (v1 == 0 && v2 == 0) {
5293 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
5294 gtk_action_set_sensitive( ocb, FALSE );
5295 gtk_action_set_sensitive( make_whole, FALSE );
5296 }
5297 } else {
5298 gtk_action_set_sensitive( ocb, TRUE );
5299 gtk_action_set_sensitive( make_whole, TRUE );
5300 }
5301 }
5303 static void
5304 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
5305 {
5306 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5308 if (DocumentUndo::getUndoSensitive(sp_desktop_document(desktop))) {
5309 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5310 prefs->setDouble(Glib::ustring("/tools/shapes/arc/") + value_name, adj->value);
5311 }
5313 // quit if run by the attr_changed listener
5314 if (g_object_get_data( tbl, "freeze" )) {
5315 return;
5316 }
5318 // in turn, prevent listener from responding
5319 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5321 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
5323 bool modmade = false;
5324 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5325 items != NULL;
5326 items = items->next)
5327 {
5328 SPItem *item = SP_ITEM(items->data);
5330 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
5332 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
5333 SPArc *arc = SP_ARC(item);
5335 if (!strcmp(value_name, "start")) {
5336 ge->start = (adj->value * M_PI)/ 180;
5337 } else {
5338 ge->end = (adj->value * M_PI)/ 180;
5339 }
5341 sp_genericellipse_normalize(ge);
5342 ((SPObject *)arc)->updateRepr();
5343 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
5345 modmade = true;
5346 }
5347 }
5349 g_free(namespaced_name);
5351 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
5353 sp_arctb_sensitivize( tbl, adj->value, other->value );
5355 if (modmade) {
5356 DocumentUndo::maybeDone(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
5357 _("Arc: Change start/end"));
5358 }
5360 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5361 }
5364 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
5365 {
5366 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
5367 }
5369 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
5370 {
5371 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
5372 }
5375 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
5376 {
5377 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5378 if (DocumentUndo::getUndoSensitive(sp_desktop_document(desktop))) {
5379 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5380 prefs->setBool("/tools/shapes/arc/open", ege_select_one_action_get_active(act) != 0);
5381 }
5383 // quit if run by the attr_changed listener
5384 if (g_object_get_data( tbl, "freeze" )) {
5385 return;
5386 }
5388 // in turn, prevent listener from responding
5389 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5391 bool modmade = false;
5393 if ( ege_select_one_action_get_active(act) != 0 ) {
5394 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5395 items != NULL;
5396 items = items->next)
5397 {
5398 if (SP_IS_ARC((SPItem *) items->data)) {
5399 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5400 repr->setAttribute("sodipodi:open", "true");
5401 SP_OBJECT((SPItem *) items->data)->updateRepr();
5402 modmade = true;
5403 }
5404 }
5405 } else {
5406 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5407 items != NULL;
5408 items = items->next)
5409 {
5410 if (SP_IS_ARC((SPItem *) items->data)) {
5411 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5412 repr->setAttribute("sodipodi:open", NULL);
5413 SP_OBJECT((SPItem *) items->data)->updateRepr();
5414 modmade = true;
5415 }
5416 }
5417 }
5419 if (modmade) {
5420 DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
5421 _("Arc: Change open/closed"));
5422 }
5424 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5425 }
5427 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
5428 {
5429 GtkAdjustment *adj;
5430 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
5431 gtk_adjustment_set_value(adj, 0.0);
5432 gtk_adjustment_value_changed(adj);
5434 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
5435 gtk_adjustment_set_value(adj, 0.0);
5436 gtk_adjustment_value_changed(adj);
5438 spinbutton_defocus( GTK_OBJECT(obj) );
5439 }
5441 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const * /*name*/,
5442 gchar const * /*old_value*/, gchar const * /*new_value*/,
5443 bool /*is_interactive*/, gpointer data)
5444 {
5445 GObject *tbl = G_OBJECT(data);
5447 // quit if run by the _changed callbacks
5448 if (g_object_get_data( tbl, "freeze" )) {
5449 return;
5450 }
5452 // in turn, prevent callbacks from responding
5453 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5455 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
5456 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
5458 GtkAdjustment *adj1,*adj2;
5459 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
5460 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
5461 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
5462 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
5464 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
5466 char const *openstr = NULL;
5467 openstr = repr->attribute("sodipodi:open");
5468 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
5470 if (openstr) {
5471 ege_select_one_action_set_active( ocb, 1 );
5472 } else {
5473 ege_select_one_action_set_active( ocb, 0 );
5474 }
5476 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5477 }
5479 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
5480 NULL, /* child_added */
5481 NULL, /* child_removed */
5482 arc_tb_event_attr_changed,
5483 NULL, /* content_changed */
5484 NULL /* order_changed */
5485 };
5488 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
5489 {
5490 int n_selected = 0;
5491 Inkscape::XML::Node *repr = NULL;
5493 purge_repr_listener( tbl, tbl );
5495 for (GSList const *items = selection->itemList();
5496 items != NULL;
5497 items = items->next)
5498 {
5499 if (SP_IS_ARC((SPItem *) items->data)) {
5500 n_selected++;
5501 repr = SP_OBJECT_REPR((SPItem *) items->data);
5502 }
5503 }
5505 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
5507 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
5508 if (n_selected == 0) {
5509 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
5510 } else if (n_selected == 1) {
5511 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
5512 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5514 if (repr) {
5515 g_object_set_data( tbl, "repr", repr );
5516 Inkscape::GC::anchor(repr);
5517 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
5518 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
5519 }
5520 } else {
5521 // FIXME: implement averaging of all parameters for multiple selected
5522 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
5523 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5524 sp_arctb_sensitivize( tbl, 1, 0 );
5525 }
5526 }
5529 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5530 {
5531 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5533 EgeAdjustmentAction* eact = 0;
5534 Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
5537 {
5538 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
5539 ege_output_action_set_use_markup( act, TRUE );
5540 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5541 g_object_set_data( holder, "mode_action", act );
5542 }
5544 /* Start */
5545 {
5546 eact = create_adjustment_action( "ArcStartAction",
5547 _("Start"), _("Start:"),
5548 _("The angle (in degrees) from the horizontal to the arc's start point"),
5549 "/tools/shapes/arc/start", 0.0,
5550 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
5551 -360.0, 360.0, 1.0, 10.0,
5552 0, 0, 0,
5553 sp_arctb_start_value_changed);
5554 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5555 }
5557 /* End */
5558 {
5559 eact = create_adjustment_action( "ArcEndAction",
5560 _("End"), _("End:"),
5561 _("The angle (in degrees) from the horizontal to the arc's end point"),
5562 "/tools/shapes/arc/end", 0.0,
5563 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
5564 -360.0, 360.0, 1.0, 10.0,
5565 0, 0, 0,
5566 sp_arctb_end_value_changed);
5567 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5568 }
5570 /* Segments / Pie checkbox */
5571 {
5572 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5574 GtkTreeIter iter;
5575 gtk_list_store_append( model, &iter );
5576 gtk_list_store_set( model, &iter,
5577 0, _("Closed arc"),
5578 1, _("Switch to segment (closed shape with two radii)"),
5579 2, INKSCAPE_ICON_DRAW_ELLIPSE_SEGMENT,
5580 -1 );
5582 gtk_list_store_append( model, &iter );
5583 gtk_list_store_set( model, &iter,
5584 0, _("Open Arc"),
5585 1, _("Switch to arc (unclosed shape)"),
5586 2, INKSCAPE_ICON_DRAW_ELLIPSE_ARC,
5587 -1 );
5589 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5590 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5591 g_object_set_data( holder, "open_action", act );
5593 ege_select_one_action_set_appearance( act, "full" );
5594 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5595 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5596 ege_select_one_action_set_icon_column( act, 2 );
5597 ege_select_one_action_set_icon_size( act, secondarySize );
5598 ege_select_one_action_set_tooltip_column( act, 1 );
5600 bool isClosed = !prefs->getBool("/tools/shapes/arc/open", false);
5601 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
5602 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
5603 }
5605 /* Make Whole */
5606 {
5607 InkAction* inky = ink_action_new( "ArcResetAction",
5608 _("Make whole"),
5609 _("Make the shape a whole ellipse, not arc or segment"),
5610 INKSCAPE_ICON_DRAW_ELLIPSE_WHOLE,
5611 secondarySize );
5612 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
5613 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5614 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
5615 g_object_set_data( holder, "make_whole", inky );
5616 }
5618 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
5619 // sensitivize make whole and open checkbox
5620 {
5621 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
5622 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
5623 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
5624 }
5627 sigc::connection *connection = new sigc::connection(
5628 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
5629 );
5630 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
5631 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
5632 }
5637 // toggle button callbacks and updaters
5639 //########################
5640 //## Dropper ##
5641 //########################
5643 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl )
5644 {
5645 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5646 prefs->setInt( "/tools/dropper/pick", gtk_toggle_action_get_active( act ) );
5647 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
5648 if ( set_action ) {
5649 if ( gtk_toggle_action_get_active( act ) ) {
5650 gtk_action_set_sensitive( set_action, TRUE );
5651 } else {
5652 gtk_action_set_sensitive( set_action, FALSE );
5653 }
5654 }
5656 spinbutton_defocus(GTK_OBJECT(tbl));
5657 }
5659 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl )
5660 {
5661 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5662 prefs->setBool( "/tools/dropper/setalpha", gtk_toggle_action_get_active( act ) );
5663 spinbutton_defocus(GTK_OBJECT(tbl));
5664 }
5667 /**
5668 * Dropper auxiliary toolbar construction and setup.
5669 *
5670 * TODO: Would like to add swatch of current color.
5671 * TODO: Add queue of last 5 or so colors selected with new swatches so that
5672 * can drag and drop places. Will provide a nice mixing palette.
5673 */
5674 static void sp_dropper_toolbox_prep(SPDesktop * /*desktop*/, GtkActionGroup* mainActions, GObject* holder)
5675 {
5676 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5677 gint pickAlpha = prefs->getInt( "/tools/dropper/pick", 1 );
5679 {
5680 EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
5681 ege_output_action_set_use_markup( act, TRUE );
5682 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5683 }
5685 {
5686 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
5687 _("Pick opacity"),
5688 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
5689 NULL,
5690 Inkscape::ICON_SIZE_DECORATION );
5691 g_object_set( act, "short_label", _("Pick"), NULL );
5692 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5693 g_object_set_data( holder, "pick_action", act );
5694 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
5695 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
5696 }
5698 {
5699 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
5700 _("Assign opacity"),
5701 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
5702 NULL,
5703 Inkscape::ICON_SIZE_DECORATION );
5704 g_object_set( act, "short_label", _("Assign"), NULL );
5705 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5706 g_object_set_data( holder, "set_action", act );
5707 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/dropper/setalpha", true) );
5708 // make sure it's disabled if we're not picking alpha
5709 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
5710 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
5711 }
5712 }
5715 //########################
5716 //## LPETool ##
5717 //########################
5719 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
5721 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
5722 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
5723 {
5724 using namespace Inkscape::LivePathEffect;
5726 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
5727 SPEventContext *ec = desktop->event_context;
5728 if (!SP_IS_LPETOOL_CONTEXT(ec)) {
5729 return;
5730 }
5732 // only take action if run by the attr_changed listener
5733 if (!g_object_get_data(tbl, "freeze")) {
5734 // in turn, prevent listener from responding
5735 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5737 gint mode = ege_select_one_action_get_active(act);
5738 EffectType type = lpesubtools[mode].type;
5740 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5741 bool success = lpetool_try_construction(lc, type);
5742 if (success) {
5743 // since the construction was already performed, we set the state back to inactive
5744 ege_select_one_action_set_active(act, 0);
5745 mode = 0;
5746 } else {
5747 // switch to the chosen subtool
5748 SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
5749 }
5751 if (DocumentUndo::getUndoSensitive(sp_desktop_document(desktop))) {
5752 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5753 prefs->setInt( "/tools/lpetool/mode", mode );
5754 }
5756 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
5757 }
5758 }
5760 static void sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject * /*tbl*/)
5761 {
5762 SPEventContext *ec = selection->desktop()->event_context;
5763 if (SP_IS_LPETOOL_CONTEXT(ec)) {
5764 lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
5765 }
5766 }
5768 static void sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
5769 {
5770 using namespace Inkscape::LivePathEffect;
5771 SPEventContext *ec = selection->desktop()->event_context;
5772 if (!SP_IS_LPETOOL_CONTEXT(ec)) {
5773 return;
5774 }
5775 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
5777 lpetool_delete_measuring_items(lc);
5778 lpetool_create_measuring_items(lc, selection);
5780 // activate line segment combo box if a single item with LPELineSegment is selected
5781 GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
5782 SPItem *item = selection->singleItem();
5783 if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
5784 SPLPEItem *lpeitem = SP_LPE_ITEM(item);
5785 Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
5786 if (lpe && lpe->effectType() == LINE_SEGMENT) {
5787 LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
5788 g_object_set_data(tbl, "currentlpe", lpe);
5789 g_object_set_data(tbl, "currentlpeitem", lpeitem);
5790 gtk_action_set_sensitive(w, TRUE);
5791 ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
5792 } else {
5793 g_object_set_data(tbl, "currentlpe", NULL);
5794 g_object_set_data(tbl, "currentlpeitem", NULL);
5795 gtk_action_set_sensitive(w, FALSE);
5796 }
5797 } else {
5798 g_object_set_data(tbl, "currentlpe", NULL);
5799 g_object_set_data(tbl, "currentlpeitem", NULL);
5800 gtk_action_set_sensitive(w, FALSE);
5801 }
5802 }
5804 static void lpetool_toggle_show_bbox(GtkToggleAction *act, gpointer data) {
5805 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5806 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5808 bool show = gtk_toggle_action_get_active( act );
5809 prefs->setBool("/tools/lpetool/show_bbox", show);
5811 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5812 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5813 lpetool_context_reset_limiting_bbox(lc);
5814 }
5815 }
5817 static void lpetool_toggle_show_measuring_info(GtkToggleAction *act, GObject *tbl)
5818 {
5819 SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
5820 if (!tools_isactive(desktop, TOOLS_LPETOOL)) {
5821 return;
5822 }
5824 GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
5825 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5826 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5827 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5828 bool show = gtk_toggle_action_get_active( act );
5829 prefs->setBool("/tools/lpetool/show_measuring_info", show);
5830 lpetool_show_measuring_info(lc, show);
5831 gtk_action_set_sensitive(GTK_ACTION(unitact), show);
5832 }
5833 }
5835 static void lpetool_unit_changed(GtkAction* /*act*/, GObject* tbl)
5836 {
5837 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
5838 SPUnit const *unit = tracker->getActiveUnit();
5839 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5840 prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5842 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5843 if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
5844 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5845 lpetool_delete_measuring_items(lc);
5846 lpetool_create_measuring_items(lc);
5847 }
5848 }
5850 static void lpetool_toggle_set_bbox(GtkToggleAction *act, gpointer data)
5851 {
5852 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5853 Inkscape::Selection *selection = desktop->selection;
5855 Geom::OptRect bbox = selection->bounds();
5857 if (bbox) {
5858 Geom::Point A(bbox->min());
5859 Geom::Point B(bbox->max());
5861 A *= desktop->doc2dt();
5862 B *= desktop->doc2dt();
5864 // TODO: should we provide a way to store points in prefs?
5865 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5866 prefs->setDouble("/tools/lpetool/bbox_upperleftx", A[Geom::X]);
5867 prefs->setDouble("/tools/lpetool/bbox_upperlefty", A[Geom::Y]);
5868 prefs->setDouble("/tools/lpetool/bbox_lowerrightx", B[Geom::X]);
5869 prefs->setDouble("/tools/lpetool/bbox_lowerrighty", B[Geom::Y]);
5871 lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
5872 }
5874 gtk_toggle_action_set_active(act, false);
5875 }
5877 static void sp_line_segment_build_list(GObject *tbl)
5878 {
5879 g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
5881 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
5882 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
5883 gtk_list_store_clear (model);
5885 // TODO: we add the entries of rht combo box manually; later this should be done automatically
5886 {
5887 GtkTreeIter iter;
5888 gtk_list_store_append( model, &iter );
5889 gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
5890 gtk_list_store_append( model, &iter );
5891 gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
5892 gtk_list_store_append( model, &iter );
5893 gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
5894 gtk_list_store_append( model, &iter );
5895 gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
5896 }
5898 g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5899 }
5901 static void sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl)
5902 {
5903 using namespace Inkscape::LivePathEffect;
5905 // quit if run by the attr_changed listener
5906 if (g_object_get_data(tbl, "freeze")) {
5907 return;
5908 }
5910 // in turn, prevent listener from responding
5911 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5913 LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
5914 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5915 if (lpeitem) {
5916 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5917 lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
5918 sp_lpe_item_update_patheffect(lpeitem, true, true);
5919 }
5921 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5922 }
5924 static void lpetool_open_lpe_dialog(GtkToggleAction *act, gpointer data)
5925 {
5926 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5928 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5929 sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5930 }
5931 gtk_toggle_action_set_active(act, false);
5932 }
5934 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5935 {
5936 UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5937 tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5938 g_object_set_data(holder, "tracker", tracker);
5939 SPUnit const *unit = tracker->getActiveUnit();
5941 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5942 prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5944 /** Automatically create a list of LPEs that get added to the toolbar **/
5945 {
5946 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5948 GtkTreeIter iter;
5950 // the first toggle button represents the state that no subtool is active (remove this when
5951 // this can be modeled by EgeSelectOneAction or some other action)
5952 gtk_list_store_append( model, &iter );
5953 gtk_list_store_set( model, &iter,
5954 0, _("All inactive"),
5955 1, _("No geometric tool is active"),
5956 2, "draw-geometry-inactive",
5957 -1 );
5959 Inkscape::LivePathEffect::EffectType type;
5960 for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5961 type = lpesubtools[i].type;
5962 gtk_list_store_append( model, &iter );
5963 gtk_list_store_set( model, &iter,
5964 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5965 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5966 2, lpesubtools[i].icon_name,
5967 -1 );
5968 }
5970 EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5971 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5972 g_object_set_data( holder, "lpetool_mode_action", act );
5974 ege_select_one_action_set_appearance( act, "full" );
5975 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5976 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5977 ege_select_one_action_set_icon_column( act, 2 );
5978 ege_select_one_action_set_tooltip_column( act, 1 );
5980 gint lpeToolMode = prefs->getInt("/tools/lpetool/mode", 0);
5981 ege_select_one_action_set_active( act, lpeToolMode );
5982 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5983 }
5985 /* Show limiting bounding box */
5986 {
5987 InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5988 _("Show limiting bounding box"),
5989 _("Show bounding box (used to cut infinite lines)"),
5990 "show-bounding-box",
5991 Inkscape::ICON_SIZE_DECORATION );
5992 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5993 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5994 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_bbox", true ) );
5995 }
5997 /* Set limiting bounding box to bbox of current selection */
5998 {
5999 InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
6000 _("Get limiting bounding box from selection"),
6001 _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
6002 "draw-geometry-set-bounding-box",
6003 Inkscape::ICON_SIZE_DECORATION );
6004 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6005 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
6006 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
6007 }
6010 /* Combo box to choose line segment type */
6011 {
6012 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
6013 EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
6014 ege_select_one_action_set_appearance (act, "compact");
6015 g_object_set_data (holder, "lpetool_line_segment_action", act );
6017 g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
6019 sp_line_segment_build_list (holder);
6021 g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
6022 gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
6023 gtk_action_group_add_action(mainActions, GTK_ACTION(act));
6024 }
6026 /* Display measuring info for selected items */
6027 {
6028 InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
6029 _("Display measuring info"),
6030 _("Display measuring info for selected items"),
6031 "draw-geometry-show-measuring-info",
6032 Inkscape::ICON_SIZE_DECORATION );
6033 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6034 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
6035 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_measuring_info", true ) );
6036 }
6038 // add the units menu
6039 {
6040 GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
6041 gtk_action_group_add_action( mainActions, act );
6042 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
6043 g_object_set_data(holder, "lpetool_units_action", act);
6044 gtk_action_set_sensitive(act, prefs->getBool("/tools/lpetool/show_measuring_info", true));
6045 }
6047 /* Open LPE dialog (to adapt parameters numerically) */
6048 {
6049 InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
6050 _("Open LPE dialog"),
6051 _("Open LPE dialog (to adapt parameters numerically)"),
6052 "dialog-geometry",
6053 Inkscape::ICON_SIZE_DECORATION );
6054 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6055 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
6056 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
6057 }
6059 //watch selection
6060 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
6062 sigc::connection *c_selection_modified =
6063 new sigc::connection (sp_desktop_selection (desktop)->connectModified
6064 (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
6065 pool->add_connection ("selection-modified", c_selection_modified);
6067 sigc::connection *c_selection_changed =
6068 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
6069 (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
6070 pool->add_connection ("selection-changed", c_selection_changed);
6071 }
6073 //########################
6074 //## Eraser ##
6075 //########################
6077 static void sp_erc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
6078 {
6079 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6080 prefs->setDouble( "/tools/eraser/width", adj->value );
6081 update_presets_list(tbl);
6082 }
6084 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
6085 {
6086 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
6087 bool eraserMode = ege_select_one_action_get_active( act ) != 0;
6088 if (DocumentUndo::getUndoSensitive(sp_desktop_document(desktop))) {
6089 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6090 prefs->setBool( "/tools/eraser/mode", eraserMode );
6091 }
6093 // only take action if run by the attr_changed listener
6094 if (!g_object_get_data( tbl, "freeze" )) {
6095 // in turn, prevent listener from responding
6096 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6098 if ( eraserMode != 0 ) {
6099 } else {
6100 }
6101 // TODO finish implementation
6103 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6104 }
6105 }
6107 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6108 {
6109 {
6110 /* Width */
6111 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
6112 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
6113 EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
6114 _("Pen Width"), _("Width:"),
6115 _("The width of the eraser pen (relative to the visible canvas area)"),
6116 "/tools/eraser/width", 15,
6117 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
6118 1, 100, 1.0, 10.0,
6119 labels, values, G_N_ELEMENTS(labels),
6120 sp_erc_width_value_changed, 1, 0);
6121 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6122 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6123 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
6124 }
6126 {
6127 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
6129 GtkTreeIter iter;
6130 gtk_list_store_append( model, &iter );
6131 gtk_list_store_set( model, &iter,
6132 0, _("Delete"),
6133 1, _("Delete objects touched by the eraser"),
6134 2, INKSCAPE_ICON_DRAW_ERASER_DELETE_OBJECTS,
6135 -1 );
6137 gtk_list_store_append( model, &iter );
6138 gtk_list_store_set( model, &iter,
6139 0, _("Cut"),
6140 1, _("Cut out from objects"),
6141 2, INKSCAPE_ICON_PATH_DIFFERENCE,
6142 -1 );
6144 EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
6145 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
6146 g_object_set_data( holder, "eraser_mode_action", act );
6148 ege_select_one_action_set_appearance( act, "full" );
6149 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
6150 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
6151 ege_select_one_action_set_icon_column( act, 2 );
6152 ege_select_one_action_set_tooltip_column( act, 1 );
6154 /// @todo Convert to boolean?
6155 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6156 gint eraserMode = prefs->getBool("/tools/eraser/mode") ? 1 : 0;
6157 ege_select_one_action_set_active( act, eraserMode );
6158 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
6159 }
6161 }
6163 //########################
6164 //## Text Toolbox ##
6165 //########################
6167 // Functions for debugging:
6168 #ifdef DEBUG_TEXT
6170 static void sp_print_font( SPStyle *query ) {
6172 bool family_set = query->text->font_family.set;
6173 bool style_set = query->font_style.set;
6174 bool fontspec_set = query->text->font_specification.set;
6176 std::cout << " Family set? " << family_set
6177 << " Style set? " << style_set
6178 << " FontSpec set? " << fontspec_set
6179 << std::endl;
6180 std::cout << " Family: "
6181 << (query->text->font_family.value ? query->text->font_family.value : "No value")
6182 << " Style: " << query->font_style.computed
6183 << " Weight: " << query->font_weight.computed
6184 << " FontSpec: "
6185 << (query->text->font_specification.value ? query->text->font_specification.value : "No value")
6186 << std::endl;
6187 }
6189 static void sp_print_fontweight( SPStyle *query ) {
6190 const gchar* names[] = {"100", "200", "300", "400", "500", "600", "700", "800", "900",
6191 "NORMAL", "BOLD", "LIGHTER", "BOLDER", "Out of range"};
6192 // Missing book = 380
6193 int index = query->font_weight.computed;
6194 if( index < 0 || index > 13 ) index = 13;
6195 std::cout << " Weight: " << names[ index ]
6196 << " (" << query->font_weight.computed << ")" << std::endl;
6198 }
6200 static void sp_print_fontstyle( SPStyle *query ) {
6202 const gchar* names[] = {"NORMAL", "ITALIC", "OBLIQUE", "Out of range"};
6203 int index = query->font_style.computed;
6204 if( index < 0 || index > 3 ) index = 3;
6205 std::cout << " Style: " << names[ index ] << std::endl;
6207 }
6208 #endif
6210 // Format family drop-down menu.
6211 static void cell_data_func(GtkCellLayout * /*cell_layout*/,
6212 GtkCellRenderer *cell,
6213 GtkTreeModel *tree_model,
6214 GtkTreeIter *iter,
6215 gpointer /*data*/)
6216 {
6217 gchar *family;
6218 gtk_tree_model_get(tree_model, iter, 0, &family, -1);
6219 gchar *const family_escaped = g_markup_escape_text(family, -1);
6221 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6222 int show_sample = prefs->getInt("/tools/text/show_sample_in_list", 1);
6223 if (show_sample) {
6225 Glib::ustring sample = prefs->getString("/tools/text/font_sample");
6226 gchar *const sample_escaped = g_markup_escape_text(sample.data(), -1);
6228 std::stringstream markup;
6229 markup << family_escaped << " <span foreground='gray' font_family='"
6230 << family_escaped << "'>" << sample_escaped << "</span>";
6231 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6233 g_free(sample_escaped);
6234 } else {
6235 g_object_set (G_OBJECT (cell), "markup", family_escaped, NULL);
6236 }
6237 // This doesn't work for two reasons... it set both selected and not selected backgrounds
6238 // to white.. which means that white foreground text is invisible. It also only effects
6239 // the text region, leaving the padding untouched.
6240 // g_object_set (G_OBJECT (cell), "cell-background", "white", "cell-background-set", true, NULL);
6242 g_free(family);
6243 g_free(family_escaped);
6244 }
6246 // Font family
6247 static void sp_text_fontfamily_value_changed( Ink_ComboBoxEntry_Action *act, GObject *tbl )
6248 {
6249 #ifdef DEBUG_TEXT
6250 std::cout << std::endl;
6251 std::cout << "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" << std::endl;
6252 std::cout << "sp_text_fontfamily_value_changed: " << std::endl;
6253 #endif
6255 // quit if run by the _changed callbacks
6256 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6257 return;
6258 }
6259 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6261 gchar *family = ink_comboboxentry_action_get_active_text( act );
6262 #ifdef DEBUG_TEXT
6263 std::cout << " New family: " << family << std::endl;
6264 #endif
6266 // First try to get the old font spec from the stored value
6267 SPStyle *query = sp_style_new (SP_ACTIVE_DOCUMENT);
6268 int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6270 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
6272 // If that didn't work, try to get font spec from style
6273 if (fontSpec.empty()) {
6275 // Must query all to fill font-family, font-style, font-weight, font-specification
6276 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6277 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6278 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6280 // Construct a new font specification if it does not yet exist
6281 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6282 if( fontFromStyle ) {
6283 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6284 fontFromStyle->Unref();
6285 }
6286 #ifdef DEBUG_TEXT
6287 std::cout << " Fontspec not defined, reconstructed from style :" << fontSpec << ":" << std::endl;
6288 sp_print_font( query );
6289 #endif
6290 }
6292 // And if that didn't work use default
6293 if( fontSpec.empty() ) {
6294 sp_style_read_from_prefs(query, "/tools/text");
6295 #ifdef DEBUG_TEXT
6296 std::cout << " read style from prefs:" << std::endl;
6297 sp_print_font( query );
6298 #endif
6299 // Construct a new font specification if it does not yet exist
6300 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6301 if( fontFromStyle ) {
6302 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6303 fontFromStyle->Unref();
6304 }
6305 #ifdef DEBUG_TEXT
6306 std::cout << " Fontspec not defined, reconstructed from style :" << fontSpec << ":" << std::endl;
6307 sp_print_font( query );
6308 #endif
6309 }
6311 SPCSSAttr *css = sp_repr_css_attr_new ();
6312 if (!fontSpec.empty()) {
6314 // Now we have a font specification, replace family.
6315 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
6317 #ifdef DEBUG_TEXT
6318 std::cout << " New FontSpec from ReplaceFontSpecificationFamily :" << newFontSpec << ":" << std::endl;
6319 #endif
6321 if (!newFontSpec.empty()) {
6323 if (fontSpec != newFontSpec) {
6325 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
6327 if (font) {
6328 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6330 // Set all the these just in case they were altered when finding the best
6331 // match for the new family and old style...
6333 gchar c[256];
6335 font->Family(c, 256);
6337 sp_repr_css_set_property (css, "font-family", c);
6339 font->Attribute( "weight", c, 256);
6340 sp_repr_css_set_property (css, "font-weight", c);
6342 font->Attribute("style", c, 256);
6343 sp_repr_css_set_property (css, "font-style", c);
6345 font->Attribute("stretch", c, 256);
6346 sp_repr_css_set_property (css, "font-stretch", c);
6348 font->Attribute("variant", c, 256);
6349 sp_repr_css_set_property (css, "font-variant", c);
6351 font->Unref();
6352 }
6353 }
6355 } else {
6357 // newFontSpec empty
6358 // If the old font on selection (or default) does not exist on the system,
6359 // or the new font family does not exist,
6360 // ReplaceFontSpecificationFamily does not work. In that case we fall back to blindly
6361 // setting the family reported by the family chooser.
6363 // g_print ("fallback setting family: %s\n", family);
6364 sp_repr_css_set_property (css, "-inkscape-font-specification", family);
6365 sp_repr_css_set_property (css, "font-family", family);
6366 // Shoud we set other css font attributes?
6367 }
6369 } // fontSpec not empty or not
6371 // If querying returned nothing, update default style.
6372 if (result_fontspec == QUERY_STYLE_NOTHING)
6373 {
6374 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6375 prefs->mergeStyle("/tools/text/style", css);
6376 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace through a verb
6377 }
6378 else
6379 {
6380 sp_desktop_set_style (SP_ACTIVE_DESKTOP, css, true, true);
6381 }
6383 sp_style_unref(query);
6385 g_free (family);
6387 // Save for undo
6388 DocumentUndo::done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6389 _("Text: Change font family"));
6390 sp_repr_css_attr_unref (css);
6392 // unfreeze
6393 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6395 // focus to canvas
6396 gtk_widget_grab_focus (GTK_WIDGET((SP_ACTIVE_DESKTOP)->canvas));
6398 #ifdef DEBUG_TEXT
6399 std::cout << "sp_text_toolbox_fontfamily_changes: exit" << std::endl;
6400 std::cout << "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" << std::endl;
6401 std::cout << std::endl;
6402 #endif
6403 }
6405 // Font size
6406 static void sp_text_fontsize_value_changed( Ink_ComboBoxEntry_Action *act, GObject *tbl )
6407 {
6408 // quit if run by the _changed callbacks
6409 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6410 return;
6411 }
6412 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6414 gchar *text = ink_comboboxentry_action_get_active_text( act );
6415 gchar *endptr;
6416 gdouble size = g_strtod( text, &endptr );
6417 if (endptr == text) { // Conversion failed, non-numeric input.
6418 g_warning( "Conversion of size text to double failed, input: %s\n", text );
6419 g_free( text );
6420 return;
6421 }
6422 g_free( text );
6424 // Set css font size.
6425 SPCSSAttr *css = sp_repr_css_attr_new ();
6426 Inkscape::CSSOStringStream osfs;
6427 osfs << size << "px"; // For now always use px
6428 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
6430 // Apply font size to selected objects.
6431 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6432 sp_desktop_set_style (desktop, css, true, true);
6434 // Save for undo
6435 DocumentUndo::maybeDone(sp_desktop_document(SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
6436 _("Text: Change font size"));
6438 // If no selected objects, set default.
6439 SPStyle *query = sp_style_new (SP_ACTIVE_DOCUMENT);
6440 int result_numbers =
6441 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6442 if (result_numbers == QUERY_STYLE_NOTHING)
6443 {
6444 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6445 prefs->mergeStyle("/tools/text/style", css);
6446 }
6447 sp_style_unref(query);
6449 sp_repr_css_attr_unref (css);
6451 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6452 }
6454 // Handles both Bold and Italic/Oblique
6455 static void sp_text_style_changed( InkToggleAction* act, GObject *tbl )
6456 {
6457 // quit if run by the _changed callbacks
6458 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6459 return;
6460 }
6461 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6463 // Called by Bold or Italics button?
6464 const gchar* name = gtk_action_get_name( GTK_ACTION( act ) );
6465 gint prop = (strcmp(name, "TextBoldAction") == 0) ? 0 : 1;
6467 // First query font-specification, this is the most complete font face description.
6468 SPStyle *query = sp_style_new (SP_ACTIVE_DOCUMENT);
6469 int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6471 // font_specification will not be set unless defined explicitely on a tspan.
6472 // This should be fixed!
6473 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
6475 if (fontSpec.empty()) {
6476 // Construct a new font specification if it does not yet exist
6477 // Must query font-family, font-style, font-weight, to find correct font face.
6478 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6479 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6480 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6482 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6483 if( fontFromStyle ) {
6484 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6485 fontFromStyle->Unref();
6486 }
6487 }
6489 // Now that we have the old face, find the new face.
6490 Glib::ustring newFontSpec = "";
6491 SPCSSAttr *css = sp_repr_css_attr_new ();
6492 gboolean nochange = true;
6493 gboolean active = gtk_toggle_action_get_active( GTK_TOGGLE_ACTION(act) );
6495 switch (prop)
6496 {
6497 case 0:
6498 {
6499 // Bold
6500 if (!fontSpec.empty()) {
6502 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
6504 if (!newFontSpec.empty()) {
6506 // Set weight if we found font.
6507 font_instance * font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
6508 if (font) {
6509 gchar c[256];
6510 font->Attribute( "weight", c, 256);
6511 sp_repr_css_set_property (css, "font-weight", c);
6512 font->Unref();
6513 font = NULL;
6514 }
6515 nochange = false;
6516 }
6517 }
6518 // Reset button if no change.
6519 // The reset code didn't work in 0.47 and doesn't here... one must prevent an infinite loop
6520 /*
6521 if(nochange) {
6522 gtk_action_block_activate( GTK_ACTION(act) );
6523 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), !active );
6524 gtk_action_unblock_activate( GTK_ACTION(act) );
6525 }
6526 */
6527 break;
6528 }
6530 case 1:
6531 {
6532 // Italic/Oblique
6533 if (!fontSpec.empty()) {
6535 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
6537 if (!newFontSpec.empty()) {
6539 // Don't even set the italic/oblique if the font didn't exist on the system
6540 if( active ) {
6541 if( newFontSpec.find( "Italic" ) != Glib::ustring::npos ) {
6542 sp_repr_css_set_property (css, "font-style", "italic");
6543 } else {
6544 sp_repr_css_set_property (css, "font-style", "oblique");
6545 }
6546 } else {
6547 sp_repr_css_set_property (css, "font-style", "normal");
6548 }
6549 nochange = false;
6550 }
6551 }
6552 // Reset button if no change.
6553 // The reset code didn't work in 0.47... one must prevent an infinite loop
6554 /*
6555 if(nochange) {
6556 gtk_action_block_activate( GTK_ACTION(act) );
6557 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), !active );
6558 gtk_action_unblock_activate( GTK_ACTION(act) );
6559 }
6560 */
6561 break;
6562 }
6563 }
6565 if (!newFontSpec.empty()) {
6566 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6567 }
6569 // If querying returned nothing, update default style.
6570 if (result_fontspec == QUERY_STYLE_NOTHING)
6571 {
6572 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6573 prefs->mergeStyle("/tools/text/style", css);
6574 }
6576 sp_style_unref(query);
6578 // Do we need to update other CSS values?
6579 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6580 sp_desktop_set_style (desktop, css, true, true);
6581 DocumentUndo::done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6582 _("Text: Change font style"));
6583 sp_repr_css_attr_unref (css);
6585 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6586 }
6588 // Handles both Superscripts and Subscripts
6589 static void sp_text_script_changed( InkToggleAction* act, GObject *tbl )
6590 {
6591 // quit if run by the _changed callbacks
6592 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6593 return;
6594 }
6595 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6597 // Called by Superscript or Subscript button?
6598 const gchar* name = gtk_action_get_name( GTK_ACTION( act ) );
6599 gint prop = (strcmp(name, "TextSuperscriptAction") == 0) ? 0 : 1;
6601 #ifdef DEBUG_TEXT
6602 std::cout << "sp_text_script_changed: " << prop << std::endl;
6603 #endif
6605 // Query baseline
6606 SPStyle *query = sp_style_new (SP_ACTIVE_DOCUMENT);
6607 int result_baseline = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_BASELINES);
6609 bool setSuper = false;
6610 bool setSub = false;
6612 if(result_baseline == QUERY_STYLE_NOTHING || result_baseline == QUERY_STYLE_MULTIPLE_DIFFERENT ) {
6613 // If not set or mixed, turn on superscript or subscript
6614 if( prop == 0 ) {
6615 setSuper = true;
6616 } else {
6617 setSub = true;
6618 }
6619 } else {
6620 // Superscript
6621 gboolean superscriptSet = (query->baseline_shift.set &&
6622 query->baseline_shift.type == SP_BASELINE_SHIFT_LITERAL &&
6623 query->baseline_shift.literal == SP_CSS_BASELINE_SHIFT_SUPER );
6625 // Subscript
6626 gboolean subscriptSet = (query->baseline_shift.set &&
6627 query->baseline_shift.type == SP_BASELINE_SHIFT_LITERAL &&
6628 query->baseline_shift.literal == SP_CSS_BASELINE_SHIFT_SUB );
6630 setSuper = !superscriptSet && prop == 0;
6631 setSub = !subscriptSet && prop == 1;
6632 }
6634 // Set css properties
6635 SPCSSAttr *css = sp_repr_css_attr_new ();
6636 if( setSuper || setSub ) {
6637 // Openoffice 2.3 and Adobe use 58%, Microsoft Word 2002 uses 65%, LaTex about 70%.
6638 // 58% looks too small to me, especially if a superscript is placed on a superscript.
6639 // If you make a change here, consider making a change to baseline-shift amount
6640 // in style.cpp.
6641 sp_repr_css_set_property (css, "font-size", "65%");
6642 } else {
6643 sp_repr_css_set_property (css, "font-size", "");
6644 }
6645 if( setSuper ) {
6646 sp_repr_css_set_property (css, "baseline-shift", "super");
6647 } else if( setSub ) {
6648 sp_repr_css_set_property (css, "baseline-shift", "sub");
6649 } else {
6650 sp_repr_css_set_property (css, "baseline-shift", "baseline");
6651 }
6653 // Apply css to selected objects.
6654 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6655 sp_desktop_set_style (desktop, css, true, false);
6657 // Save for undo
6658 DocumentUndo::maybeDone(sp_desktop_document(SP_ACTIVE_DESKTOP), "ttb:script", SP_VERB_NONE,
6659 _("Text: Change superscript or subscript"));
6661 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6662 }
6664 static void sp_text_align_mode_changed( EgeSelectOneAction *act, GObject *tbl )
6665 {
6666 // quit if run by the _changed callbacks
6667 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6668 return;
6669 }
6670 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6672 int mode = ege_select_one_action_get_active( act );
6674 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6675 prefs->setInt("/tools/text/align_mode", mode);
6677 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6679 // move the x of all texts to preserve the same bbox
6680 Inkscape::Selection *selection = sp_desktop_selection(desktop);
6681 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
6682 if (SP_IS_TEXT((SPItem *) items->data)) {
6683 SPItem *item = SP_ITEM(items->data);
6685 unsigned writing_mode = SP_OBJECT_STYLE(item)->writing_mode.value;
6686 // below, variable names suggest horizontal move, but we check the writing direction
6687 // and move in the corresponding axis
6688 int axis;
6689 if (writing_mode == SP_CSS_WRITING_MODE_LR_TB || writing_mode == SP_CSS_WRITING_MODE_RL_TB) {
6690 axis = NR::X;
6691 } else {
6692 axis = NR::Y;
6693 }
6695 Geom::OptRect bbox
6696 = item->getBounds(Geom::identity(), SPItem::GEOMETRIC_BBOX);
6697 if (!bbox)
6698 continue;
6699 double width = bbox->dimensions()[axis];
6700 // If you want to align within some frame, other than the text's own bbox, calculate
6701 // the left and right (or top and bottom for tb text) slacks of the text inside that
6702 // frame (currently unused)
6703 double left_slack = 0;
6704 double right_slack = 0;
6705 unsigned old_align = SP_OBJECT_STYLE(item)->text_align.value;
6706 double move = 0;
6707 if (old_align == SP_CSS_TEXT_ALIGN_START || old_align == SP_CSS_TEXT_ALIGN_LEFT) {
6708 switch (mode) {
6709 case 0:
6710 move = -left_slack;
6711 break;
6712 case 1:
6713 move = width/2 + (right_slack - left_slack)/2;
6714 break;
6715 case 2:
6716 move = width + right_slack;
6717 break;
6718 }
6719 } else if (old_align == SP_CSS_TEXT_ALIGN_CENTER) {
6720 switch (mode) {
6721 case 0:
6722 move = -width/2 - left_slack;
6723 break;
6724 case 1:
6725 move = (right_slack - left_slack)/2;
6726 break;
6727 case 2:
6728 move = width/2 + right_slack;
6729 break;
6730 }
6731 } else if (old_align == SP_CSS_TEXT_ALIGN_END || old_align == SP_CSS_TEXT_ALIGN_RIGHT) {
6732 switch (mode) {
6733 case 0:
6734 move = -width - left_slack;
6735 break;
6736 case 1:
6737 move = -width/2 + (right_slack - left_slack)/2;
6738 break;
6739 case 2:
6740 move = right_slack;
6741 break;
6742 }
6743 }
6744 Geom::Point XY = SP_TEXT(item)->attributes.firstXY();
6745 if (axis == NR::X) {
6746 XY = XY + Geom::Point (move, 0);
6747 } else {
6748 XY = XY + Geom::Point (0, move);
6749 }
6750 SP_TEXT(item)->attributes.setFirstXY(XY);
6751 SP_OBJECT(item)->updateRepr();
6752 SP_OBJECT(item)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
6753 }
6754 }
6756 SPCSSAttr *css = sp_repr_css_attr_new ();
6757 switch (mode)
6758 {
6759 case 0:
6760 {
6761 sp_repr_css_set_property (css, "text-anchor", "start");
6762 sp_repr_css_set_property (css, "text-align", "start");
6763 break;
6764 }
6765 case 1:
6766 {
6767 sp_repr_css_set_property (css, "text-anchor", "middle");
6768 sp_repr_css_set_property (css, "text-align", "center");
6769 break;
6770 }
6772 case 2:
6773 {
6774 sp_repr_css_set_property (css, "text-anchor", "end");
6775 sp_repr_css_set_property (css, "text-align", "end");
6776 break;
6777 }
6779 case 3:
6780 {
6781 sp_repr_css_set_property (css, "text-anchor", "start");
6782 sp_repr_css_set_property (css, "text-align", "justify");
6783 break;
6784 }
6785 }
6787 SPStyle *query =
6788 sp_style_new (SP_ACTIVE_DOCUMENT);
6789 int result_numbers =
6790 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6792 // If querying returned nothing, update default style.
6793 if (result_numbers == QUERY_STYLE_NOTHING)
6794 {
6795 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6796 prefs->mergeStyle("/tools/text/style", css);
6797 }
6799 sp_style_unref(query);
6801 sp_desktop_set_style (desktop, css, true, true);
6802 DocumentUndo::done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6803 _("Text: Change alignment"));
6804 sp_repr_css_attr_unref (css);
6806 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6808 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6809 }
6811 static void sp_text_lineheight_value_changed( GtkAdjustment *adj, GObject *tbl )
6812 {
6813 // quit if run by the _changed callbacks
6814 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6815 return;
6816 }
6817 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6819 // At the moment this handles only numerical values (i.e. no percent).
6820 // Set css line height.
6821 SPCSSAttr *css = sp_repr_css_attr_new ();
6822 Inkscape::CSSOStringStream osfs;
6823 osfs << adj->value*100 << "%";
6824 sp_repr_css_set_property (css, "line-height", osfs.str().c_str());
6826 // Apply line-height to selected objects.
6827 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6828 sp_desktop_set_style (desktop, css, true, false);
6831 // Until deprecated sodipodi:linespacing purged:
6832 Inkscape::Selection *selection = sp_desktop_selection(desktop);
6833 GSList const *items = selection->itemList();
6834 for (; items != NULL; items = items->next) {
6835 if (SP_IS_TEXT (items->data)) {
6836 SP_OBJECT_REPR(items->data)->setAttribute("sodipodi:linespacing", sp_repr_css_property (css, "line-height", NULL));
6837 }
6838 }
6840 // Save for undo
6841 DocumentUndo::maybeDone(sp_desktop_document(SP_ACTIVE_DESKTOP), "ttb:line-height", SP_VERB_NONE,
6842 _("Text: Change line-height"));
6844 // If no selected objects, set default.
6845 SPStyle *query = sp_style_new (SP_ACTIVE_DOCUMENT);
6846 int result_numbers =
6847 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6848 if (result_numbers == QUERY_STYLE_NOTHING)
6849 {
6850 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6851 prefs->mergeStyle("/tools/text/style", css);
6852 }
6853 sp_style_unref(query);
6855 sp_repr_css_attr_unref (css);
6857 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6858 }
6860 static void sp_text_wordspacing_value_changed( GtkAdjustment *adj, GObject *tbl )
6861 {
6862 // quit if run by the _changed callbacks
6863 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6864 return;
6865 }
6866 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6868 // At the moment this handles only numerical values (i.e. no em unit).
6869 // Set css word-spacing
6870 SPCSSAttr *css = sp_repr_css_attr_new ();
6871 Inkscape::CSSOStringStream osfs;
6872 osfs << adj->value << "px"; // For now always use px
6873 sp_repr_css_set_property (css, "word-spacing", osfs.str().c_str());
6875 // Apply word-spacing to selected objects.
6876 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6877 sp_desktop_set_style (desktop, css, true, false);
6879 // Save for undo
6880 DocumentUndo::maybeDone(sp_desktop_document(SP_ACTIVE_DESKTOP), "ttb:word-spacing", SP_VERB_NONE,
6881 _("Text: Change word-spacing"));
6883 // If no selected objects, set default.
6884 SPStyle *query = sp_style_new (SP_ACTIVE_DOCUMENT);
6885 int result_numbers =
6886 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6887 if (result_numbers == QUERY_STYLE_NOTHING)
6888 {
6889 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6890 prefs->mergeStyle("/tools/text/style", css);
6891 }
6892 sp_style_unref(query);
6894 sp_repr_css_attr_unref (css);
6896 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6897 }
6899 static void sp_text_letterspacing_value_changed( GtkAdjustment *adj, GObject *tbl )
6900 {
6901 // quit if run by the _changed callbacks
6902 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6903 return;
6904 }
6905 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6907 // At the moment this handles only numerical values (i.e. no em unit).
6908 // Set css letter-spacing
6909 SPCSSAttr *css = sp_repr_css_attr_new ();
6910 Inkscape::CSSOStringStream osfs;
6911 osfs << adj->value << "px"; // For now always use px
6912 sp_repr_css_set_property (css, "letter-spacing", osfs.str().c_str());
6914 // Apply letter-spacing to selected objects.
6915 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6916 sp_desktop_set_style (desktop, css, true, false);
6918 // Save for undo
6919 DocumentUndo::maybeDone(sp_desktop_document(SP_ACTIVE_DESKTOP), "ttb:letter-spacing", SP_VERB_NONE,
6920 _("Text: Change letter-spacing"));
6922 // If no selected objects, set default.
6923 SPStyle *query = sp_style_new (SP_ACTIVE_DOCUMENT);
6924 int result_numbers =
6925 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6926 if (result_numbers == QUERY_STYLE_NOTHING)
6927 {
6928 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6929 prefs->mergeStyle("/tools/text/style", css);
6930 }
6931 sp_style_unref(query);
6933 sp_repr_css_attr_unref (css);
6935 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6936 }
6939 static void sp_text_dx_value_changed( GtkAdjustment *adj, GObject *tbl )
6940 {
6941 // quit if run by the _changed callbacks
6942 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6943 return;
6944 }
6945 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6947 gdouble new_dx = adj->value;
6949 if( SP_IS_TEXT_CONTEXT((SP_ACTIVE_DESKTOP)->event_context) ) {
6950 SPTextContext *const tc = SP_TEXT_CONTEXT((SP_ACTIVE_DESKTOP)->event_context);
6951 if( tc ) {
6952 unsigned char_index = -1;
6953 TextTagAttributes *attributes =
6954 text_tag_attributes_at_position( tc->text, std::min(tc->text_sel_start, tc->text_sel_end), &char_index );
6955 if( attributes ) {
6956 double old_dx = attributes->getDx( char_index );
6957 double delta_dx = new_dx - old_dx;
6958 sp_te_adjust_dx( tc->text, tc->text_sel_start, tc->text_sel_end, SP_ACTIVE_DESKTOP, delta_dx );
6959 }
6960 }
6961 }
6963 // Save for undo
6964 DocumentUndo::maybeDone(sp_desktop_document(SP_ACTIVE_DESKTOP), "ttb:dx", SP_VERB_NONE,
6965 _("Text: Change dx (kern)"));
6967 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6968 }
6970 static void sp_text_dy_value_changed( GtkAdjustment *adj, GObject *tbl )
6971 {
6972 // quit if run by the _changed callbacks
6973 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6974 return;
6975 }
6976 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6978 gdouble new_dy = adj->value;
6980 if( SP_IS_TEXT_CONTEXT((SP_ACTIVE_DESKTOP)->event_context) ) {
6981 SPTextContext *const tc = SP_TEXT_CONTEXT((SP_ACTIVE_DESKTOP)->event_context);
6982 if( tc ) {
6983 unsigned char_index = -1;
6984 TextTagAttributes *attributes =
6985 text_tag_attributes_at_position( tc->text, std::min(tc->text_sel_start, tc->text_sel_end), &char_index );
6986 if( attributes ) {
6987 double old_dy = attributes->getDy( char_index );
6988 double delta_dy = new_dy - old_dy;
6989 sp_te_adjust_dy( tc->text, tc->text_sel_start, tc->text_sel_end, SP_ACTIVE_DESKTOP, delta_dy );
6990 }
6991 }
6992 }
6994 // Save for undo
6995 DocumentUndo::maybeDone(sp_desktop_document(SP_ACTIVE_DESKTOP), "ttb:dy", SP_VERB_NONE,
6996 _("Text: Change dy"));
6998 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6999 }
7001 static void sp_text_rotation_value_changed( GtkAdjustment *adj, GObject *tbl )
7002 {
7003 // quit if run by the _changed callbacks
7004 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
7005 return;
7006 }
7007 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7009 gdouble new_degrees = adj->value;
7011 if( SP_IS_TEXT_CONTEXT((SP_ACTIVE_DESKTOP)->event_context) ) {
7012 SPTextContext *const tc = SP_TEXT_CONTEXT((SP_ACTIVE_DESKTOP)->event_context);
7013 if( tc ) {
7014 unsigned char_index = -1;
7015 TextTagAttributes *attributes =
7016 text_tag_attributes_at_position( tc->text, std::min(tc->text_sel_start, tc->text_sel_end), &char_index );
7017 if( attributes ) {
7018 double old_degrees = attributes->getRotate( char_index );
7019 double delta_deg = new_degrees - old_degrees;
7020 sp_te_adjust_rotation( tc->text, tc->text_sel_start, tc->text_sel_end, SP_ACTIVE_DESKTOP, delta_deg );
7021 }
7022 }
7023 }
7025 // Save for undo
7026 DocumentUndo::maybeDone(sp_desktop_document(SP_ACTIVE_DESKTOP), "ttb:rotate", SP_VERB_NONE,
7027 _("Text: Change rotate"));
7029 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7030 }
7032 static void sp_text_orientation_mode_changed( EgeSelectOneAction *act, GObject *tbl )
7033 {
7034 // quit if run by the _changed callbacks
7035 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
7036 return;
7037 }
7038 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7040 int mode = ege_select_one_action_get_active( act );
7042 SPCSSAttr *css = sp_repr_css_attr_new ();
7043 switch (mode)
7044 {
7045 case 0:
7046 {
7047 sp_repr_css_set_property (css, "writing-mode", "lr");
7048 break;
7049 }
7051 case 1:
7052 {
7053 sp_repr_css_set_property (css, "writing-mode", "tb");
7054 break;
7055 }
7056 }
7058 SPStyle *query =
7059 sp_style_new (SP_ACTIVE_DOCUMENT);
7060 int result_numbers =
7061 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
7063 // If querying returned nothing, update default style.
7064 if (result_numbers == QUERY_STYLE_NOTHING)
7065 {
7066 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7067 prefs->mergeStyle("/tools/text/style", css);
7068 }
7070 sp_desktop_set_style (SP_ACTIVE_DESKTOP, css, true, true);
7071 DocumentUndo::done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
7072 _("Text: Change orientation"));
7073 sp_repr_css_attr_unref (css);
7075 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7076 }
7078 /*
7079 * This function sets up the text-tool tool-controls, setting the entry boxes
7080 * etc. to the values from the current selection or the default if no selection.
7081 * It is called whenever a text selection is changed, including stepping cursor
7082 * through text.
7083 */
7084 static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/, GObject *tbl)
7085 {
7086 #ifdef DEBUG_TEXT
7087 static int count = 0;
7088 ++count;
7089 std::cout << std::endl;
7090 std::cout << "&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&" << std::endl;
7091 std::cout << "sp_text_toolbox_selection_changed: start " << count << std::endl;
7093 std::cout << " Selected items:" << std::endl;
7094 for (GSList const *items = sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
7095 items != NULL;
7096 items = items->next)
7097 {
7098 const gchar* id = reinterpret_cast<SPItem *>(items->data)->getId();
7099 std::cout << " " << id << std::endl;
7100 }
7101 Glib::ustring selected_text = sp_text_get_selected_text((SP_ACTIVE_DESKTOP)->event_context);
7102 std::cout << " Selected text:" << std::endl;
7103 std::cout << selected_text << std::endl;
7104 #endif
7106 // quit if run by the _changed callbacks
7107 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
7108 #ifdef DEBUG_TEXT
7109 std::cout << " Frozen, returning" << std::endl;
7110 std::cout << "&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&" << std::endl;
7111 std::cout << std::endl;
7112 #endif
7113 return;
7114 }
7115 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7117 // Only flowed text can be justified, only normal text can be kerned...
7118 // Find out if we have flowed text now so we can use it several places
7119 gboolean isFlow = false;
7120 for (GSList const *items = sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
7121 items != NULL;
7122 items = items->next) {
7123 // const gchar* id = reinterpret_cast<SPItem *>(items->data)->getId();
7124 // std::cout << " " << id << std::endl;
7125 if( SP_IS_FLOWTEXT(( SPItem *) items->data )) {
7126 isFlow = true;
7127 // std::cout << " Found flowed text" << std::endl;
7128 break;
7129 }
7130 }
7132 /*
7133 * Query from current selection:
7134 * Font family (font-family)
7135 * Style (font-weight, font-style, font-stretch, font-variant, font-align)
7136 * Numbers (font-size, letter-spacing, word-spacing, line-height, text-anchor, writing-mode)
7137 * Font specification (Inkscape private attribute)
7138 */
7139 SPStyle *query =
7140 sp_style_new (SP_ACTIVE_DOCUMENT);
7141 int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
7142 int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
7143 int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
7144 int result_baseline = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_BASELINES);
7146 // Used later:
7147 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
7149 /*
7150 * If no text in selection (querying returned nothing), read the style from
7151 * the /tools/text preferencess (default style for new texts). Return if
7152 * tool bar already set to these preferences.
7153 */
7154 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
7155 // There are no texts in selection, read from preferences.
7156 sp_style_read_from_prefs(query, "/tools/text");
7157 #ifdef DEBUG_TEXT
7158 std::cout << " read style from prefs:" << std::endl;
7159 sp_print_font( query );
7160 #endif
7161 if (g_object_get_data(tbl, "text_style_from_prefs")) {
7162 // Do not reset the toolbar style from prefs if we already did it last time
7163 sp_style_unref(query);
7164 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7165 #ifdef DEBUG_TEXT
7166 std::cout << " text_style_from_prefs: toolbar already set" << std:: endl;
7167 std::cout << "&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&" << std::endl;
7168 std::cout << std::endl;
7169 #endif
7170 return;
7171 }
7173 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
7174 } else {
7175 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
7176 }
7178 // If we have valid query data for text (font-family, font-specification) set toolbar accordingly.
7179 if (query->text)
7180 {
7181 // Font family
7182 if( query->text->font_family.value ) {
7183 gchar *fontFamily = query->text->font_family.value;
7185 Ink_ComboBoxEntry_Action* fontFamilyAction =
7186 INK_COMBOBOXENTRY_ACTION( g_object_get_data( tbl, "TextFontFamilyAction" ) );
7187 ink_comboboxentry_action_set_active_text( fontFamilyAction, fontFamily );
7188 }
7191 // Size (average of text selected)
7192 double size = query->font_size.computed;
7193 gchar size_text[G_ASCII_DTOSTR_BUF_SIZE];
7194 g_ascii_dtostr (size_text, sizeof (size_text), size);
7196 Ink_ComboBoxEntry_Action* fontSizeAction =
7197 INK_COMBOBOXENTRY_ACTION( g_object_get_data( tbl, "TextFontSizeAction" ) );
7198 ink_comboboxentry_action_set_active_text( fontSizeAction, size_text );
7201 // Weight (Bold)
7202 // Note: in the enumeration, normal and lighter come at the end so we must explicitly test for them.
7203 gboolean boldSet = ((query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700) &&
7204 (query->font_weight.computed != SP_CSS_FONT_WEIGHT_NORMAL) &&
7205 (query->font_weight.computed != SP_CSS_FONT_WEIGHT_LIGHTER));
7207 InkToggleAction* textBoldAction = INK_TOGGLE_ACTION( g_object_get_data( tbl, "TextBoldAction" ) );
7208 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(textBoldAction), boldSet );
7211 // Style (Italic/Oblique)
7212 gboolean italicSet = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
7214 InkToggleAction* textItalicAction = INK_TOGGLE_ACTION( g_object_get_data( tbl, "TextItalicAction" ) );
7215 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(textItalicAction), italicSet );
7218 // Superscript
7219 gboolean superscriptSet =
7220 ((result_baseline == QUERY_STYLE_SINGLE || result_baseline == QUERY_STYLE_MULTIPLE_SAME ) &&
7221 query->baseline_shift.set &&
7222 query->baseline_shift.type == SP_BASELINE_SHIFT_LITERAL &&
7223 query->baseline_shift.literal == SP_CSS_BASELINE_SHIFT_SUPER );
7225 InkToggleAction* textSuperscriptAction = INK_TOGGLE_ACTION( g_object_get_data( tbl, "TextSuperscriptAction" ) );
7226 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(textSuperscriptAction), superscriptSet );
7229 // Subscript
7230 gboolean subscriptSet =
7231 ((result_baseline == QUERY_STYLE_SINGLE || result_baseline == QUERY_STYLE_MULTIPLE_SAME ) &&
7232 query->baseline_shift.set &&
7233 query->baseline_shift.type == SP_BASELINE_SHIFT_LITERAL &&
7234 query->baseline_shift.literal == SP_CSS_BASELINE_SHIFT_SUB );
7236 InkToggleAction* textSubscriptAction = INK_TOGGLE_ACTION( g_object_get_data( tbl, "TextSubscriptAction" ) );
7237 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(textSubscriptAction), subscriptSet );
7240 // Alignment
7241 EgeSelectOneAction* textAlignAction = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "TextAlignAction" ) );
7243 // Note: SVG 1.1 doesn't include text-align, SVG 1.2 Tiny doesn't include text-align="justify"
7244 // text-align="justify" was a draft SVG 1.2 item (along with flowed text).
7245 // Only flowed text can be left and right justified at the same time.
7246 // Disable button if we don't have flowed text.
7248 // The GtkTreeModel class doesn't have a set function so we can't
7249 // simply add an ege_select_one_action_set_sensitive method!
7250 // We must set values directly with the GtkListStore and then
7251 // ask that the GtkAction update the sensitive parameters.
7252 GtkListStore * model = GTK_LIST_STORE( ege_select_one_action_get_model( textAlignAction ) );
7253 GtkTreePath * path = gtk_tree_path_new_from_string("3"); // Justify entry
7254 GtkTreeIter iter;
7255 gtk_tree_model_get_iter( GTK_TREE_MODEL (model), &iter, path );
7256 gtk_list_store_set( model, &iter, /* column */ 3, isFlow, -1 );
7257 ege_select_one_action_update_sensitive( textAlignAction );
7258 // ege_select_one_action_set_sensitive( textAlignAction, 3, isFlow );
7260 int activeButton = 0;
7261 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
7262 {
7263 activeButton = 3;
7264 } else {
7265 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START) activeButton = 0;
7266 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE) activeButton = 1;
7267 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END) activeButton = 2;
7268 }
7269 ege_select_one_action_set_active( textAlignAction, activeButton );
7272 // Line height (spacing)
7273 double height;
7274 if (query->line_height.normal) {
7275 height = Inkscape::Text::Layout::LINE_HEIGHT_NORMAL;
7276 } else {
7277 if (query->line_height.unit == SP_CSS_UNIT_PERCENT) {
7278 height = query->line_height.value;
7279 } else {
7280 height = query->line_height.computed;
7281 }
7282 }
7284 GtkAction* lineHeightAction = GTK_ACTION( g_object_get_data( tbl, "TextLineHeightAction" ) );
7285 GtkAdjustment *lineHeightAdjustment =
7286 ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION( lineHeightAction ));
7287 gtk_adjustment_set_value( lineHeightAdjustment, height );
7290 // Word spacing
7291 double wordSpacing;
7292 if (query->word_spacing.normal) wordSpacing = 0.0;
7293 else wordSpacing = query->word_spacing.computed; // Assume no units (change in desktop-style.cpp)
7295 GtkAction* wordSpacingAction = GTK_ACTION( g_object_get_data( tbl, "TextWordSpacingAction" ) );
7296 GtkAdjustment *wordSpacingAdjustment =
7297 ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION( wordSpacingAction ));
7298 gtk_adjustment_set_value( wordSpacingAdjustment, wordSpacing );
7301 // Letter spacing
7302 double letterSpacing;
7303 if (query->letter_spacing.normal) letterSpacing = 0.0;
7304 else letterSpacing = query->letter_spacing.computed; // Assume no units (change in desktop-style.cpp)
7306 GtkAction* letterSpacingAction = GTK_ACTION( g_object_get_data( tbl, "TextLetterSpacingAction" ) );
7307 GtkAdjustment *letterSpacingAdjustment =
7308 ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION( letterSpacingAction ));
7309 gtk_adjustment_set_value( letterSpacingAdjustment, letterSpacing );
7312 // Orientation
7313 int activeButton2 = (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB ? 0 : 1);
7315 EgeSelectOneAction* textOrientationAction =
7316 EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "TextOrientationAction" ) );
7317 ege_select_one_action_set_active( textOrientationAction, activeButton2 );
7320 } // if( query->text )
7322 #ifdef DEBUG_TEXT
7323 std::cout << " GUI: fontfamily.value: "
7324 << (query->text->font_family.value ? query->text->font_family.value : "No value")
7325 << std::endl;
7326 std::cout << " GUI: font_size.computed: " << query->font_size.computed << std::endl;
7327 std::cout << " GUI: font_weight.computed: " << query->font_weight.computed << std::endl;
7328 std::cout << " GUI: font_style.computed: " << query->font_style.computed << std::endl;
7329 std::cout << " GUI: text_anchor.computed: " << query->text_anchor.computed << std::endl;
7330 std::cout << " GUI: text_align.computed: " << query->text_align.computed << std::endl;
7331 std::cout << " GUI: line_height.computed: " << query->line_height.computed
7332 << " line_height.value: " << query->line_height.value
7333 << " line_height.unit: " << query->line_height.unit << std::endl;
7334 std::cout << " GUI: word_spacing.computed: " << query->word_spacing.computed
7335 << " word_spacing.value: " << query->word_spacing.value
7336 << " word_spacing.unit: " << query->word_spacing.unit << std::endl;
7337 std::cout << " GUI: letter_spacing.computed: " << query->letter_spacing.computed
7338 << " letter_spacing.value: " << query->letter_spacing.value
7339 << " letter_spacing.unit: " << query->letter_spacing.unit << std::endl;
7340 std::cout << " GUI: writing_mode.computed: " << query->writing_mode.computed << std::endl;
7341 #endif
7343 sp_style_unref(query);
7345 // Kerning (xshift), yshift, rotation. NB: These are not CSS attributes.
7346 if( SP_IS_TEXT_CONTEXT((SP_ACTIVE_DESKTOP)->event_context) ) {
7347 SPTextContext *const tc = SP_TEXT_CONTEXT((SP_ACTIVE_DESKTOP)->event_context);
7348 if( tc ) {
7349 unsigned char_index = -1;
7350 TextTagAttributes *attributes =
7351 text_tag_attributes_at_position( tc->text, std::min(tc->text_sel_start, tc->text_sel_end), &char_index );
7352 if( attributes ) {
7354 // Dx
7355 double dx = attributes->getDx( char_index );
7356 GtkAction* dxAction = GTK_ACTION( g_object_get_data( tbl, "TextDxAction" ));
7357 GtkAdjustment *dxAdjustment =
7358 ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION( dxAction ));
7359 gtk_adjustment_set_value( dxAdjustment, dx );
7361 // Dy
7362 double dy = attributes->getDy( char_index );
7363 GtkAction* dyAction = GTK_ACTION( g_object_get_data( tbl, "TextDyAction" ));
7364 GtkAdjustment *dyAdjustment =
7365 ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION( dyAction ));
7366 gtk_adjustment_set_value( dyAdjustment, dy );
7368 // Rotation
7369 double rotation = attributes->getRotate( char_index );
7370 /* SVG value is between 0 and 360 but we're using -180 to 180 in widget */
7371 if( rotation > 180.0 ) rotation -= 360.0;
7372 GtkAction* rotationAction = GTK_ACTION( g_object_get_data( tbl, "TextRotationAction" ));
7373 GtkAdjustment *rotationAdjustment =
7374 ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION( rotationAction ));
7375 gtk_adjustment_set_value( rotationAdjustment, rotation );
7377 #ifdef DEBUG_TEXT
7378 std::cout << " GUI: Dx: " << dx << std::endl;
7379 std::cout << " GUI: Dy: " << dy << std::endl;
7380 std::cout << " GUI: Rotation: " << rotation << std::endl;
7381 #endif
7382 }
7383 }
7384 }
7386 {
7387 // Set these here as we don't always have kerning/rotating attributes
7388 GtkAction* dxAction = GTK_ACTION( g_object_get_data( tbl, "TextDxAction" ));
7389 gtk_action_set_sensitive( GTK_ACTION(dxAction), !isFlow );
7391 GtkAction* dyAction = GTK_ACTION( g_object_get_data( tbl, "TextDyAction" ));
7392 gtk_action_set_sensitive( GTK_ACTION(dyAction), !isFlow );
7394 GtkAction* rotationAction = GTK_ACTION( g_object_get_data( tbl, "TextRotationAction" ));
7395 gtk_action_set_sensitive( GTK_ACTION(rotationAction), !isFlow );
7396 }
7398 #ifdef DEBUG_TEXT
7399 std::cout << "&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&" << std::endl;
7400 std::cout << std::endl;
7401 #endif
7403 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7405 }
7407 static void sp_text_toolbox_selection_modified(Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
7408 {
7409 sp_text_toolbox_selection_changed (selection, tbl);
7410 }
7412 void
7413 sp_text_toolbox_subselection_changed (gpointer /*tc*/, GObject *tbl)
7414 {
7415 sp_text_toolbox_selection_changed (NULL, tbl);
7416 }
7418 // Define all the "widgets" in the toolbar.
7419 static void sp_text_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
7420 {
7421 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7422 Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
7424 // Is this used?
7425 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
7426 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
7427 g_object_set_data( holder, "tracker", tracker );
7429 /* Font family */
7430 {
7431 // Font list
7432 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
7433 GtkListStore* model = store->gobj();
7435 Ink_ComboBoxEntry_Action* act = ink_comboboxentry_action_new( "TextFontFamilyAction",
7436 _("Font Family"),
7437 _("Select Font Family (Alt-X to access)"),
7438 NULL,
7439 GTK_TREE_MODEL(model),
7440 -1, // Entry width
7441 50, // Extra list width
7442 (gpointer)cell_data_func ); // Cell layout
7443 ink_comboboxentry_action_popup_enable( act ); // Enable entry completion
7444 gchar *const warning = _("Font not found on system");
7445 ink_comboboxentry_action_set_warning( act, warning ); // Show icon with tooltip if missing font
7446 ink_comboboxentry_action_set_altx_name( act, "altx-text" ); // Set Alt-X keyboard shortcut
7447 g_signal_connect( G_OBJECT(act), "changed", G_CALLBACK(sp_text_fontfamily_value_changed), holder );
7448 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
7449 g_object_set_data( holder, "TextFontFamilyAction", act );
7451 // Change style of drop-down from menu to list
7452 gtk_rc_parse_string (
7453 "style \"dropdown-as-list-style\"\n"
7454 "{\n"
7455 " GtkComboBox::appears-as-list = 1\n"
7456 "}\n"
7457 "widget \"*.TextFontFamilyAction_combobox\" style \"dropdown-as-list-style\"");
7458 }
7460 /* Font size */
7461 {
7462 // List of font sizes for drop-down menu
7463 GtkListStore* model_size = gtk_list_store_new( 1, G_TYPE_STRING );
7464 gchar const *const sizes[] = {
7465 "4", "6", "8", "9", "10", "11", "12", "13", "14", "16",
7466 "18", "20", "22", "24", "28", "32", "36", "40", "48", "56",
7467 "64", "72", "144"
7468 };
7469 for( unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i ) {
7470 GtkTreeIter iter;
7471 gtk_list_store_append( model_size, &iter );
7472 gtk_list_store_set( model_size, &iter, 0, sizes[i], -1 );
7473 }
7475 Ink_ComboBoxEntry_Action* act = ink_comboboxentry_action_new( "TextFontSizeAction",
7476 _("Font Size"),
7477 _("Font size (px)"),
7478 NULL,
7479 GTK_TREE_MODEL(model_size),
7480 4 ); // Width in characters
7481 g_signal_connect( G_OBJECT(act), "changed", G_CALLBACK(sp_text_fontsize_value_changed), holder );
7482 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
7483 g_object_set_data( holder, "TextFontSizeAction", act );
7484 }
7486 /* Style - Bold */
7487 {
7488 InkToggleAction* act = ink_toggle_action_new( "TextBoldAction", // Name
7489 _("Toggle Bold"), // Label
7490 _("Toggle bold or normal weight"), // Tooltip
7491 GTK_STOCK_BOLD, // Icon (inkId)
7492 secondarySize ); // Icon size
7493 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7494 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_text_style_changed), holder );
7495 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/text/bold", false) );
7496 g_object_set_data( holder, "TextBoldAction", act );
7497 }
7499 /* Style - Italic/Oblique */
7500 {
7501 InkToggleAction* act = ink_toggle_action_new( "TextItalicAction", // Name
7502 _("Toggle Italic/Oblique"), // Label
7503 _("Toggle italic/oblique style"),// Tooltip
7504 GTK_STOCK_ITALIC, // Icon (inkId)
7505 secondarySize ); // Icon size
7506 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7507 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_text_style_changed), holder );
7508 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/text/italic", false) );
7509 g_object_set_data( holder, "TextItalicAction", act );
7510 }
7512 /* Style - Superscript */
7513 {
7514 InkToggleAction* act = ink_toggle_action_new( "TextSuperscriptAction", // Name
7515 _("Toggle Superscript"), // Label
7516 _("Toggle superscript"), // Tooltip
7517 "text_superscript", // Icon (inkId)
7518 secondarySize ); // Icon size
7519 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7520 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_text_script_changed), holder );
7521 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/text/super", false) );
7522 g_object_set_data( holder, "TextSuperscriptAction", act );
7523 }
7525 /* Style - Subscript */
7526 {
7527 InkToggleAction* act = ink_toggle_action_new( "TextSubscriptAction", // Name
7528 _("Toggle Subscript"), // Label
7529 _("Toggle subscript"), // Tooltip
7530 "text_subscript", // Icon (inkId)
7531 secondarySize ); // Icon size
7532 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7533 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_text_script_changed), holder );
7534 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/text/sub", false) );
7535 g_object_set_data( holder, "TextSubscriptAction", act );
7536 }
7538 /* Alignment */
7539 {
7540 GtkListStore* model = gtk_list_store_new( 4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN );
7542 GtkTreeIter iter;
7544 gtk_list_store_append( model, &iter );
7545 gtk_list_store_set( model, &iter,
7546 0, _("Align left"),
7547 1, _("Align left"),
7548 2, GTK_STOCK_JUSTIFY_LEFT,
7549 3, true,
7550 -1 );
7552 gtk_list_store_append( model, &iter );
7553 gtk_list_store_set( model, &iter,
7554 0, _("Align center"),
7555 1, _("Align center"),
7556 2, GTK_STOCK_JUSTIFY_CENTER,
7557 3, true,
7558 -1 );
7560 gtk_list_store_append( model, &iter );
7561 gtk_list_store_set( model, &iter,
7562 0, _("Align right"),
7563 1, _("Align right"),
7564 2, GTK_STOCK_JUSTIFY_RIGHT,
7565 3, true,
7566 -1 );
7568 gtk_list_store_append( model, &iter );
7569 gtk_list_store_set( model, &iter,
7570 0, _("Justify"),
7571 1, _("Justify (only flowed text)"),
7572 2, GTK_STOCK_JUSTIFY_FILL,
7573 3, false,
7574 -1 );
7576 EgeSelectOneAction* act = ege_select_one_action_new( "TextAlignAction", // Name
7577 _("Alignment"), // Label
7578 _("Text alignment"), // Tooltip
7579 NULL, // StockID
7580 GTK_TREE_MODEL(model) ); // Model
7581 g_object_set( act, "short_label", "NotUsed", NULL );
7582 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
7583 g_object_set_data( holder, "TextAlignAction", act );
7585 ege_select_one_action_set_appearance( act, "full" );
7586 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
7587 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
7588 ege_select_one_action_set_icon_column( act, 2 );
7589 ege_select_one_action_set_icon_size( act, secondarySize );
7590 ege_select_one_action_set_tooltip_column( act, 1 );
7591 ege_select_one_action_set_sensitive_column( act, 3 );
7592 gint mode = prefs->getInt("/tools/text/align_mode", 0);
7593 ege_select_one_action_set_active( act, mode );
7594 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_text_align_mode_changed), holder );
7595 }
7597 /* Orientation (Left to Right, Top to Bottom */
7598 {
7599 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
7601 GtkTreeIter iter;
7603 gtk_list_store_append( model, &iter );
7604 gtk_list_store_set( model, &iter,
7605 0, _("Horizontal"),
7606 1, _("Horizontal text"),
7607 2, INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_HORIZONTAL,
7608 -1 );
7610 gtk_list_store_append( model, &iter );
7611 gtk_list_store_set( model, &iter,
7612 0, _("Vertical"),
7613 1, _("Vertical text"),
7614 2, INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_VERTICAL,
7615 -1 );
7617 EgeSelectOneAction* act = ege_select_one_action_new( "TextOrientationAction", // Name
7618 _("Orientation"), // Label
7619 _("Text orientation"), // Tooltip
7620 NULL, // StockID
7621 GTK_TREE_MODEL(model) ); // Model
7623 g_object_set( act, "short_label", "NotUsed", NULL );
7624 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
7625 g_object_set_data( holder, "TextOrientationAction", act );
7627 ege_select_one_action_set_appearance( act, "full" );
7628 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
7629 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
7630 ege_select_one_action_set_icon_column( act, 2 );
7631 ege_select_one_action_set_icon_size( act, secondarySize );
7632 ege_select_one_action_set_tooltip_column( act, 1 );
7634 gint mode = prefs->getInt("/tools/text/orientation", 0);
7635 ege_select_one_action_set_active( act, mode );
7636 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_text_orientation_mode_changed), holder );
7637 }
7639 /* Line height */
7640 {
7641 // Drop down menu
7642 gchar const* labels[] = {_("Smaller spacing"), 0, 0, 0, 0, _("Normal"), 0, 0, 0, 0, 0, _("Larger spacing")};
7643 gdouble values[] = { 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1,2, 1.3, 1.4, 1.5, 2.0};
7645 EgeAdjustmentAction *eact = create_adjustment_action(
7646 "TextLineHeightAction", /* name */
7647 _("Line Height"), /* label */
7648 _("Line:"), /* short label */
7649 _("Spacing between lines (times font size)"), /* tooltip */
7650 "/tools/text/lineheight", /* preferences path */
7651 0.0, /* default */
7652 GTK_WIDGET(desktop->canvas), /* focusTarget */
7653 NULL, /* unit selector */
7654 holder, /* dataKludge */
7655 FALSE, /* set alt-x keyboard shortcut? */
7656 NULL, /* altx_mark */
7657 0.0, 10.0, 0.01, 0.10, /* lower, upper, step (arrow up/down), page up/down */
7658 labels, values, G_N_ELEMENTS(labels), /* drop down menu */
7659 sp_text_lineheight_value_changed, /* callback */
7660 0.1, /* step (used?) */
7661 2, /* digits to show */
7662 1.0 /* factor (multiplies default) */
7663 );
7664 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7665 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
7666 g_object_set_data( holder, "TextLineHeightAction", eact );
7667 g_object_set( G_OBJECT(eact), "iconId", "text_line_spacing", NULL );
7668 }
7670 /* Word spacing */
7671 {
7672 // Drop down menu
7673 gchar const* labels[] = {_("Negative spacing"), 0, 0, 0, _("Normal"), 0, 0, 0, 0, 0, 0, 0, _("Positive spacing")};
7674 gdouble values[] = {-2.0, -1.5, -1.0, -0.5, 0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 4.0, 5.0};
7676 EgeAdjustmentAction *eact = create_adjustment_action(
7677 "TextWordSpacingAction", /* name */
7678 _("Word spacing"), /* label */
7679 _("Word:"), /* short label */
7680 _("Spacing between words (px)"), /* tooltip */
7681 "/tools/text/wordspacing", /* preferences path */
7682 0.0, /* default */
7683 GTK_WIDGET(desktop->canvas), /* focusTarget */
7684 NULL, /* unit selector */
7685 holder, /* dataKludge */
7686 FALSE, /* set alt-x keyboard shortcut? */
7687 NULL, /* altx_mark */
7688 -100.0, 100.0, 0.01, 0.10, /* lower, upper, step (arrow up/down), page up/down */
7689 labels, values, G_N_ELEMENTS(labels), /* drop down menu */
7690 sp_text_wordspacing_value_changed, /* callback */
7691 0.1, /* step (used?) */
7692 2, /* digits to show */
7693 1.0 /* factor (multiplies default) */
7694 );
7695 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7696 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
7697 g_object_set_data( holder, "TextWordSpacingAction", eact );
7698 g_object_set( G_OBJECT(eact), "iconId", "text_word_spacing", NULL );
7699 }
7701 /* Letter spacing */
7702 {
7703 // Drop down menu
7704 gchar const* labels[] = {_("Negative spacing"), 0, 0, 0, _("Normal"), 0, 0, 0, 0, 0, 0, 0, _("Positive spacing")};
7705 gdouble values[] = {-2.0, -1.5, -1.0, -0.5, 0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 4.0, 5.0};
7707 EgeAdjustmentAction *eact = create_adjustment_action(
7708 "TextLetterSpacingAction", /* name */
7709 _("Letter spacing"), /* label */
7710 _("Letter:"), /* short label */
7711 _("Spacing between letters (px)"), /* tooltip */
7712 "/tools/text/letterspacing", /* preferences path */
7713 0.0, /* default */
7714 GTK_WIDGET(desktop->canvas), /* focusTarget */
7715 NULL, /* unit selector */
7716 holder, /* dataKludge */
7717 FALSE, /* set alt-x keyboard shortcut? */
7718 NULL, /* altx_mark */
7719 -100.0, 100.0, 0.01, 0.10, /* lower, upper, step (arrow up/down), page up/down */
7720 labels, values, G_N_ELEMENTS(labels), /* drop down menu */
7721 sp_text_letterspacing_value_changed, /* callback */
7722 0.1, /* step (used?) */
7723 2, /* digits to show */
7724 1.0 /* factor (multiplies default) */
7725 );
7726 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7727 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
7728 g_object_set_data( holder, "TextLetterSpacingAction", eact );
7729 g_object_set( G_OBJECT(eact), "iconId", "text_letter_spacing", NULL );
7730 }
7732 /* Character kerning (horizontal shift) */
7733 {
7734 // Drop down menu
7735 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
7736 gdouble values[] = { -2.0, -1.5, -1.0, -0.5, 0, 0.5, 1.0, 1.5, 2.0, 2.5 };
7738 EgeAdjustmentAction *eact = create_adjustment_action(
7739 "TextDxAction", /* name */
7740 _("Kerning"), /* label */
7741 _("Kern:"), /* short label */
7742 _("Horizontal kerning (px)"), /* tooltip */
7743 "/tools/text/dx", /* preferences path */
7744 0.0, /* default */
7745 GTK_WIDGET(desktop->canvas), /* focusTarget */
7746 NULL, /* unit selector */
7747 holder, /* dataKludge */
7748 FALSE, /* set alt-x keyboard shortcut? */
7749 NULL, /* altx_mark */
7750 -100.0, 100.0, 0.01, 0.1, /* lower, upper, step (arrow up/down), page up/down */
7751 labels, values, G_N_ELEMENTS(labels), /* drop down menu */
7752 sp_text_dx_value_changed, /* callback */
7753 0.1, /* step (used?) */
7754 2, /* digits to show */
7755 1.0 /* factor (multiplies default) */
7756 );
7757 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7758 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
7759 g_object_set_data( holder, "TextDxAction", eact );
7760 g_object_set( G_OBJECT(eact), "iconId", "text_horz_kern", NULL );
7761 }
7763 /* Character vertical shift */
7764 {
7765 // Drop down menu
7766 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
7767 gdouble values[] = { -2.0, -1.5, -1.0, -0.5, 0, 0.5, 1.0, 1.5, 2.0, 2.5 };
7769 EgeAdjustmentAction *eact = create_adjustment_action(
7770 "TextDyAction", /* name */
7771 _("Vertical Shift"), /* label */
7772 _("Vert:"), /* short label */
7773 _("Vertical shift (px)"), /* tooltip */
7774 "/tools/text/dy", /* preferences path */
7775 0.0, /* default */
7776 GTK_WIDGET(desktop->canvas), /* focusTarget */
7777 NULL, /* unit selector */
7778 holder, /* dataKludge */
7779 FALSE, /* set alt-x keyboard shortcut? */
7780 NULL, /* altx_mark */
7781 -100.0, 100.0, 0.01, 0.1, /* lower, upper, step (arrow up/down), page up/down */
7782 labels, values, G_N_ELEMENTS(labels), /* drop down menu */
7783 sp_text_dy_value_changed, /* callback */
7784 0.1, /* step (used?) */
7785 2, /* digits to show */
7786 1.0 /* factor (multiplies default) */
7787 );
7788 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7789 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
7790 g_object_set_data( holder, "TextDyAction", eact );
7791 g_object_set( G_OBJECT(eact), "iconId", "text_vert_kern", NULL );
7792 }
7794 /* Character rotation */
7795 {
7796 // Drop down menu
7797 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
7798 gdouble values[] = { -90, -45, -30, -15, 0, 15, 30, 45, 90, 180 };
7800 EgeAdjustmentAction *eact = create_adjustment_action(
7801 "TextRotationAction", /* name */
7802 _("Letter rotation"), /* label */
7803 _("Rot:"), /* short label */
7804 _("Character rotation (degrees)"),/* tooltip */
7805 "/tools/text/rotation", /* preferences path */
7806 0.0, /* default */
7807 GTK_WIDGET(desktop->canvas), /* focusTarget */
7808 NULL, /* unit selector */
7809 holder, /* dataKludge */
7810 FALSE, /* set alt-x keyboard shortcut? */
7811 NULL, /* altx_mark */
7812 -180.0, 180.0, 0.1, 1.0, /* lower, upper, step (arrow up/down), page up/down */
7813 labels, values, G_N_ELEMENTS(labels), /* drop down menu */
7814 sp_text_rotation_value_changed, /* callback */
7815 0.1, /* step (used?) */
7816 2, /* digits to show */
7817 1.0 /* factor (multiplies default) */
7818 );
7819 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7820 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
7821 g_object_set_data( holder, "TextRotationAction", eact );
7822 g_object_set( G_OBJECT(eact), "iconId", "text_rotation", NULL );
7823 }
7825 // Is this necessary to call? Shouldn't hurt.
7826 sp_text_toolbox_selection_changed(sp_desktop_selection(desktop), holder);
7828 // Watch selection
7829 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolboxGTK");
7831 sigc::connection *c_selection_changed =
7832 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
7833 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)holder)));
7834 pool->add_connection ("selection-changed", c_selection_changed);
7836 sigc::connection *c_selection_modified =
7837 new sigc::connection (sp_desktop_selection (desktop)->connectModified
7838 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)holder)));
7839 pool->add_connection ("selection-modified", c_selection_modified);
7841 sigc::connection *c_subselection_changed =
7842 new sigc::connection (desktop->connectToolSubselectionChanged
7843 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)holder)));
7844 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
7846 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
7848 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
7850 }
7853 //#########################
7854 //## Connector ##
7855 //#########################
7857 static void sp_connector_mode_toggled( GtkToggleAction* act, GtkObject * /*tbl*/ )
7858 {
7859 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7860 prefs->setBool("/tools/connector/mode",
7861 gtk_toggle_action_get_active( act ));
7862 }
7864 static void sp_connector_path_set_avoid(void)
7865 {
7866 cc_selection_set_avoid(true);
7867 }
7870 static void sp_connector_path_set_ignore(void)
7871 {
7872 cc_selection_set_avoid(false);
7873 }
7875 static void sp_connector_orthogonal_toggled( GtkToggleAction* act, GObject *tbl )
7876 {
7877 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7878 Inkscape::Selection * selection = sp_desktop_selection(desktop);
7879 SPDocument *doc = sp_desktop_document(desktop);
7881 if (!DocumentUndo::getUndoSensitive(doc)) {
7882 return;
7883 }
7886 // quit if run by the _changed callbacks
7887 if (g_object_get_data( tbl, "freeze" )) {
7888 return;
7889 }
7891 // in turn, prevent callbacks from responding
7892 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7894 bool is_orthog = gtk_toggle_action_get_active( act );
7895 gchar orthog_str[] = "orthogonal";
7896 gchar polyline_str[] = "polyline";
7897 gchar *value = is_orthog ? orthog_str : polyline_str ;
7899 bool modmade = false;
7900 GSList *l = (GSList *) selection->itemList();
7901 while (l) {
7902 SPItem *item = (SPItem *) l->data;
7904 if (cc_item_is_connector(item)) {
7905 item->setAttribute( "inkscape:connector-type",
7906 value, false);
7907 item->avoidRef->handleSettingChange();
7908 modmade = true;
7909 }
7910 l = l->next;
7911 }
7913 if (!modmade) {
7914 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7915 prefs->setBool("/tools/connector/orthogonal", is_orthog);
7916 }
7918 DocumentUndo::done(doc, SP_VERB_CONTEXT_CONNECTOR,
7919 is_orthog ? _("Set connector type: orthogonal"): _("Set connector type: polyline"));
7921 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7922 }
7924 static void connector_curvature_changed(GtkAdjustment *adj, GObject* tbl)
7925 {
7926 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7927 Inkscape::Selection * selection = sp_desktop_selection(desktop);
7928 SPDocument *doc = sp_desktop_document(desktop);
7930 if (!DocumentUndo::getUndoSensitive(doc)) {
7931 return;
7932 }
7935 // quit if run by the _changed callbacks
7936 if (g_object_get_data( tbl, "freeze" )) {
7937 return;
7938 }
7940 // in turn, prevent callbacks from responding
7941 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7943 gdouble newValue = gtk_adjustment_get_value(adj);
7944 gchar value[G_ASCII_DTOSTR_BUF_SIZE];
7945 g_ascii_dtostr(value, G_ASCII_DTOSTR_BUF_SIZE, newValue);
7947 bool modmade = false;
7948 GSList *l = (GSList *) selection->itemList();
7949 while (l) {
7950 SPItem *item = (SPItem *) l->data;
7952 if (cc_item_is_connector(item)) {
7953 item->setAttribute( "inkscape:connector-curvature",
7954 value, false);
7955 item->avoidRef->handleSettingChange();
7956 modmade = true;
7957 }
7958 l = l->next;
7959 }
7961 if (!modmade) {
7962 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7963 prefs->setDouble(Glib::ustring("/tools/connector/curvature"), newValue);
7964 }
7966 DocumentUndo::done(doc, SP_VERB_CONTEXT_CONNECTOR,
7967 _("Change connector curvature"));
7969 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7970 }
7973 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
7974 {
7975 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7976 SPDocument *doc = sp_desktop_document(desktop);
7978 if (!DocumentUndo::getUndoSensitive(doc)) {
7979 return;
7980 }
7982 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7984 if ( !repr->attribute("inkscape:connector-spacing") &&
7985 ( adj->value == defaultConnSpacing )) {
7986 // Don't need to update the repr if the attribute doesn't
7987 // exist and it is being set to the default value -- as will
7988 // happen at startup.
7989 return;
7990 }
7992 // quit if run by the attr_changed listener
7993 if (g_object_get_data( tbl, "freeze" )) {
7994 return;
7995 }
7997 // in turn, prevent listener from responding
7998 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
8000 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
8001 SP_OBJECT(desktop->namedview)->updateRepr();
8003 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
8004 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
8005 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
8006 Geom::Matrix m = Geom::identity();
8007 avoid_item_move(&m, item);
8008 }
8010 if (items) {
8011 g_slist_free(items);
8012 }
8014 DocumentUndo::done(doc, SP_VERB_CONTEXT_CONNECTOR,
8015 _("Change connector spacing"));
8017 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
8018 }
8020 static void sp_connector_graph_layout(void)
8021 {
8022 if (!SP_ACTIVE_DESKTOP) {
8023 return;
8024 }
8025 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
8027 // hack for clones, see comment in align-and-distribute.cpp
8028 int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
8029 prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
8031 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
8033 prefs->setInt("/options/clonecompensation/value", saved_compensation);
8035 DocumentUndo::done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
8036 }
8038 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject * /*tbl*/ )
8039 {
8040 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
8041 prefs->setBool("/tools/connector/directedlayout",
8042 gtk_toggle_action_get_active( act ));
8043 }
8045 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject * /*tbl*/ )
8046 {
8047 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
8048 prefs->setBool("/tools/connector/avoidoverlaplayout",
8049 gtk_toggle_action_get_active( act ));
8050 }
8053 static void connector_length_changed(GtkAdjustment *adj, GObject* /*tbl*/)
8054 {
8055 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
8056 prefs->setDouble("/tools/connector/length", adj->value);
8057 }
8059 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
8060 gchar const *name, gchar const * /*old_value*/, gchar const * /*new_value*/,
8061 bool /*is_interactive*/, gpointer data)
8062 {
8063 GtkWidget *tbl = GTK_WIDGET(data);
8065 if ( !g_object_get_data(G_OBJECT(tbl), "freeze")
8066 && (strcmp(name, "inkscape:connector-spacing") == 0) ) {
8067 GtkAdjustment *adj = static_cast<GtkAdjustment*>(gtk_object_get_data(GTK_OBJECT(tbl), "spacing"));
8068 gdouble spacing = defaultConnSpacing;
8069 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
8071 gtk_adjustment_set_value(adj, spacing);
8072 gtk_adjustment_value_changed(adj);
8074 spinbutton_defocus(GTK_OBJECT(tbl));
8075 }
8076 }
8078 static void sp_connector_new_connection_point(GtkWidget *, GObject *tbl)
8079 {
8080 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
8081 SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
8083 if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE) {
8084 cc_create_connection_point(cc);
8085 }
8086 }
8088 static void sp_connector_remove_connection_point(GtkWidget *, GObject *tbl)
8089 {
8090 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
8091 SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
8093 if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE) {
8094 cc_remove_connection_point(cc);
8095 }
8096 }
8098 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
8099 NULL, /* child_added */
8100 NULL, /* child_removed */
8101 connector_tb_event_attr_changed,
8102 NULL, /* content_changed */
8103 NULL /* order_changed */
8104 };
8106 static void sp_connector_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
8107 {
8108 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "curvature" ) );
8109 GtkToggleAction *act = GTK_TOGGLE_ACTION( g_object_get_data( tbl, "orthogonal" ) );
8110 SPItem *item = selection->singleItem();
8111 if (SP_IS_PATH(item))
8112 {
8113 gdouble curvature = SP_PATH(item)->connEndPair.getCurvature();
8114 bool is_orthog = SP_PATH(item)->connEndPair.isOrthogonal();
8115 gtk_toggle_action_set_active(act, is_orthog);
8116 gtk_adjustment_set_value(adj, curvature);
8117 }
8119 }
8121 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
8122 {
8123 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
8124 Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
8126 // Editing mode toggle button
8127 {
8128 InkToggleAction* act = ink_toggle_action_new( "ConnectorEditModeAction",
8129 _("EditMode"),
8130 _("Switch between connection point editing and connector drawing mode"),
8131 INKSCAPE_ICON_CONNECTOR_EDIT,
8132 Inkscape::ICON_SIZE_DECORATION );
8133 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
8135 bool tbuttonstate = prefs->getBool("/tools/connector/mode");
8136 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
8137 g_object_set_data( holder, "mode", act );
8138 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_mode_toggled), holder );
8139 }
8142 {
8143 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
8144 _("Avoid"),
8145 _("Make connectors avoid selected objects"),
8146 INKSCAPE_ICON_CONNECTOR_AVOID,
8147 secondarySize );
8148 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
8149 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
8150 }
8152 {
8153 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
8154 _("Ignore"),
8155 _("Make connectors ignore selected objects"),
8156 INKSCAPE_ICON_CONNECTOR_IGNORE,
8157 secondarySize );
8158 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
8159 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
8160 }
8162 // Orthogonal connectors toggle button
8163 {
8164 InkToggleAction* act = ink_toggle_action_new( "ConnectorOrthogonalAction",
8165 _("Orthogonal"),
8166 _("Make connector orthogonal or polyline"),
8167 INKSCAPE_ICON_CONNECTOR_ORTHOGONAL,
8168 Inkscape::ICON_SIZE_DECORATION );
8169 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
8171 bool tbuttonstate = prefs->getBool("/tools/connector/orthogonal");
8172 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
8173 g_object_set_data( holder, "orthogonal", act );
8174 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_orthogonal_toggled), holder );
8175 }
8177 EgeAdjustmentAction* eact = 0;
8178 // Curvature spinbox
8179 eact = create_adjustment_action( "ConnectorCurvatureAction",
8180 _("Connector Curvature"), _("Curvature:"),
8181 _("The amount of connectors curvature"),
8182 "/tools/connector/curvature", defaultConnCurvature,
8183 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-curvature",
8184 0, 100, 1.0, 10.0,
8185 0, 0, 0,
8186 connector_curvature_changed, 1, 0 );
8187 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
8189 // Spacing spinbox
8190 eact = create_adjustment_action( "ConnectorSpacingAction",
8191 _("Connector Spacing"), _("Spacing:"),
8192 _("The amount of space left around objects by auto-routing connectors"),
8193 "/tools/connector/spacing", defaultConnSpacing,
8194 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
8195 0, 100, 1.0, 10.0,
8196 0, 0, 0,
8197 connector_spacing_changed, 1, 0 );
8198 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
8200 // Graph (connector network) layout
8201 {
8202 InkAction* inky = ink_action_new( "ConnectorGraphAction",
8203 _("Graph"),
8204 _("Nicely arrange selected connector network"),
8205 INKSCAPE_ICON_DISTRIBUTE_GRAPH,
8206 secondarySize );
8207 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
8208 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
8209 }
8211 // Default connector length spinbox
8212 eact = create_adjustment_action( "ConnectorLengthAction",
8213 _("Connector Length"), _("Length:"),
8214 _("Ideal length for connectors when layout is applied"),
8215 "/tools/connector/length", 100,
8216 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
8217 10, 1000, 10.0, 100.0,
8218 0, 0, 0,
8219 connector_length_changed, 1, 0 );
8220 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
8223 // Directed edges toggle button
8224 {
8225 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
8226 _("Downwards"),
8227 _("Make connectors with end-markers (arrows) point downwards"),
8228 INKSCAPE_ICON_DISTRIBUTE_GRAPH_DIRECTED,
8229 Inkscape::ICON_SIZE_DECORATION );
8230 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
8232 bool tbuttonstate = prefs->getBool("/tools/connector/directedlayout");
8233 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
8235 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
8236 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_connector_toolbox_selection_changed), (GObject *)holder));
8237 }
8239 // Avoid overlaps toggle button
8240 {
8241 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
8242 _("Remove overlaps"),
8243 _("Do not allow overlapping shapes"),
8244 INKSCAPE_ICON_DISTRIBUTE_REMOVE_OVERLAPS,
8245 Inkscape::ICON_SIZE_DECORATION );
8246 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
8248 bool tbuttonstate = prefs->getBool("/tools/connector/avoidoverlaplayout");
8249 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), (tbuttonstate ? TRUE : FALSE ));
8251 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
8252 }
8255 // New connection point button
8256 {
8257 InkAction* inky = ink_action_new( "ConnectorNewConnPointAction",
8258 _("New connection point"),
8259 _("Add a new connection point to the currently selected item"),
8260 INKSCAPE_ICON_CONNECTOR_NEW_CONNPOINT,
8261 secondarySize );
8262 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_new_connection_point), holder );
8263 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
8264 }
8266 // Remove selected connection point button
8268 {
8269 InkAction* inky = ink_action_new( "ConnectorRemoveConnPointAction",
8270 _("Remove connection point"),
8271 _("Remove the currently selected connection point"),
8272 INKSCAPE_ICON_CONNECTOR_REMOVE_CONNPOINT,
8273 secondarySize );
8274 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_remove_connection_point), holder );
8275 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
8276 }
8279 // Code to watch for changes to the connector-spacing attribute in
8280 // the XML.
8281 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
8282 g_assert(repr != NULL);
8284 purge_repr_listener( holder, holder );
8286 if (repr) {
8287 g_object_set_data( holder, "repr", repr );
8288 Inkscape::GC::anchor(repr);
8289 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
8290 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
8291 }
8292 } // end of sp_connector_toolbox_prep()
8295 //#########################
8296 //## Paintbucket ##
8297 //#########################
8299 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
8300 {
8301 gint channels = ege_select_one_action_get_active( act );
8302 flood_channels_set_channels( channels );
8303 }
8305 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject * /*tbl*/)
8306 {
8307 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
8308 prefs->setInt("/tools/paintbucket/threshold", (gint)adj->value);
8309 }
8311 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject * /*tbl*/)
8312 {
8313 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
8314 prefs->setBool("/tools/paintbucket/autogap", ege_select_one_action_get_active( act ));
8315 }
8317 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
8318 {
8319 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
8320 SPUnit const *unit = tracker->getActiveUnit();
8321 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
8323 prefs->setDouble("/tools/paintbucket/offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
8324 prefs->setString("/tools/paintbucket/offsetunits", sp_unit_get_abbreviation(unit));
8325 }
8327 static void paintbucket_defaults(GtkWidget *, GObject *tbl)
8328 {
8329 // FIXME: make defaults settable via Inkscape Options
8330 struct KeyValue {
8331 char const *key;
8332 double value;
8333 } const key_values[] = {
8334 {"threshold", 15},
8335 {"offset", 0.0}
8336 };
8338 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
8339 KeyValue const &kv = key_values[i];
8340 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
8341 if ( adj ) {
8342 gtk_adjustment_set_value(adj, kv.value);
8343 }
8344 }
8346 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
8347 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
8348 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
8349 ege_select_one_action_set_active( autogap_action, 0 );
8350 }
8352 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
8353 {
8354 EgeAdjustmentAction* eact = 0;
8355 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
8357 {
8358 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
8360 GList* items = 0;
8361 gint count = 0;
8362 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
8363 {
8364 GtkTreeIter iter;
8365 gtk_list_store_append( model, &iter );
8366 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
8367 count++;
8368 }
8369 g_list_free( items );
8370 items = 0;
8371 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
8372 g_object_set( act1, "short_label", _("Fill by:"), NULL );
8373 ege_select_one_action_set_appearance( act1, "compact" );
8374 ege_select_one_action_set_active( act1, prefs->getInt("/tools/paintbucket/channels", 0) );
8375 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
8376 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
8377 g_object_set_data( holder, "channels_action", act1 );
8378 }
8380 // Spacing spinbox
8381 {
8382 eact = create_adjustment_action(
8383 "ThresholdAction",
8384 _("Fill Threshold"), _("Threshold:"),
8385 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
8386 "/tools/paintbucket/threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
8387 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
8388 0, 0, 0,
8389 paintbucket_threshold_changed, 1, 0 );
8391 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
8392 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
8393 }
8395 // Create the units menu.
8396 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
8397 Glib::ustring stored_unit = prefs->getString("/tools/paintbucket/offsetunits");
8398 if (!stored_unit.empty()) {
8399 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit.data()));
8400 }
8401 g_object_set_data( holder, "tracker", tracker );
8402 {
8403 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
8404 gtk_action_group_add_action( mainActions, act );
8405 }
8407 // Offset spinbox
8408 {
8409 eact = create_adjustment_action(
8410 "OffsetAction",
8411 _("Grow/shrink by"), _("Grow/shrink by:"),
8412 _("The amount to grow (positive) or shrink (negative) the created fill path"),
8413 "/tools/paintbucket/offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
8414 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
8415 0, 0, 0,
8416 paintbucket_offset_changed, 1, 2);
8417 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
8419 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
8420 }
8422 /* Auto Gap */
8423 {
8424 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
8426 GList* items = 0;
8427 gint count = 0;
8428 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
8429 {
8430 GtkTreeIter iter;
8431 gtk_list_store_append( model, &iter );
8432 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
8433 count++;
8434 }
8435 g_list_free( items );
8436 items = 0;
8437 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
8438 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
8439 ege_select_one_action_set_appearance( act2, "compact" );
8440 ege_select_one_action_set_active( act2, prefs->getBool("/tools/paintbucket/autogap") );
8441 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
8442 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
8443 g_object_set_data( holder, "autogap_action", act2 );
8444 }
8446 /* Reset */
8447 {
8448 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
8449 _("Defaults"),
8450 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
8451 GTK_STOCK_CLEAR );
8452 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
8453 gtk_action_group_add_action( mainActions, act );
8454 gtk_action_set_sensitive( act, TRUE );
8455 }
8457 }
8459 /*
8460 Local Variables:
8461 mode:c++
8462 c-file-style:"stroustrup"
8463 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
8464 indent-tabs-mode:nil
8465 fill-column:99
8466 End:
8467 */
8468 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :