f4913c01930a8dbb3727a06c1ee8e6fdfd527995
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@users.sf.net>
13 * Jon A. Cruz <jon@joncruz.org>
14 * Maximilian Albert <maximilian.albert@gmail.com>
15 *
16 * Copyright (C) 2004 David Turner
17 * Copyright (C) 2003 MenTaLguY
18 * Copyright (C) 1999-2008 authors
19 * Copyright (C) 2001-2002 Ximian, Inc.
20 *
21 * Released under GNU GPL, read the file 'COPYING' for more information
22 */
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
28 #include <cstring>
29 #include <string>
31 #include <gtkmm.h>
32 #include <gtk/gtk.h>
33 #include <iostream>
34 #include <sstream>
35 #include <glibmm/i18n.h>
37 #include "../box3d-context.h"
38 #include "../box3d.h"
39 #include "../conn-avoid-ref.h"
40 #include "../connection-pool.h"
41 #include "../connector-context.h"
42 #include "../desktop.h"
43 #include "../desktop-handles.h"
44 #include "../desktop-style.h"
45 #include "../dialogs/dialog-events.h"
46 #include "../dialogs/text-edit.h"
47 #include "../document-private.h"
48 #include "../ege-adjustment-action.h"
49 #include "../ege-output-action.h"
50 #include "../ege-select-one-action.h"
51 #include "../flood-context.h"
52 #include "gradient-toolbar.h"
53 #include "../graphlayout/graphlayout.h"
54 #include "../helper/unit-menu.h"
55 #include "../helper/units.h"
56 #include "../helper/unit-tracker.h"
57 #include "icon.h"
58 #include "../ink-action.h"
59 #include "../inkscape.h"
60 #include "../interface.h"
61 #include "../libnrtype/font-instance.h"
62 #include "../libnrtype/font-lister.h"
63 #include "../live_effects/effect.h"
64 #include "../live_effects/lpe-angle_bisector.h"
65 #include "../live_effects/lpe-line_segment.h"
66 #include "../lpe-tool-context.h"
67 #include "../mod360.h"
68 #include "../node-context.h"
69 #include "../pen-context.h"
70 #include "../preferences.h"
71 #include "../selection-chemistry.h"
72 #include "../selection.h"
73 #include "select-toolbar.h"
74 #include "../shape-editor.h"
75 #include "../shortcuts.h"
76 #include "../sp-clippath.h"
77 #include "../sp-ellipse.h"
78 #include "../sp-flowtext.h"
79 #include "../sp-mask.h"
80 #include "../sp-namedview.h"
81 #include "../sp-rect.h"
82 #include "../sp-spiral.h"
83 #include "../sp-star.h"
84 #include "../sp-text.h"
85 #include "../style.h"
86 #include "../svg/css-ostringstream.h"
87 #include "../tools-switch.h"
88 #include "../tweak-context.h"
89 #include "../spray-context.h"
90 #include "../ui/dialog/calligraphic-profile-rename.h"
91 #include "../ui/icon-names.h"
92 #include "../ui/widget/style-swatch.h"
93 #include "../verbs.h"
94 #include "../widgets/button.h"
95 #include "../widgets/spinbutton-events.h"
96 #include "../widgets/spw-utilities.h"
97 #include "../widgets/widget-sizes.h"
98 #include "../xml/attribute-record.h"
99 #include "../xml/node-event-vector.h"
100 #include "../xml/repr.h"
102 #include "toolbox.h"
104 using Inkscape::UnitTracker;
106 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
107 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
109 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
110 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
111 static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
112 static void sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
113 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
114 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
115 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
116 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
117 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
118 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
119 static void sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
120 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
121 static void sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
122 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
123 static void sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
126 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
128 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
131 Inkscape::IconSize prefToSize( Glib::ustring const &path, int base ) {
132 static Inkscape::IconSize sizeChoices[] = {
133 Inkscape::ICON_SIZE_LARGE_TOOLBAR,
134 Inkscape::ICON_SIZE_SMALL_TOOLBAR,
135 Inkscape::ICON_SIZE_MENU
136 };
137 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
138 int index = prefs->getIntLimited( path, base, 0, G_N_ELEMENTS(sizeChoices) );
139 return sizeChoices[index];
140 }
142 static struct {
143 gchar const *type_name;
144 gchar const *data_name;
145 sp_verb_t verb;
146 sp_verb_t doubleclick_verb;
147 } const tools[] = {
148 { "SPSelectContext", "select_tool", SP_VERB_CONTEXT_SELECT, SP_VERB_CONTEXT_SELECT_PREFS},
149 { "SPNodeContext", "node_tool", SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
150 { "SPTweakContext", "tweak_tool", SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
151 { "SPSprayContext", "spray_tool", SP_VERB_CONTEXT_SPRAY, SP_VERB_CONTEXT_SPRAY_PREFS },
152 { "SPZoomContext", "zoom_tool", SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
153 { "SPRectContext", "rect_tool", SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
154 { "Box3DContext", "3dbox_tool", SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
155 { "SPArcContext", "arc_tool", SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
156 { "SPStarContext", "star_tool", SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
157 { "SPSpiralContext", "spiral_tool", SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
158 { "SPPencilContext", "pencil_tool", SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
159 { "SPPenContext", "pen_tool", SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
160 { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
161 { "SPLPEToolContext", "lpetool_tool", SP_VERB_CONTEXT_LPETOOL, SP_VERB_CONTEXT_LPETOOL_PREFS },
162 { "SPEraserContext", "eraser_tool", SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
163 { "SPFloodContext", "paintbucket_tool", SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
164 { "SPTextContext", "text_tool", SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
165 { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
166 { "SPGradientContext", "gradient_tool", SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
167 { "SPDropperContext", "dropper_tool", SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
168 { NULL, NULL, 0, 0 }
169 };
171 static struct {
172 gchar const *type_name;
173 gchar const *data_name;
174 GtkWidget *(*create_func)(SPDesktop *desktop);
175 void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
176 gchar const *ui_name;
177 gint swatch_verb_id;
178 gchar const *swatch_tool;
179 gchar const *swatch_tip;
180 } const aux_toolboxes[] = {
181 { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep, "SelectToolbar",
182 SP_VERB_INVALID, 0, 0},
183 { "SPNodeContext", "node_toolbox", 0, sp_node_toolbox_prep, "NodeToolbar",
184 SP_VERB_INVALID, 0, 0},
185 { "SPTweakContext", "tweak_toolbox", 0, sp_tweak_toolbox_prep, "TweakToolbar",
186 SP_VERB_CONTEXT_TWEAK_PREFS, "/tools/tweak", N_("Color/opacity used for color tweaking")},
187 { "SPSprayContext", "spray_toolbox", 0, sp_spray_toolbox_prep, "SprayToolbar",
188 SP_VERB_CONTEXT_SPRAY_PREFS, "/tools/spray", N_("Color/opacity used for color spraying")},
189 { "SPZoomContext", "zoom_toolbox", 0, sp_zoom_toolbox_prep, "ZoomToolbar",
190 SP_VERB_INVALID, 0, 0},
191 { "SPStarContext", "star_toolbox", 0, sp_star_toolbox_prep, "StarToolbar",
192 SP_VERB_CONTEXT_STAR_PREFS, "/tools/shapes/star", N_("Style of new stars")},
193 { "SPRectContext", "rect_toolbox", 0, sp_rect_toolbox_prep, "RectToolbar",
194 SP_VERB_CONTEXT_RECT_PREFS, "/tools/shapes/rect", N_("Style of new rectangles")},
195 { "Box3DContext", "3dbox_toolbox", 0, box3d_toolbox_prep, "3DBoxToolbar",
196 SP_VERB_CONTEXT_3DBOX_PREFS, "/tools/shapes/3dbox", N_("Style of new 3D boxes")},
197 { "SPArcContext", "arc_toolbox", 0, sp_arc_toolbox_prep, "ArcToolbar",
198 SP_VERB_CONTEXT_ARC_PREFS, "/tools/shapes/arc", N_("Style of new ellipses")},
199 { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep, "SpiralToolbar",
200 SP_VERB_CONTEXT_SPIRAL_PREFS, "/tools/shapes/spiral", N_("Style of new spirals")},
201 { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep, "PencilToolbar",
202 SP_VERB_CONTEXT_PENCIL_PREFS, "/tools/freehand/pencil", N_("Style of new paths created by Pencil")},
203 { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep, "PenToolbar",
204 SP_VERB_CONTEXT_PEN_PREFS, "/tools/freehand/pen", N_("Style of new paths created by Pen")},
205 { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
206 SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "/tools/calligraphic", N_("Style of new calligraphic strokes")},
207 { "SPEraserContext", "eraser_toolbox", 0, sp_eraser_toolbox_prep,"EraserToolbar",
208 SP_VERB_CONTEXT_ERASER_PREFS, "/tools/eraser", _("TBD")},
209 { "SPLPEToolContext", "lpetool_toolbox", 0, sp_lpetool_toolbox_prep, "LPEToolToolbar",
210 SP_VERB_CONTEXT_LPETOOL_PREFS, "/tools/lpetool", _("TBD")},
211 { "SPTextContext", "text_toolbox", sp_text_toolbox_new, 0, 0,
212 SP_VERB_INVALID, 0, 0},
213 { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep, "DropperToolbar",
214 SP_VERB_INVALID, 0, 0},
215 { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0, 0,
216 SP_VERB_INVALID, 0, 0},
217 { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep, "ConnectorToolbar",
218 SP_VERB_INVALID, 0, 0},
219 { "SPFloodContext", "paintbucket_toolbox", 0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
220 SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "/tools/paintbucket", N_("Style of Paint Bucket fill objects")},
221 { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
222 };
224 #define TOOLBAR_SLIDER_HINT "full"
226 static gchar const * ui_descr =
227 "<ui>"
228 " <toolbar name='SelectToolbar'>"
229 " <toolitem action='EditSelectAll' />"
230 " <toolitem action='EditSelectAllInAllLayers' />"
231 " <toolitem action='EditDeselect' />"
232 " <separator />"
233 " <toolitem action='ObjectRotate90CCW' />"
234 " <toolitem action='ObjectRotate90' />"
235 " <toolitem action='ObjectFlipHorizontally' />"
236 " <toolitem action='ObjectFlipVertically' />"
237 " <separator />"
238 " <toolitem action='SelectionToBack' />"
239 " <toolitem action='SelectionLower' />"
240 " <toolitem action='SelectionRaise' />"
241 " <toolitem action='SelectionToFront' />"
242 " <separator />"
243 " <toolitem action='XAction' />"
244 " <toolitem action='YAction' />"
245 " <toolitem action='WidthAction' />"
246 " <toolitem action='LockAction' />"
247 " <toolitem action='HeightAction' />"
248 " <toolitem action='UnitsAction' />"
249 " <separator />"
250 " <toolitem action='transform_affect_label' />"
251 " <toolitem action='transform_stroke' />"
252 " <toolitem action='transform_corners' />"
253 " <toolitem action='transform_gradient' />"
254 " <toolitem action='transform_pattern' />"
255 " </toolbar>"
257 " <toolbar name='NodeToolbar'>"
258 " <toolitem action='NodeInsertAction' />"
259 " <toolitem action='NodeDeleteAction' />"
260 " <separator />"
261 " <toolitem action='NodeJoinAction' />"
262 " <toolitem action='NodeBreakAction' />"
263 " <separator />"
264 " <toolitem action='NodeJoinSegmentAction' />"
265 " <toolitem action='NodeDeleteSegmentAction' />"
266 " <separator />"
267 " <toolitem action='NodeCuspAction' />"
268 " <toolitem action='NodeSmoothAction' />"
269 " <toolitem action='NodeSymmetricAction' />"
270 " <toolitem action='NodeAutoAction' />"
271 " <separator />"
272 " <toolitem action='NodeLineAction' />"
273 " <toolitem action='NodeCurveAction' />"
274 " <separator />"
275 " <toolitem action='ObjectToPath' />"
276 " <toolitem action='StrokeToPath' />"
277 " <separator />"
278 " <toolitem action='NodeXAction' />"
279 " <toolitem action='NodeYAction' />"
280 " <toolitem action='NodeUnitsAction' />"
281 " <separator />"
282 " <toolitem action='ObjectEditClipPathAction' />"
283 " <toolitem action='ObjectEditMaskPathAction' />"
284 " <toolitem action='EditNextLPEParameterAction' />"
285 " <separator />"
286 " <toolitem action='NodesShowHandlesAction' />"
287 " <toolitem action='NodesShowHelperpath' />"
288 " </toolbar>"
290 " <toolbar name='TweakToolbar'>"
291 " <toolitem action='TweakWidthAction' />"
292 " <separator />"
293 " <toolitem action='TweakForceAction' />"
294 " <toolitem action='TweakPressureAction' />"
295 " <separator />"
296 " <toolitem action='TweakModeAction' />"
297 " <separator />"
298 " <toolitem action='TweakFidelityAction' />"
299 " <separator />"
300 " <toolitem action='TweakChannelsLabel' />"
301 " <toolitem action='TweakDoH' />"
302 " <toolitem action='TweakDoS' />"
303 " <toolitem action='TweakDoL' />"
304 " <toolitem action='TweakDoO' />"
305 " </toolbar>"
307 " <toolbar name='SprayToolbar'>"
308 " <toolitem action='SprayModeAction' />"
309 " <separator />"
310 " <separator />"
311 " <toolitem action='SprayWidthAction' />"
312 " <toolitem action='SprayPressureAction' />"
313 " <toolitem action='SprayPopulationAction' />"
314 " <separator />"
315 " <toolitem action='SprayRotationAction' />"
316 " <toolitem action='SprayScaleAction' />"
317 " <separator />"
318 " <toolitem action='SprayStandard_deviationAction' />"
319 " <toolitem action='SprayMeanAction' />"
320 " </toolbar>"
322 " <toolbar name='ZoomToolbar'>"
323 " <toolitem action='ZoomIn' />"
324 " <toolitem action='ZoomOut' />"
325 " <separator />"
326 " <toolitem action='Zoom1:0' />"
327 " <toolitem action='Zoom1:2' />"
328 " <toolitem action='Zoom2:1' />"
329 " <separator />"
330 " <toolitem action='ZoomSelection' />"
331 " <toolitem action='ZoomDrawing' />"
332 " <toolitem action='ZoomPage' />"
333 " <toolitem action='ZoomPageWidth' />"
334 " <separator />"
335 " <toolitem action='ZoomPrev' />"
336 " <toolitem action='ZoomNext' />"
337 " </toolbar>"
339 " <toolbar name='StarToolbar'>"
340 " <separator />"
341 " <toolitem action='StarStateAction' />"
342 " <separator />"
343 " <toolitem action='FlatAction' />"
344 " <separator />"
345 " <toolitem action='MagnitudeAction' />"
346 " <toolitem action='SpokeAction' />"
347 " <toolitem action='RoundednessAction' />"
348 " <toolitem action='RandomizationAction' />"
349 " <separator />"
350 " <toolitem action='StarResetAction' />"
351 " </toolbar>"
353 " <toolbar name='RectToolbar'>"
354 " <toolitem action='RectStateAction' />"
355 " <toolitem action='RectWidthAction' />"
356 " <toolitem action='RectHeightAction' />"
357 " <toolitem action='RadiusXAction' />"
358 " <toolitem action='RadiusYAction' />"
359 " <toolitem action='RectUnitsAction' />"
360 " <separator />"
361 " <toolitem action='RectResetAction' />"
362 " </toolbar>"
364 " <toolbar name='3DBoxToolbar'>"
365 " <toolitem action='3DBoxAngleXAction' />"
366 " <toolitem action='3DBoxVPXStateAction' />"
367 " <separator />"
368 " <toolitem action='3DBoxAngleYAction' />"
369 " <toolitem action='3DBoxVPYStateAction' />"
370 " <separator />"
371 " <toolitem action='3DBoxAngleZAction' />"
372 " <toolitem action='3DBoxVPZStateAction' />"
373 " </toolbar>"
375 " <toolbar name='SpiralToolbar'>"
376 " <toolitem action='SpiralStateAction' />"
377 " <toolitem action='SpiralRevolutionAction' />"
378 " <toolitem action='SpiralExpansionAction' />"
379 " <toolitem action='SpiralT0Action' />"
380 " <separator />"
381 " <toolitem action='SpiralResetAction' />"
382 " </toolbar>"
384 " <toolbar name='PenToolbar'>"
385 " <toolitem action='FreehandModeActionPen' />"
386 " <separator />"
387 " <toolitem action='SetPenShapeAction'/>"
388 " </toolbar>"
390 " <toolbar name='PencilToolbar'>"
391 " <toolitem action='FreehandModeActionPencil' />"
392 " <separator />"
393 " <toolitem action='PencilToleranceAction' />"
394 " <separator />"
395 " <toolitem action='PencilResetAction' />"
396 " <separator />"
397 " <toolitem action='SetPencilShapeAction'/>"
398 " </toolbar>"
400 " <toolbar name='CalligraphyToolbar'>"
401 " <separator />"
402 " <toolitem action='SetProfileAction'/>"
403 " <separator />"
404 " <toolitem action='CalligraphyWidthAction' />"
405 " <toolitem action='PressureAction' />"
406 " <toolitem action='TraceAction' />"
407 " <toolitem action='ThinningAction' />"
408 " <separator />"
409 " <toolitem action='AngleAction' />"
410 " <toolitem action='TiltAction' />"
411 " <toolitem action='FixationAction' />"
412 " <separator />"
413 " <toolitem action='CapRoundingAction' />"
414 " <separator />"
415 " <toolitem action='TremorAction' />"
416 " <toolitem action='WiggleAction' />"
417 " <toolitem action='MassAction' />"
418 " <separator />"
419 " </toolbar>"
421 " <toolbar name='ArcToolbar'>"
422 " <toolitem action='ArcStateAction' />"
423 " <separator />"
424 " <toolitem action='ArcStartAction' />"
425 " <toolitem action='ArcEndAction' />"
426 " <separator />"
427 " <toolitem action='ArcOpenAction' />"
428 " <separator />"
429 " <toolitem action='ArcResetAction' />"
430 " <separator />"
431 " </toolbar>"
433 " <toolbar name='PaintbucketToolbar'>"
434 " <toolitem action='ChannelsAction' />"
435 " <separator />"
436 " <toolitem action='ThresholdAction' />"
437 " <separator />"
438 " <toolitem action='OffsetAction' />"
439 " <toolitem action='PaintbucketUnitsAction' />"
440 " <separator />"
441 " <toolitem action='AutoGapAction' />"
442 " <separator />"
443 " <toolitem action='PaintbucketResetAction' />"
444 " </toolbar>"
446 " <toolbar name='EraserToolbar'>"
447 " <toolitem action='EraserWidthAction' />"
448 " <separator />"
449 " <toolitem action='EraserModeAction' />"
450 " </toolbar>"
452 " <toolbar name='LPEToolToolbar'>"
453 " <toolitem action='LPEToolModeAction' />"
454 " <separator />"
455 " <toolitem action='LPEShowBBoxAction' />"
456 " <toolitem action='LPEBBoxFromSelectionAction' />"
457 " <separator />"
458 " <toolitem action='LPELineSegmentAction' />"
459 " <separator />"
460 " <toolitem action='LPEMeasuringAction' />"
461 " <toolitem action='LPEToolUnitsAction' />"
462 " <separator />"
463 " <toolitem action='LPEOpenLPEDialogAction' />"
464 " </toolbar>"
466 " <toolbar name='DropperToolbar'>"
467 " <toolitem action='DropperOpacityAction' />"
468 " <toolitem action='DropperPickAlphaAction' />"
469 " <toolitem action='DropperSetAlphaAction' />"
470 " </toolbar>"
472 " <toolbar name='ConnectorToolbar'>"
473 " <toolitem action='ConnectorEditModeAction' />"
474 " <toolitem action='ConnectorAvoidAction' />"
475 " <toolitem action='ConnectorIgnoreAction' />"
476 " <toolitem action='ConnectorOrthogonalAction' />"
477 " <toolitem action='ConnectorCurvatureAction' />"
478 " <toolitem action='ConnectorSpacingAction' />"
479 " <toolitem action='ConnectorGraphAction' />"
480 " <toolitem action='ConnectorLengthAction' />"
481 " <toolitem action='ConnectorDirectedAction' />"
482 " <toolitem action='ConnectorOverlapAction' />"
483 " <toolitem action='ConnectorNewConnPointAction' />"
484 " <toolitem action='ConnectorRemoveConnPointAction' />"
485 " </toolbar>"
487 "</ui>"
488 ;
490 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
492 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
494 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
495 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
497 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
498 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
500 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
501 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
503 GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick( GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
504 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
505 Inkscape::UI::View::View *view, GtkTooltips *tt);
507 class VerbAction : public Gtk::Action {
508 public:
509 static Glib::RefPtr<VerbAction> create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
511 virtual ~VerbAction();
512 virtual void set_active(bool active = true);
514 protected:
515 virtual Gtk::Widget* create_menu_item_vfunc();
516 virtual Gtk::Widget* create_tool_item_vfunc();
518 virtual void connect_proxy_vfunc(Gtk::Widget* proxy);
519 virtual void disconnect_proxy_vfunc(Gtk::Widget* proxy);
521 virtual void on_activate();
523 private:
524 Inkscape::Verb* verb;
525 Inkscape::Verb* verb2;
526 Inkscape::UI::View::View *view;
527 GtkTooltips *tooltips;
528 bool active;
530 VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
531 };
534 Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips)
535 {
536 Glib::RefPtr<VerbAction> result;
537 SPAction *action = verb->get_action(view);
538 if ( action ) {
539 //SPAction* action2 = verb2 ? verb2->get_action(view) : 0;
540 result = Glib::RefPtr<VerbAction>(new VerbAction(verb, verb2, view, tooltips));
541 }
543 return result;
544 }
546 VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips) :
547 Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(verb->get_image()), Glib::ustring(_(verb->get_name())), Glib::ustring(_(verb->get_tip()))),
548 verb(verb),
549 verb2(verb2),
550 view(view),
551 tooltips(tooltips),
552 active(false)
553 {
554 }
556 VerbAction::~VerbAction()
557 {
558 }
560 Gtk::Widget* VerbAction::create_menu_item_vfunc()
561 {
562 // First call in to get the icon rendered if present in SVG
563 Gtk::Widget *widget = sp_icon_get_icon( property_stock_id().get_value().get_string(), Inkscape::ICON_SIZE_MENU );
564 delete widget;
565 widget = 0;
567 Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
568 // g_message("create_menu_item_vfunc() = %p for '%s'", widg, verb->get_id());
569 return widg;
570 }
572 Gtk::Widget* VerbAction::create_tool_item_vfunc()
573 {
574 // Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
575 Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
576 GtkWidget* toolbox = 0;
577 GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
578 SP_BUTTON_TYPE_TOGGLE,
579 verb,
580 verb2,
581 view,
582 tooltips );
583 if ( active ) {
584 sp_button_toggle_set_down( SP_BUTTON(button), active);
585 }
586 gtk_widget_show_all( button );
587 Gtk::Widget* wrapped = Glib::wrap(button);
588 Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
589 holder->add(*wrapped);
591 // g_message("create_tool_item_vfunc() = %p for '%s'", holder, verb->get_id());
592 return holder;
593 }
595 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
596 {
597 // g_message("connect_proxy_vfunc(%p) for '%s'", proxy, verb->get_id());
598 Gtk::Action::connect_proxy_vfunc(proxy);
599 }
601 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
602 {
603 // g_message("disconnect_proxy_vfunc(%p) for '%s'", proxy, verb->get_id());
604 Gtk::Action::disconnect_proxy_vfunc(proxy);
605 }
607 void VerbAction::set_active(bool active)
608 {
609 this->active = active;
610 Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
611 for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
612 Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
613 if (ti) {
614 // *should* have one child that is the SPButton
615 Gtk::Widget* child = ti->get_child();
616 if ( child && SP_IS_BUTTON(child->gobj()) ) {
617 SPButton* button = SP_BUTTON(child->gobj());
618 sp_button_toggle_set_down( button, active );
619 }
620 }
621 }
622 }
624 void VerbAction::on_activate()
625 {
626 if ( verb ) {
627 SPAction *action = verb->get_action(view);
628 if ( action ) {
629 sp_action_perform(action, 0);
630 }
631 }
632 }
634 /* Global text entry widgets necessary for update */
635 /* GtkWidget *dropper_rgb_entry,
636 *dropper_opacity_entry ; */
637 // should be made a private member once this is converted to class
639 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
640 connection->disconnect();
641 delete connection;
642 }
644 static void purge_repr_listener( GObject* obj, GObject* tbl )
645 {
646 (void)obj;
647 Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
648 if (oldrepr) { // remove old listener
649 sp_repr_remove_listener_by_data(oldrepr, tbl);
650 Inkscape::GC::release(oldrepr);
651 oldrepr = 0;
652 g_object_set_data( tbl, "repr", NULL );
653 }
654 }
656 GtkWidget *
657 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
658 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
659 Inkscape::UI::View::View *view, GtkTooltips *tt)
660 {
661 SPAction *action = verb->get_action(view);
662 if (!action) return NULL;
664 SPAction *doubleclick_action;
665 if (doubleclick_verb)
666 doubleclick_action = doubleclick_verb->get_action(view);
667 else
668 doubleclick_action = NULL;
670 /* fixme: Handle sensitive/unsensitive */
671 /* fixme: Implement sp_button_new_from_action */
672 GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
673 gtk_widget_show(b);
676 unsigned int shortcut = sp_shortcut_get_primary(verb);
677 if (shortcut) {
678 gchar key[256];
679 sp_ui_shortcut_string(shortcut, key);
680 gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
681 if ( t ) {
682 gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
683 }
684 g_free(tip);
685 } else {
686 if ( t ) {
687 gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
688 }
689 }
691 return b;
692 }
695 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
696 {
697 SPAction* targetAction = SP_ACTION(user_data);
698 if ( targetAction ) {
699 sp_action_perform( targetAction, NULL );
700 }
701 }
703 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
704 {
705 if ( data ) {
706 GtkAction* act = GTK_ACTION(data);
707 gtk_action_set_sensitive( act, sensitive );
708 }
709 }
711 static SPActionEventVector action_event_vector = {
712 {NULL},
713 NULL,
714 NULL,
715 sp_action_action_set_sensitive,
716 NULL,
717 NULL
718 };
720 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
721 {
722 GtkAction* act = 0;
724 SPAction* targetAction = verb->get_action(view);
725 InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size );
726 act = GTK_ACTION(inky);
727 gtk_action_set_sensitive( act, targetAction->sensitive );
729 g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
731 SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
732 nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
734 return act;
735 }
737 Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
738 {
739 Inkscape::UI::View::View *view = desktop;
740 gint verbsToUse[] = {
741 // disabled until we have icons for them:
742 //find
743 //SP_VERB_EDIT_TILE,
744 //SP_VERB_EDIT_UNTILE,
745 SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
746 SP_VERB_DIALOG_DISPLAY,
747 SP_VERB_DIALOG_FILL_STROKE,
748 SP_VERB_DIALOG_NAMEDVIEW,
749 SP_VERB_DIALOG_TEXT,
750 SP_VERB_DIALOG_XML_EDITOR,
751 SP_VERB_DIALOG_LAYERS,
752 SP_VERB_EDIT_CLONE,
753 SP_VERB_EDIT_COPY,
754 SP_VERB_EDIT_CUT,
755 SP_VERB_EDIT_DUPLICATE,
756 SP_VERB_EDIT_PASTE,
757 SP_VERB_EDIT_REDO,
758 SP_VERB_EDIT_UNDO,
759 SP_VERB_EDIT_UNLINK_CLONE,
760 SP_VERB_FILE_EXPORT,
761 SP_VERB_FILE_IMPORT,
762 SP_VERB_FILE_NEW,
763 SP_VERB_FILE_OPEN,
764 SP_VERB_FILE_PRINT,
765 SP_VERB_FILE_SAVE,
766 SP_VERB_OBJECT_TO_CURVE,
767 SP_VERB_SELECTION_GROUP,
768 SP_VERB_SELECTION_OUTLINE,
769 SP_VERB_SELECTION_UNGROUP,
770 SP_VERB_ZOOM_1_1,
771 SP_VERB_ZOOM_1_2,
772 SP_VERB_ZOOM_2_1,
773 SP_VERB_ZOOM_DRAWING,
774 SP_VERB_ZOOM_IN,
775 SP_VERB_ZOOM_NEXT,
776 SP_VERB_ZOOM_OUT,
777 SP_VERB_ZOOM_PAGE,
778 SP_VERB_ZOOM_PAGE_WIDTH,
779 SP_VERB_ZOOM_PREV,
780 SP_VERB_ZOOM_SELECTION,
781 };
783 Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
785 static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
786 Glib::RefPtr<Gtk::ActionGroup> mainActions;
787 if ( groups.find(desktop) != groups.end() ) {
788 mainActions = groups[desktop];
789 }
791 if ( !mainActions ) {
792 mainActions = Gtk::ActionGroup::create("main");
793 groups[desktop] = mainActions;
794 }
796 for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
797 Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
798 if ( verb ) {
799 if (!mainActions->get_action(verb->get_id())) {
800 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
801 mainActions->add(Glib::wrap(act));
802 }
803 }
804 }
806 if ( !mainActions->get_action("ToolZoom") ) {
807 GtkTooltips *tt = gtk_tooltips_new();
808 for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
809 Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
810 if ( va ) {
811 mainActions->add(va);
812 if ( i == 0 ) {
813 va->set_active(true);
814 }
815 }
816 }
817 }
820 return mainActions;
821 }
824 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
825 {
826 gtk_widget_set_size_request( widget,
827 widget->allocation.width,
828 widget->allocation.height );
829 }
831 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
832 {
833 gtk_widget_set_size_request( widget, -1, -1 );
834 }
838 GtkWidget *
839 sp_tool_toolbox_new()
840 {
841 GtkTooltips *tt = gtk_tooltips_new();
842 GtkWidget* tb = gtk_toolbar_new();
843 gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
844 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
846 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
847 g_object_set_data(G_OBJECT(tb), "tooltips", tt);
849 gtk_widget_set_sensitive(tb, FALSE);
851 GtkWidget *hb = gtk_handle_box_new();
852 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
853 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
854 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
856 gtk_container_add(GTK_CONTAINER(hb), tb);
857 gtk_widget_show(GTK_WIDGET(tb));
859 sigc::connection* conn = new sigc::connection;
860 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
862 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
863 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
865 return hb;
866 }
868 GtkWidget *
869 sp_aux_toolbox_new()
870 {
871 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
873 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
875 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
877 gtk_widget_set_sensitive(tb, FALSE);
879 GtkWidget *hb = gtk_handle_box_new();
880 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
881 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
882 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
884 gtk_container_add(GTK_CONTAINER(hb), tb);
885 gtk_widget_show(GTK_WIDGET(tb));
887 sigc::connection* conn = new sigc::connection;
888 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
890 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
891 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
893 return hb;
894 }
896 //####################################
897 //# Commands Bar
898 //####################################
900 GtkWidget *
901 sp_commands_toolbox_new()
902 {
903 GtkWidget *tb = gtk_toolbar_new();
905 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
906 gtk_widget_set_sensitive(tb, FALSE);
908 GtkWidget *hb = gtk_handle_box_new();
909 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
910 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
911 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
913 gtk_container_add(GTK_CONTAINER(hb), tb);
914 gtk_widget_show(GTK_WIDGET(tb));
916 sigc::connection* conn = new sigc::connection;
917 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
919 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
920 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
922 return hb;
923 }
925 GtkWidget *
926 sp_snap_toolbox_new()
927 {
928 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
929 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
930 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
932 //GtkWidget *tb = gtk_toolbar_new();
933 //g_object_set_data(G_OBJECT(tb), "desktop", NULL);
935 gtk_widget_set_sensitive(tb, FALSE);
937 GtkWidget *hb = gtk_handle_box_new();
938 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
939 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
940 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
942 gtk_container_add(GTK_CONTAINER(hb), tb);
943 gtk_widget_show(GTK_WIDGET(tb));
945 sigc::connection* conn = new sigc::connection;
946 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
948 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
949 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
951 return hb;
952 }
954 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
955 gchar const *label, gchar const *shortLabel, gchar const *tooltip,
956 Glib::ustring const &path, gdouble def,
957 GtkWidget *focusTarget,
958 GtkWidget *us,
959 GObject *dataKludge,
960 gboolean altx, gchar const *altx_mark,
961 gdouble lower, gdouble upper, gdouble step, gdouble page,
962 gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
963 void (*callback)(GtkAdjustment *, GObject *),
964 gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
965 {
966 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
967 GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs->getDouble(path, def) * factor,
968 lower, upper, step, page, 0 ) );
969 if (us) {
970 sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
971 }
973 gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
975 EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
976 if ( shortLabel ) {
977 g_object_set( act, "short_label", shortLabel, NULL );
978 }
980 if ( (descrCount > 0) && descrLabels && descrValues ) {
981 ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
982 }
984 if ( focusTarget ) {
985 ege_adjustment_action_set_focuswidget( act, focusTarget );
986 }
988 if ( altx && altx_mark ) {
989 g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
990 }
992 if ( dataKludge ) {
993 // Rather lame, but it's the only place where we need to get the entry name
994 // but we don't have an Entry
995 g_object_set_data( dataKludge, prefs->getEntry(path).getEntryName().data(), adj );
996 }
998 // Using a cast just to make sure we pass in the right kind of function pointer
999 g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
1001 return act;
1002 }
1005 //####################################
1006 //# node editing callbacks
1007 //####################################
1009 /**
1010 * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
1011 */
1012 static ShapeEditor *get_current_shape_editor()
1013 {
1014 if (!SP_ACTIVE_DESKTOP) {
1015 return NULL;
1016 }
1018 SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
1020 if (!SP_IS_NODE_CONTEXT(event_context)) {
1021 return NULL;
1022 }
1024 return event_context->shape_editor;
1025 }
1028 void
1029 sp_node_path_edit_add(void)
1030 {
1031 ShapeEditor *shape_editor = get_current_shape_editor();
1032 if (shape_editor) shape_editor->add_node();
1033 }
1035 void
1036 sp_node_path_edit_delete(void)
1037 {
1038 ShapeEditor *shape_editor = get_current_shape_editor();
1039 if (shape_editor) shape_editor->delete_nodes_preserving_shape();
1040 }
1042 void
1043 sp_node_path_edit_delete_segment(void)
1044 {
1045 ShapeEditor *shape_editor = get_current_shape_editor();
1046 if (shape_editor) shape_editor->delete_segment();
1047 }
1049 void
1050 sp_node_path_edit_break(void)
1051 {
1052 ShapeEditor *shape_editor = get_current_shape_editor();
1053 if (shape_editor) shape_editor->break_at_nodes();
1054 }
1056 void
1057 sp_node_path_edit_join(void)
1058 {
1059 ShapeEditor *shape_editor = get_current_shape_editor();
1060 if (shape_editor) shape_editor->join_nodes();
1061 }
1063 void
1064 sp_node_path_edit_join_segment(void)
1065 {
1066 ShapeEditor *shape_editor = get_current_shape_editor();
1067 if (shape_editor) shape_editor->join_segments();
1068 }
1070 void
1071 sp_node_path_edit_toline(void)
1072 {
1073 ShapeEditor *shape_editor = get_current_shape_editor();
1074 if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1075 }
1077 void
1078 sp_node_path_edit_tocurve(void)
1079 {
1080 ShapeEditor *shape_editor = get_current_shape_editor();
1081 if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1082 }
1084 void
1085 sp_node_path_edit_cusp(void)
1086 {
1087 ShapeEditor *shape_editor = get_current_shape_editor();
1088 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1089 }
1091 void
1092 sp_node_path_edit_smooth(void)
1093 {
1094 ShapeEditor *shape_editor = get_current_shape_editor();
1095 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1096 }
1098 void
1099 sp_node_path_edit_symmetrical(void)
1100 {
1101 ShapeEditor *shape_editor = get_current_shape_editor();
1102 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1103 }
1105 void
1106 sp_node_path_edit_auto(void)
1107 {
1108 ShapeEditor *shape_editor = get_current_shape_editor();
1109 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_AUTO);
1110 }
1112 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1113 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1114 bool show = gtk_toggle_action_get_active( act );
1115 prefs->setBool("/tools/nodes/show_handles", show);
1116 ShapeEditor *shape_editor = get_current_shape_editor();
1117 if (shape_editor) shape_editor->show_handles(show);
1118 }
1120 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1121 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1122 bool show = gtk_toggle_action_get_active( act );
1123 prefs->setBool("/tools/nodes/show_helperpath", show);
1124 ShapeEditor *shape_editor = get_current_shape_editor();
1125 if (shape_editor) shape_editor->show_helperpath(show);
1126 }
1128 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1129 sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1130 }
1132 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1133 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1134 }
1136 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1137 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1138 }
1140 /* is called when the node selection is modified */
1141 static void
1142 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1143 {
1144 GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1145 GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1146 GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1147 GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1149 // quit if run by the attr_changed listener
1150 if (g_object_get_data( tbl, "freeze" )) {
1151 return;
1152 }
1154 // in turn, prevent listener from responding
1155 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1157 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1158 SPUnit const *unit = tracker->getActiveUnit();
1160 ShapeEditor *shape_editor = get_current_shape_editor();
1161 if (shape_editor && shape_editor->has_nodepath()) {
1162 Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1163 int n_selected = 0;
1164 if (nodepath) {
1165 n_selected = nodepath->numSelected();
1166 }
1168 if (n_selected == 0) {
1169 gtk_action_set_sensitive(xact, FALSE);
1170 gtk_action_set_sensitive(yact, FALSE);
1171 } else {
1172 gtk_action_set_sensitive(xact, TRUE);
1173 gtk_action_set_sensitive(yact, TRUE);
1174 Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1175 Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1177 if (n_selected == 1) {
1178 Geom::Point sel_node = nodepath->singleSelectedCoords();
1179 if (oldx != sel_node[Geom::X] || oldy != sel_node[Geom::Y]) {
1180 gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[Geom::X], *unit));
1181 gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[Geom::Y], *unit));
1182 }
1183 } else {
1184 boost::optional<Geom::Coord> x = sp_node_selected_common_coord(nodepath, Geom::X);
1185 boost::optional<Geom::Coord> y = sp_node_selected_common_coord(nodepath, Geom::Y);
1186 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1187 /* Note: Currently x and y will always have a value, even if the coordinates of the
1188 selected nodes don't coincide (in this case we use the coordinates of the center
1189 of the bounding box). So the entries are never set to zero. */
1190 // FIXME: Maybe we should clear the entry if several nodes are selected
1191 // instead of providing a kind of average value
1192 gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1193 gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1194 }
1195 }
1196 }
1197 } else {
1198 // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1199 gtk_action_set_sensitive(xact, FALSE);
1200 gtk_action_set_sensitive(yact, FALSE);
1201 }
1203 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1204 }
1206 static void
1207 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1208 {
1209 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1210 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1212 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1213 SPUnit const *unit = tracker->getActiveUnit();
1215 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1216 prefs->setDouble(Glib::ustring("/tools/nodes/") + value_name, sp_units_get_pixels(adj->value, *unit));
1217 }
1219 // quit if run by the attr_changed listener
1220 if (g_object_get_data( tbl, "freeze" )) {
1221 return;
1222 }
1224 // in turn, prevent listener from responding
1225 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1227 ShapeEditor *shape_editor = get_current_shape_editor();
1228 if (shape_editor && shape_editor->has_nodepath()) {
1229 double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1230 if (!strcmp(value_name, "x")) {
1231 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::X);
1232 }
1233 if (!strcmp(value_name, "y")) {
1234 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::Y);
1235 }
1236 }
1238 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1239 }
1241 static void
1242 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1243 {
1244 sp_node_path_value_changed(adj, tbl, "x");
1245 }
1247 static void
1248 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1249 {
1250 sp_node_path_value_changed(adj, tbl, "y");
1251 }
1253 void
1254 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1255 {
1256 {
1257 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1258 SPItem *item = selection->singleItem();
1259 if (item && SP_IS_LPE_ITEM(item)) {
1260 if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1261 gtk_action_set_sensitive(w, TRUE);
1262 } else {
1263 gtk_action_set_sensitive(w, FALSE);
1264 }
1265 } else {
1266 gtk_action_set_sensitive(w, FALSE);
1267 }
1268 }
1270 {
1271 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1272 SPItem *item = selection->singleItem();
1273 if (item && item->clip_ref && item->clip_ref->getObject()) {
1274 gtk_action_set_sensitive(w, TRUE);
1275 } else {
1276 gtk_action_set_sensitive(w, FALSE);
1277 }
1278 }
1280 {
1281 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1282 SPItem *item = selection->singleItem();
1283 if (item && item->mask_ref && item->mask_ref->getObject()) {
1284 gtk_action_set_sensitive(w, TRUE);
1285 } else {
1286 gtk_action_set_sensitive(w, FALSE);
1287 }
1288 }
1289 }
1291 void
1292 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1293 {
1294 sp_node_toolbox_sel_changed (selection, tbl);
1295 }
1299 //################################
1300 //## Node Editing Toolbox ##
1301 //################################
1303 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1304 {
1305 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1306 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1307 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1308 g_object_set_data( holder, "tracker", tracker );
1310 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
1312 {
1313 InkAction* inky = ink_action_new( "NodeInsertAction",
1314 _("Insert node"),
1315 _("Insert new nodes into selected segments"),
1316 INKSCAPE_ICON_NODE_ADD,
1317 secondarySize );
1318 g_object_set( inky, "short_label", _("Insert"), NULL );
1319 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1320 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1321 }
1323 {
1324 InkAction* inky = ink_action_new( "NodeDeleteAction",
1325 _("Delete node"),
1326 _("Delete selected nodes"),
1327 INKSCAPE_ICON_NODE_DELETE,
1328 secondarySize );
1329 g_object_set( inky, "short_label", _("Delete"), NULL );
1330 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1331 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1332 }
1334 {
1335 InkAction* inky = ink_action_new( "NodeJoinAction",
1336 _("Join endnodes"),
1337 _("Join selected endnodes"),
1338 INKSCAPE_ICON_NODE_JOIN,
1339 secondarySize );
1340 g_object_set( inky, "short_label", _("Join"), NULL );
1341 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1342 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1343 }
1345 {
1346 InkAction* inky = ink_action_new( "NodeBreakAction",
1347 _("Break nodes"),
1348 _("Break path at selected nodes"),
1349 INKSCAPE_ICON_NODE_BREAK,
1350 secondarySize );
1351 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1352 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1353 }
1356 {
1357 InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1358 _("Join with segment"),
1359 _("Join selected endnodes with a new segment"),
1360 INKSCAPE_ICON_NODE_JOIN_SEGMENT,
1361 secondarySize );
1362 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1363 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1364 }
1366 {
1367 InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1368 _("Delete segment"),
1369 _("Delete segment between two non-endpoint nodes"),
1370 INKSCAPE_ICON_NODE_DELETE_SEGMENT,
1371 secondarySize );
1372 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1373 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1374 }
1376 {
1377 InkAction* inky = ink_action_new( "NodeCuspAction",
1378 _("Node Cusp"),
1379 _("Make selected nodes corner"),
1380 INKSCAPE_ICON_NODE_TYPE_CUSP,
1381 secondarySize );
1382 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1383 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1384 }
1386 {
1387 InkAction* inky = ink_action_new( "NodeSmoothAction",
1388 _("Node Smooth"),
1389 _("Make selected nodes smooth"),
1390 INKSCAPE_ICON_NODE_TYPE_SMOOTH,
1391 secondarySize );
1392 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1393 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1394 }
1396 {
1397 InkAction* inky = ink_action_new( "NodeSymmetricAction",
1398 _("Node Symmetric"),
1399 _("Make selected nodes symmetric"),
1400 INKSCAPE_ICON_NODE_TYPE_SYMMETRIC,
1401 secondarySize );
1402 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1403 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1404 }
1406 {
1407 InkAction* inky = ink_action_new( "NodeAutoAction",
1408 _("Node Auto"),
1409 _("Make selected nodes auto-smooth"),
1410 INKSCAPE_ICON_NODE_TYPE_AUTO_SMOOTH,
1411 secondarySize );
1412 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_auto), 0 );
1413 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1414 }
1416 {
1417 InkAction* inky = ink_action_new( "NodeLineAction",
1418 _("Node Line"),
1419 _("Make selected segments lines"),
1420 INKSCAPE_ICON_NODE_SEGMENT_LINE,
1421 secondarySize );
1422 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1423 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1424 }
1426 {
1427 InkAction* inky = ink_action_new( "NodeCurveAction",
1428 _("Node Curve"),
1429 _("Make selected segments curves"),
1430 INKSCAPE_ICON_NODE_SEGMENT_CURVE,
1431 secondarySize );
1432 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1433 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1434 }
1436 {
1437 InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1438 _("Show Handles"),
1439 _("Show the Bezier handles of selected nodes"),
1440 INKSCAPE_ICON_SHOW_NODE_HANDLES,
1441 Inkscape::ICON_SIZE_DECORATION );
1442 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1443 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1444 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_handles", true) );
1445 }
1447 {
1448 InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1449 _("Show Outline"),
1450 _("Show the outline of the path"),
1451 INKSCAPE_ICON_SHOW_PATH_OUTLINE,
1452 Inkscape::ICON_SIZE_DECORATION );
1453 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1454 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1455 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_helperpath", false) );
1456 }
1458 {
1459 InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1460 _("Next path effect parameter"),
1461 _("Show next path effect parameter for editing"),
1462 INKSCAPE_ICON_PATH_EFFECT_PARAMETER_NEXT,
1463 Inkscape::ICON_SIZE_DECORATION );
1464 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1465 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1466 g_object_set_data( holder, "nodes_lpeedit", inky);
1467 }
1469 {
1470 InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1471 _("Edit clipping path"),
1472 _("Edit the clipping path of the object"),
1473 INKSCAPE_ICON_PATH_CLIP_EDIT,
1474 Inkscape::ICON_SIZE_DECORATION );
1475 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1476 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1477 g_object_set_data( holder, "nodes_clippathedit", inky);
1478 }
1480 {
1481 InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1482 _("Edit mask path"),
1483 _("Edit the mask of the object"),
1484 INKSCAPE_ICON_PATH_MASK_EDIT,
1485 Inkscape::ICON_SIZE_DECORATION );
1486 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1487 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1488 g_object_set_data( holder, "nodes_maskedit", inky);
1489 }
1491 /* X coord of selected node(s) */
1492 {
1493 EgeAdjustmentAction* eact = 0;
1494 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1495 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1496 eact = create_adjustment_action( "NodeXAction",
1497 _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1498 "/tools/nodes/Xcoord", 0,
1499 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1500 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1501 labels, values, G_N_ELEMENTS(labels),
1502 sp_node_path_x_value_changed );
1503 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1504 g_object_set_data( holder, "nodes_x_action", eact );
1505 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1506 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1507 }
1509 /* Y coord of selected node(s) */
1510 {
1511 EgeAdjustmentAction* eact = 0;
1512 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1513 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1514 eact = create_adjustment_action( "NodeYAction",
1515 _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1516 "/tools/nodes/Ycoord", 0,
1517 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1518 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1519 labels, values, G_N_ELEMENTS(labels),
1520 sp_node_path_y_value_changed );
1521 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1522 g_object_set_data( holder, "nodes_y_action", eact );
1523 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1524 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1525 }
1527 // add the units menu
1528 {
1529 GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1530 gtk_action_group_add_action( mainActions, act );
1531 }
1534 sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1536 //watch selection
1537 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1539 sigc::connection *c_selection_changed =
1540 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1541 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1542 pool->add_connection ("selection-changed", c_selection_changed);
1544 sigc::connection *c_selection_modified =
1545 new sigc::connection (sp_desktop_selection (desktop)->connectModified
1546 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1547 pool->add_connection ("selection-modified", c_selection_modified);
1549 sigc::connection *c_subselection_changed =
1550 new sigc::connection (desktop->connectToolSubselectionChanged
1551 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1552 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1554 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1556 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1557 } // end of sp_node_toolbox_prep()
1560 //########################
1561 //## Zoom Toolbox ##
1562 //########################
1564 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1565 {
1566 // no custom GtkAction setup needed
1567 } // end of sp_zoom_toolbox_prep()
1569 void
1570 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1571 {
1572 toolbox_set_desktop(toolbox,
1573 desktop,
1574 setup_tool_toolbox,
1575 update_tool_toolbox,
1576 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1577 "event_context_connection")));
1578 }
1581 void
1582 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1583 {
1584 toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1585 desktop,
1586 setup_aux_toolbox,
1587 update_aux_toolbox,
1588 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1589 "event_context_connection")));
1590 }
1592 void
1593 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1594 {
1595 toolbox_set_desktop(toolbox,
1596 desktop,
1597 setup_commands_toolbox,
1598 update_commands_toolbox,
1599 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1600 "event_context_connection")));
1601 }
1603 void
1604 sp_snap_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1605 {
1606 toolbox_set_desktop(toolbox,
1607 desktop,
1608 setup_snap_toolbox,
1609 update_snap_toolbox,
1610 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1611 "event_context_connection")));
1612 }
1615 static void
1616 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1617 {
1618 gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1619 SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1621 if (old_desktop) {
1622 GList *children, *iter;
1624 children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1625 for ( iter = children ; iter ; iter = iter->next ) {
1626 gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1627 }
1628 g_list_free(children);
1629 }
1631 g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1633 if (desktop) {
1634 gtk_widget_set_sensitive(toolbox, TRUE);
1635 setup_func(toolbox, desktop);
1636 update_func(desktop, desktop->event_context, toolbox);
1637 *conn = desktop->connectEventContextChanged
1638 (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1639 } else {
1640 gtk_widget_set_sensitive(toolbox, FALSE);
1641 }
1643 } // end of toolbox_set_desktop()
1646 static void
1647 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1648 {
1649 gchar const * descr =
1650 "<ui>"
1651 " <toolbar name='ToolToolbar'>"
1652 " <toolitem action='ToolSelector' />"
1653 " <toolitem action='ToolNode' />"
1654 " <toolitem action='ToolTweak' />"
1655 " <toolitem action='ToolSpray' />"
1656 " <toolitem action='ToolZoom' />"
1657 " <toolitem action='ToolRect' />"
1658 " <toolitem action='Tool3DBox' />"
1659 " <toolitem action='ToolArc' />"
1660 " <toolitem action='ToolStar' />"
1661 " <toolitem action='ToolSpiral' />"
1662 " <toolitem action='ToolPencil' />"
1663 " <toolitem action='ToolPen' />"
1664 " <toolitem action='ToolCalligraphic' />"
1665 " <toolitem action='ToolEraser' />"
1666 // " <toolitem action='ToolLPETool' />"
1667 " <toolitem action='ToolPaintBucket' />"
1668 " <toolitem action='ToolText' />"
1669 " <toolitem action='ToolConnector' />"
1670 " <toolitem action='ToolGradient' />"
1671 " <toolitem action='ToolDropper' />"
1672 " </toolbar>"
1673 "</ui>";
1674 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1675 GtkUIManager* mgr = gtk_ui_manager_new();
1676 GError* errVal = 0;
1677 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1679 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1680 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1682 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" );
1683 if ( prefs->getBool("/toolbox/icononly", true) ) {
1684 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1685 }
1686 Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
1687 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1689 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
1690 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1692 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1694 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1695 if ( child ) {
1696 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1697 }
1699 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1700 // Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
1701 }
1704 static void
1705 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1706 {
1707 gchar const *const tname = ( eventcontext
1708 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1709 : NULL );
1710 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1712 for (int i = 0 ; tools[i].type_name ; i++ ) {
1713 Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1714 if ( act ) {
1715 bool setActive = tname && !strcmp(tname, tools[i].type_name);
1716 Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1717 if ( verbAct ) {
1718 verbAct->set_active(setActive);
1719 }
1720 }
1721 }
1722 }
1724 static void
1725 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1726 {
1727 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1728 GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1729 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1730 GtkUIManager* mgr = gtk_ui_manager_new();
1731 GError* errVal = 0;
1732 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1733 gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1735 std::map<std::string, GtkWidget*> dataHolders;
1737 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1738 if ( aux_toolboxes[i].prep_func ) {
1739 // converted to GtkActions and UIManager
1741 GtkWidget* kludge = gtk_toolbar_new();
1742 g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1743 g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1744 dataHolders[aux_toolboxes[i].type_name] = kludge;
1745 aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1746 } else {
1748 GtkWidget *sub_toolbox = 0;
1749 if (aux_toolboxes[i].create_func == NULL)
1750 sub_toolbox = sp_empty_toolbox_new(desktop);
1751 else {
1752 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1753 }
1755 gtk_size_group_add_widget( grouper, sub_toolbox );
1757 gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1758 g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1760 }
1761 }
1763 // Second pass to create toolbars *after* all GtkActions are created
1764 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1765 if ( aux_toolboxes[i].prep_func ) {
1766 // converted to GtkActions and UIManager
1768 GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1770 GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1771 gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1773 gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1774 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1775 g_free( tmp );
1776 tmp = 0;
1778 if ( prefs->getBool( "/toolbox/icononly", true) ) {
1779 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1780 }
1782 Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
1783 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1785 gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1787 if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1788 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1789 swatch->setDesktop( desktop );
1790 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1791 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1792 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1793 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 );
1794 }
1796 gtk_widget_show_all( holder );
1797 sp_set_font_size_smaller( holder );
1799 gtk_size_group_add_widget( grouper, holder );
1801 gtk_container_add( GTK_CONTAINER(toolbox), holder );
1802 g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1803 }
1804 }
1806 g_object_unref( G_OBJECT(grouper) );
1807 }
1809 static void
1810 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1811 {
1812 gchar const *tname = ( eventcontext
1813 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1814 : NULL );
1815 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1816 GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1817 if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1818 gtk_widget_show_all(sub_toolbox);
1819 g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1820 } else {
1821 gtk_widget_hide(sub_toolbox);
1822 }
1823 }
1824 }
1826 static void
1827 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1828 {
1829 gchar const * descr =
1830 "<ui>"
1831 " <toolbar name='CommandsToolbar'>"
1832 " <toolitem action='FileNew' />"
1833 " <toolitem action='FileOpen' />"
1834 " <toolitem action='FileSave' />"
1835 " <toolitem action='FilePrint' />"
1836 " <separator />"
1837 " <toolitem action='FileImport' />"
1838 " <toolitem action='FileExport' />"
1839 " <separator />"
1840 " <toolitem action='EditUndo' />"
1841 " <toolitem action='EditRedo' />"
1842 " <separator />"
1843 " <toolitem action='EditCopy' />"
1844 " <toolitem action='EditCut' />"
1845 " <toolitem action='EditPaste' />"
1846 " <separator />"
1847 " <toolitem action='ZoomSelection' />"
1848 " <toolitem action='ZoomDrawing' />"
1849 " <toolitem action='ZoomPage' />"
1850 " <separator />"
1851 " <toolitem action='EditDuplicate' />"
1852 " <toolitem action='EditClone' />"
1853 " <toolitem action='EditUnlinkClone' />"
1854 " <separator />"
1855 " <toolitem action='SelectionGroup' />"
1856 " <toolitem action='SelectionUnGroup' />"
1857 " <separator />"
1858 " <toolitem action='DialogFillStroke' />"
1859 " <toolitem action='DialogText' />"
1860 " <toolitem action='DialogLayers' />"
1861 " <toolitem action='DialogXMLEditor' />"
1862 " <toolitem action='DialogAlignDistribute' />"
1863 " <separator />"
1864 " <toolitem action='DialogPreferences' />"
1865 " <toolitem action='DialogDocumentProperties' />"
1866 " </toolbar>"
1867 "</ui>";
1868 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1869 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1871 GtkUIManager* mgr = gtk_ui_manager_new();
1872 GError* errVal = 0;
1874 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1875 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1877 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1878 if ( prefs->getBool("/toolbox/icononly", true) ) {
1879 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1880 }
1882 Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
1883 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1885 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1886 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1889 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1891 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1892 if ( child ) {
1893 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1894 }
1896 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1897 }
1899 static void
1900 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1901 {
1902 }
1904 void toggle_snap_callback (GtkToggleAction *act, gpointer data) { //data points to the toolbox
1906 if (g_object_get_data(G_OBJECT(data), "freeze" )) {
1907 return;
1908 }
1910 gpointer ptr = g_object_get_data(G_OBJECT(data), "desktop");
1911 g_assert(ptr != NULL);
1913 SPDesktop *dt = reinterpret_cast<SPDesktop*>(ptr);
1914 SPNamedView *nv = sp_desktop_namedview(dt);
1915 SPDocument *doc = SP_OBJECT_DOCUMENT(nv);
1917 if (dt == NULL || nv == NULL) {
1918 g_warning("No desktop or namedview specified (in toggle_snap_callback)!");
1919 return;
1920 }
1922 Inkscape::XML::Node *repr = SP_OBJECT_REPR(nv);
1924 if (repr == NULL) {
1925 g_warning("This namedview doesn't have a xml representation attached!");
1926 return;
1927 }
1929 bool saved = sp_document_get_undo_sensitive(doc);
1930 sp_document_set_undo_sensitive(doc, false);
1932 bool v = false;
1933 SPAttributeEnum attr = (SPAttributeEnum) GPOINTER_TO_INT(g_object_get_data(G_OBJECT(act), "SP_ATTR_INKSCAPE"));
1935 switch (attr) {
1936 case SP_ATTR_INKSCAPE_SNAP_GLOBAL:
1937 dt->toggleSnapGlobal();
1938 break;
1939 case SP_ATTR_INKSCAPE_SNAP_BBOX:
1940 v = nv->snap_manager.snapprefs.getSnapModeBBox();
1941 sp_repr_set_boolean(repr, "inkscape:snap-bbox", !v);
1942 break;
1943 case SP_ATTR_INKSCAPE_BBOX_PATHS:
1944 v = nv->snap_manager.snapprefs.getSnapToBBoxPath();
1945 sp_repr_set_boolean(repr, "inkscape:bbox-paths", !v);
1946 break;
1947 case SP_ATTR_INKSCAPE_BBOX_NODES:
1948 v = nv->snap_manager.snapprefs.getSnapToBBoxNode();
1949 sp_repr_set_boolean(repr, "inkscape:bbox-nodes", !v);
1950 break;
1951 case SP_ATTR_INKSCAPE_SNAP_NODES:
1952 v = nv->snap_manager.snapprefs.getSnapModeNode();
1953 sp_repr_set_boolean(repr, "inkscape:snap-nodes", !v);
1954 break;
1955 case SP_ATTR_INKSCAPE_OBJECT_PATHS:
1956 v = nv->snap_manager.snapprefs.getSnapToItemPath();
1957 sp_repr_set_boolean(repr, "inkscape:object-paths", !v);
1958 break;
1959 case SP_ATTR_INKSCAPE_OBJECT_NODES:
1960 v = nv->snap_manager.snapprefs.getSnapToItemNode();
1961 sp_repr_set_boolean(repr, "inkscape:object-nodes", !v);
1962 break;
1963 case SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES:
1964 v = nv->snap_manager.snapprefs.getSnapSmoothNodes();
1965 sp_repr_set_boolean(repr, "inkscape:snap-smooth-nodes", !v);
1966 break;
1967 case SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS:
1968 v = nv->snap_manager.snapprefs.getSnapIntersectionCS();
1969 sp_repr_set_boolean(repr, "inkscape:snap-intersection-paths", !v);
1970 break;
1971 case SP_ATTR_INKSCAPE_SNAP_CENTER:
1972 v = nv->snap_manager.snapprefs.getIncludeItemCenter();
1973 sp_repr_set_boolean(repr, "inkscape:snap-center", !v);
1974 break;
1975 case SP_ATTR_INKSCAPE_SNAP_GRIDS:
1976 v = nv->snap_manager.snapprefs.getSnapToGrids();
1977 sp_repr_set_boolean(repr, "inkscape:snap-grids", !v);
1978 break;
1979 case SP_ATTR_INKSCAPE_SNAP_TO_GUIDES:
1980 v = nv->snap_manager.snapprefs.getSnapToGuides();
1981 sp_repr_set_boolean(repr, "inkscape:snap-to-guides", !v);
1982 break;
1983 case SP_ATTR_INKSCAPE_SNAP_PAGE:
1984 v = nv->snap_manager.snapprefs.getSnapToPageBorder();
1985 sp_repr_set_boolean(repr, "inkscape:snap-page", !v);
1986 break;
1987 /*case SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE:
1988 v = nv->snap_manager.snapprefs.getSnapIntersectionGG();
1989 sp_repr_set_boolean(repr, "inkscape:snap-intersection-grid-guide", !v);
1990 break;*/
1991 case SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS:
1992 v = nv->snap_manager.snapprefs.getSnapLineMidpoints();
1993 sp_repr_set_boolean(repr, "inkscape:snap-midpoints", !v);
1994 break;
1995 case SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS:
1996 v = nv->snap_manager.snapprefs.getSnapObjectMidpoints();
1997 sp_repr_set_boolean(repr, "inkscape:snap-object-midpoints", !v);
1998 break;
1999 case SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS:
2000 v = nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints();
2001 sp_repr_set_boolean(repr, "inkscape:snap-bbox-edge-midpoints", !v);
2002 break;
2003 case SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS:
2004 v = nv->snap_manager.snapprefs.getSnapBBoxMidpoints();
2005 sp_repr_set_boolean(repr, "inkscape:snap-bbox-midpoints", !v);
2006 break;
2007 default:
2008 g_warning("toggle_snap_callback has been called with an ID for which no action has been defined");
2009 break;
2010 }
2012 // The snapping preferences are stored in the document, and therefore toggling makes the document dirty
2013 doc->setModifiedSinceSave();
2015 sp_document_set_undo_sensitive(doc, saved);
2016 }
2018 void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
2019 {
2020 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2021 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2023 gchar const * descr =
2024 "<ui>"
2025 " <toolbar name='SnapToolbar'>"
2026 " <toolitem action='ToggleSnapGlobal' />"
2027 " <separator />"
2028 " <toolitem action='ToggleSnapFromBBoxCorner' />"
2029 " <toolitem action='ToggleSnapToBBoxPath' />"
2030 " <toolitem action='ToggleSnapToBBoxNode' />"
2031 " <toolitem action='ToggleSnapToFromBBoxEdgeMidpoints' />"
2032 " <toolitem action='ToggleSnapToFromBBoxCenters' />"
2033 " <separator />"
2034 " <toolitem action='ToggleSnapFromNode' />"
2035 " <toolitem action='ToggleSnapToItemPath' />"
2036 " <toolitem action='ToggleSnapToPathIntersections' />"
2037 " <toolitem action='ToggleSnapToItemNode' />"
2038 " <toolitem action='ToggleSnapToSmoothNodes' />"
2039 " <toolitem action='ToggleSnapToFromLineMidpoints' />"
2040 " <toolitem action='ToggleSnapToFromObjectCenters' />"
2041 " <toolitem action='ToggleSnapToFromRotationCenter' />"
2042 " <separator />"
2043 " <toolitem action='ToggleSnapToPageBorder' />"
2044 " <toolitem action='ToggleSnapToGrids' />"
2045 " <toolitem action='ToggleSnapToGuides' />"
2046 //" <toolitem action='ToggleSnapToGridGuideIntersections' />"
2047 " </toolbar>"
2048 "</ui>";
2050 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
2052 {
2053 InkToggleAction* act = ink_toggle_action_new("ToggleSnapGlobal",
2054 _("Snap"), _("Enable snapping"), INKSCAPE_ICON_SNAP, secondarySize,
2055 SP_ATTR_INKSCAPE_SNAP_GLOBAL);
2057 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2058 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2059 }
2061 {
2062 InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromBBoxCorner",
2063 _("Bounding box"), _("Snap bounding box corners"), INKSCAPE_ICON_SNAP_BOUNDING_BOX,
2064 secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX);
2066 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2067 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2068 }
2070 {
2071 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxPath",
2072 _("Bounding box edges"), _("Snap to edges of a bounding box"),
2073 INKSCAPE_ICON_SNAP_BOUNDING_BOX_EDGES, secondarySize, SP_ATTR_INKSCAPE_BBOX_PATHS);
2075 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2076 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2077 }
2079 {
2080 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxNode",
2081 _("Bounding box corners"), _("Snap to bounding box corners"),
2082 INKSCAPE_ICON_SNAP_BOUNDING_BOX_CORNERS, secondarySize, SP_ATTR_INKSCAPE_BBOX_NODES);
2084 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2085 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2086 }
2088 {
2089 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxEdgeMidpoints",
2090 _("BBox Edge Midpoints"), _("Snap from and to midpoints of bounding box edges"),
2091 INKSCAPE_ICON_SNAP_BOUNDING_BOX_MIDPOINTS, secondarySize,
2092 SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS);
2094 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2095 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2096 }
2098 {
2099 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxCenters",
2100 _("BBox Centers"), _("Snapping from and to centers of bounding boxes"),
2101 INKSCAPE_ICON_SNAP_BOUNDING_BOX_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS);
2103 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2104 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2105 }
2107 {
2108 InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromNode",
2109 _("Nodes"), _("Snap nodes or handles"), INKSCAPE_ICON_SNAP_NODES, secondarySize, SP_ATTR_INKSCAPE_SNAP_NODES);
2111 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2112 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2113 }
2115 {
2116 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemPath",
2117 _("Paths"), _("Snap to paths"), INKSCAPE_ICON_SNAP_NODES_PATH, secondarySize,
2118 SP_ATTR_INKSCAPE_OBJECT_PATHS);
2120 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2121 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2122 }
2124 {
2125 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPathIntersections",
2126 _("Path intersections"), _("Snap to path intersections"),
2127 INKSCAPE_ICON_SNAP_NODES_INTERSECTION, secondarySize, SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS);
2129 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2130 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2131 }
2133 {
2134 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemNode",
2135 _("To nodes"), _("Snap to cusp nodes"), INKSCAPE_ICON_SNAP_NODES_CUSP, secondarySize,
2136 SP_ATTR_INKSCAPE_OBJECT_NODES);
2138 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2139 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2140 }
2142 {
2143 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToSmoothNodes",
2144 _("Smooth nodes"), _("Snap to smooth nodes"), INKSCAPE_ICON_SNAP_NODES_SMOOTH,
2145 secondarySize, SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES);
2147 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2148 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2149 }
2151 {
2152 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromLineMidpoints",
2153 _("Line Midpoints"), _("Snap from and to midpoints of line segments"),
2154 INKSCAPE_ICON_SNAP_NODES_MIDPOINT, secondarySize, SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS);
2156 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2157 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2158 }
2160 {
2161 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromObjectCenters",
2162 _("Object Centers"), _("Snap from and to centers of objects"),
2163 INKSCAPE_ICON_SNAP_NODES_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS);
2165 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2166 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2167 }
2169 {
2170 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromRotationCenter",
2171 _("Rotation Centers"), _("Snap from and to an item's rotation center"),
2172 INKSCAPE_ICON_SNAP_NODES_ROTATION_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_CENTER);
2174 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2175 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2176 }
2178 {
2179 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPageBorder",
2180 _("Page border"), _("Snap to the page border"), INKSCAPE_ICON_SNAP_PAGE,
2181 secondarySize, SP_ATTR_INKSCAPE_SNAP_PAGE);
2183 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2184 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2185 }
2187 {
2188 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGrids",
2189 _("Grids"), _("Snap to grids"), INKSCAPE_ICON_GRID_RECTANGULAR, secondarySize,
2190 SP_ATTR_INKSCAPE_SNAP_GRIDS);
2192 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2193 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2194 }
2196 {
2197 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGuides",
2198 _("Guides"), _("Snap to guides"), INKSCAPE_ICON_GUIDES, secondarySize,
2199 SP_ATTR_INKSCAPE_SNAP_TO_GUIDES);
2201 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2202 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2203 }
2205 /*{
2206 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGridGuideIntersections",
2207 _("Grid/guide intersections"), _("Snap to intersections of a grid with a guide"),
2208 INKSCAPE_ICON_SNAP_GRID_GUIDE_INTERSECTIONS, secondarySize,
2209 SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE);
2211 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2212 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2213 }*/
2215 GtkUIManager* mgr = gtk_ui_manager_new();
2216 GError* errVal = 0;
2218 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
2219 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
2221 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/SnapToolbar" );
2222 if ( prefs->getBool("/toolbox/icononly", true) ) {
2223 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
2224 }
2226 Inkscape::IconSize toolboxSize = prefToSize("/toolbox/secondary");
2227 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
2229 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
2230 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
2232 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
2234 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
2235 if ( child ) {
2236 gtk_container_remove( GTK_CONTAINER(toolbox), child );
2237 }
2239 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
2241 }
2243 void update_snap_toolbox(SPDesktop *desktop, SPEventContext */*eventcontext*/, GtkWidget *toolbox)
2244 {
2245 g_assert(desktop != NULL);
2246 g_assert(toolbox != NULL);
2248 SPNamedView *nv = sp_desktop_namedview(desktop);
2249 if (nv == NULL) {
2250 g_warning("Namedview cannot be retrieved (in update_snap_toolbox)!");
2251 return;
2252 }
2254 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2256 Glib::RefPtr<Gtk::Action> act1 = mainActions->get_action("ToggleSnapGlobal");
2257 Glib::RefPtr<Gtk::Action> act2 = mainActions->get_action("ToggleSnapFromBBoxCorner");
2258 Glib::RefPtr<Gtk::Action> act3 = mainActions->get_action("ToggleSnapToBBoxPath");
2259 Glib::RefPtr<Gtk::Action> act4 = mainActions->get_action("ToggleSnapToBBoxNode");
2260 Glib::RefPtr<Gtk::Action> act4b = mainActions->get_action("ToggleSnapToFromBBoxEdgeMidpoints");
2261 Glib::RefPtr<Gtk::Action> act4c = mainActions->get_action("ToggleSnapToFromBBoxCenters");
2262 Glib::RefPtr<Gtk::Action> act5 = mainActions->get_action("ToggleSnapFromNode");
2263 Glib::RefPtr<Gtk::Action> act6 = mainActions->get_action("ToggleSnapToItemPath");
2264 Glib::RefPtr<Gtk::Action> act6b = mainActions->get_action("ToggleSnapToPathIntersections");
2265 Glib::RefPtr<Gtk::Action> act7 = mainActions->get_action("ToggleSnapToItemNode");
2266 Glib::RefPtr<Gtk::Action> act8 = mainActions->get_action("ToggleSnapToSmoothNodes");
2267 Glib::RefPtr<Gtk::Action> act9 = mainActions->get_action("ToggleSnapToFromLineMidpoints");
2268 Glib::RefPtr<Gtk::Action> act10 = mainActions->get_action("ToggleSnapToFromObjectCenters");
2269 Glib::RefPtr<Gtk::Action> act11 = mainActions->get_action("ToggleSnapToFromRotationCenter");
2270 Glib::RefPtr<Gtk::Action> act12 = mainActions->get_action("ToggleSnapToPageBorder");
2271 //Glib::RefPtr<Gtk::Action> act13 = mainActions->get_action("ToggleSnapToGridGuideIntersections");
2272 Glib::RefPtr<Gtk::Action> act14 = mainActions->get_action("ToggleSnapToGrids");
2273 Glib::RefPtr<Gtk::Action> act15 = mainActions->get_action("ToggleSnapToGuides");
2276 if (!act1) {
2277 return; // The snap actions haven't been defined yet (might be the case during startup)
2278 }
2280 // The ..._set_active calls below will toggle the buttons, but this shouldn't lead to
2281 // changes in our document because we're only updating the UI;
2282 // Setting the "freeze" parameter to true will block the code in toggle_snap_callback()
2283 g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(TRUE));
2285 bool const c1 = nv->snap_manager.snapprefs.getSnapEnabledGlobally();
2286 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act1->gobj()), c1);
2288 bool const c2 = nv->snap_manager.snapprefs.getSnapModeBBox();
2289 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act2->gobj()), c2);
2290 gtk_action_set_sensitive(GTK_ACTION(act2->gobj()), c1);
2292 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act3->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxPath());
2293 gtk_action_set_sensitive(GTK_ACTION(act3->gobj()), c1 && c2);
2294 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxNode());
2295 gtk_action_set_sensitive(GTK_ACTION(act4->gobj()), c1 && c2);
2296 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4b->gobj()), nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints());
2297 gtk_action_set_sensitive(GTK_ACTION(act4b->gobj()), c1 && c2);
2298 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4c->gobj()), nv->snap_manager.snapprefs.getSnapBBoxMidpoints());
2299 gtk_action_set_sensitive(GTK_ACTION(act4c->gobj()), c1 && c2);
2301 bool const c3 = nv->snap_manager.snapprefs.getSnapModeNode();
2302 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act5->gobj()), c3);
2303 gtk_action_set_sensitive(GTK_ACTION(act5->gobj()), c1);
2305 bool const c4 = nv->snap_manager.snapprefs.getSnapToItemPath();
2306 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6->gobj()), c4);
2307 gtk_action_set_sensitive(GTK_ACTION(act6->gobj()), c1 && c3);
2308 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6b->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionCS());
2309 gtk_action_set_sensitive(GTK_ACTION(act6b->gobj()), c1 && c3 && c4);
2310 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act7->gobj()), nv->snap_manager.snapprefs.getSnapToItemNode());
2311 gtk_action_set_sensitive(GTK_ACTION(act7->gobj()), c1 && c3);
2312 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act8->gobj()), nv->snap_manager.snapprefs.getSnapSmoothNodes());
2313 gtk_action_set_sensitive(GTK_ACTION(act8->gobj()), c1 && c3);
2314 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act9->gobj()), nv->snap_manager.snapprefs.getSnapLineMidpoints());
2315 gtk_action_set_sensitive(GTK_ACTION(act9->gobj()), c1 && c3);
2316 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act10->gobj()), nv->snap_manager.snapprefs.getSnapObjectMidpoints());
2317 gtk_action_set_sensitive(GTK_ACTION(act10->gobj()), c1 && c3);
2318 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act11->gobj()), nv->snap_manager.snapprefs.getIncludeItemCenter());
2319 gtk_action_set_sensitive(GTK_ACTION(act11->gobj()), c1 && c3);
2321 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act12->gobj()), nv->snap_manager.snapprefs.getSnapToPageBorder());
2322 gtk_action_set_sensitive(GTK_ACTION(act12->gobj()), c1);
2323 //gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act13->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionGG());
2324 //gtk_action_set_sensitive(GTK_ACTION(act13->gobj()), c1);
2326 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act14->gobj()), nv->snap_manager.snapprefs.getSnapToGrids());
2327 gtk_action_set_sensitive(GTK_ACTION(act14->gobj()), c1);
2328 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act15->gobj()), nv->snap_manager.snapprefs.getSnapToGuides());
2329 gtk_action_set_sensitive(GTK_ACTION(act15->gobj()), c1);
2332 g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(FALSE)); // unfreeze (see above)
2333 }
2335 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
2336 {
2337 gtk_widget_show(toolbox_toplevel);
2338 GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
2340 GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
2341 if (!shown_toolbox) {
2342 return;
2343 }
2344 gtk_widget_show(toolbox);
2346 gtk_widget_show_all(shown_toolbox);
2347 }
2349 static GtkWidget *
2350 sp_empty_toolbox_new(SPDesktop *desktop)
2351 {
2352 GtkWidget *tbl = gtk_toolbar_new();
2353 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
2354 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
2356 gtk_widget_show_all(tbl);
2357 sp_set_font_size_smaller (tbl);
2359 return tbl;
2360 }
2362 #define MODE_LABEL_WIDTH 70
2364 //########################
2365 //## Star ##
2366 //########################
2368 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2369 {
2370 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2372 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2373 // do not remember prefs if this call is initiated by an undo change, because undoing object
2374 // creation sets bogus values to its attributes before it is deleted
2375 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2376 prefs->setInt("/tools/shapes/star/magnitude", (gint)adj->value);
2377 }
2379 // quit if run by the attr_changed listener
2380 if (g_object_get_data( dataKludge, "freeze" )) {
2381 return;
2382 }
2384 // in turn, prevent listener from responding
2385 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2387 bool modmade = false;
2389 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2390 GSList const *items = selection->itemList();
2391 for (; items != NULL; items = items->next) {
2392 if (SP_IS_STAR((SPItem *) items->data)) {
2393 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2394 sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
2395 sp_repr_set_svg_double(repr, "sodipodi:arg2",
2396 (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
2397 + M_PI / (gint)adj->value));
2398 SP_OBJECT((SPItem *) items->data)->updateRepr();
2399 modmade = true;
2400 }
2401 }
2402 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2403 _("Star: Change number of corners"));
2405 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2406 }
2408 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2409 {
2410 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2412 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2413 if (!IS_NAN(adj->value)) {
2414 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2415 prefs->setDouble("/tools/shapes/star/proportion", adj->value);
2416 }
2417 }
2419 // quit if run by the attr_changed listener
2420 if (g_object_get_data( dataKludge, "freeze" )) {
2421 return;
2422 }
2424 // in turn, prevent listener from responding
2425 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2427 bool modmade = false;
2428 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2429 GSList const *items = selection->itemList();
2430 for (; items != NULL; items = items->next) {
2431 if (SP_IS_STAR((SPItem *) items->data)) {
2432 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2434 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2435 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2436 if (r2 < r1) {
2437 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
2438 } else {
2439 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
2440 }
2442 SP_OBJECT((SPItem *) items->data)->updateRepr();
2443 modmade = true;
2444 }
2445 }
2447 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2448 _("Star: Change spoke ratio"));
2450 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2451 }
2453 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
2454 {
2455 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2456 bool flat = ege_select_one_action_get_active( act ) == 0;
2458 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2459 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2460 prefs->setBool( "/tools/shapes/star/isflatsided", flat);
2461 }
2463 // quit if run by the attr_changed listener
2464 if (g_object_get_data( dataKludge, "freeze" )) {
2465 return;
2466 }
2468 // in turn, prevent listener from responding
2469 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2471 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2472 GSList const *items = selection->itemList();
2473 GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2474 bool modmade = false;
2476 if ( prop_action ) {
2477 gtk_action_set_sensitive( prop_action, !flat );
2478 }
2480 for (; items != NULL; items = items->next) {
2481 if (SP_IS_STAR((SPItem *) items->data)) {
2482 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2483 repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
2484 SP_OBJECT((SPItem *) items->data)->updateRepr();
2485 modmade = true;
2486 }
2487 }
2489 if (modmade) {
2490 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2491 flat ? _("Make polygon") : _("Make star"));
2492 }
2494 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2495 }
2497 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2498 {
2499 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2501 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2502 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2503 prefs->setDouble("/tools/shapes/star/rounded", (gdouble) adj->value);
2504 }
2506 // quit if run by the attr_changed listener
2507 if (g_object_get_data( dataKludge, "freeze" )) {
2508 return;
2509 }
2511 // in turn, prevent listener from responding
2512 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2514 bool modmade = false;
2516 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2517 GSList const *items = selection->itemList();
2518 for (; items != NULL; items = items->next) {
2519 if (SP_IS_STAR((SPItem *) items->data)) {
2520 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2521 sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2522 SP_OBJECT(items->data)->updateRepr();
2523 modmade = true;
2524 }
2525 }
2526 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2527 _("Star: Change rounding"));
2529 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2530 }
2532 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2533 {
2534 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2536 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2537 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2538 prefs->setDouble("/tools/shapes/star/randomized", (gdouble) adj->value);
2539 }
2541 // quit if run by the attr_changed listener
2542 if (g_object_get_data( dataKludge, "freeze" )) {
2543 return;
2544 }
2546 // in turn, prevent listener from responding
2547 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2549 bool modmade = false;
2551 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2552 GSList const *items = selection->itemList();
2553 for (; items != NULL; items = items->next) {
2554 if (SP_IS_STAR((SPItem *) items->data)) {
2555 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2556 sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2557 SP_OBJECT(items->data)->updateRepr();
2558 modmade = true;
2559 }
2560 }
2561 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2562 _("Star: Change randomization"));
2564 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2565 }
2568 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2569 gchar const */*old_value*/, gchar const */*new_value*/,
2570 bool /*is_interactive*/, gpointer data)
2571 {
2572 GtkWidget *tbl = GTK_WIDGET(data);
2574 // quit if run by the _changed callbacks
2575 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2576 return;
2577 }
2579 // in turn, prevent callbacks from responding
2580 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2582 GtkAdjustment *adj = 0;
2584 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2585 bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2587 if (!strcmp(name, "inkscape:randomized")) {
2588 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2589 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2590 } else if (!strcmp(name, "inkscape:rounded")) {
2591 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2592 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2593 } else if (!strcmp(name, "inkscape:flatsided")) {
2594 GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2595 char const *flatsides = repr->attribute("inkscape:flatsided");
2596 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2597 if ( flatsides && !strcmp(flatsides,"false") ) {
2598 ege_select_one_action_set_active( flat_action, 1 );
2599 gtk_action_set_sensitive( prop_action, TRUE );
2600 } else {
2601 ege_select_one_action_set_active( flat_action, 0 );
2602 gtk_action_set_sensitive( prop_action, FALSE );
2603 }
2604 } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2605 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2606 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2607 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2608 if (r2 < r1) {
2609 gtk_adjustment_set_value(adj, r2/r1);
2610 } else {
2611 gtk_adjustment_set_value(adj, r1/r2);
2612 }
2613 } else if (!strcmp(name, "sodipodi:sides")) {
2614 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2615 gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2616 }
2618 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2619 }
2622 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2623 {
2624 NULL, /* child_added */
2625 NULL, /* child_removed */
2626 star_tb_event_attr_changed,
2627 NULL, /* content_changed */
2628 NULL /* order_changed */
2629 };
2632 /**
2633 * \param selection Should not be NULL.
2634 */
2635 static void
2636 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2637 {
2638 int n_selected = 0;
2639 Inkscape::XML::Node *repr = NULL;
2641 purge_repr_listener( tbl, tbl );
2643 for (GSList const *items = selection->itemList();
2644 items != NULL;
2645 items = items->next)
2646 {
2647 if (SP_IS_STAR((SPItem *) items->data)) {
2648 n_selected++;
2649 repr = SP_OBJECT_REPR((SPItem *) items->data);
2650 }
2651 }
2653 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2655 if (n_selected == 0) {
2656 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2657 } else if (n_selected == 1) {
2658 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2660 if (repr) {
2661 g_object_set_data( tbl, "repr", repr );
2662 Inkscape::GC::anchor(repr);
2663 sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2664 sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2665 }
2666 } else {
2667 // FIXME: implement averaging of all parameters for multiple selected stars
2668 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2669 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2670 }
2671 }
2674 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2675 {
2676 // FIXME: in this and all other _default functions, set some flag telling the value_changed
2677 // callbacks to lump all the changes for all selected objects in one undo step
2679 GtkAdjustment *adj = 0;
2681 // fixme: make settable in prefs!
2682 gint mag = 5;
2683 gdouble prop = 0.5;
2684 gboolean flat = FALSE;
2685 gdouble randomized = 0;
2686 gdouble rounded = 0;
2688 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2689 ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2691 GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2692 gtk_action_set_sensitive( sb2, !flat );
2694 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2695 gtk_adjustment_set_value(adj, mag);
2696 gtk_adjustment_value_changed(adj);
2698 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2699 gtk_adjustment_set_value(adj, prop);
2700 gtk_adjustment_value_changed(adj);
2702 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2703 gtk_adjustment_set_value(adj, rounded);
2704 gtk_adjustment_value_changed(adj);
2706 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2707 gtk_adjustment_set_value(adj, randomized);
2708 gtk_adjustment_value_changed(adj);
2709 }
2712 void
2713 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2714 {
2715 GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2716 if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2717 GtkWidget *l = gtk_label_new(NULL);
2718 gtk_label_set_markup(GTK_LABEL(l), title);
2719 gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2720 if ( GTK_IS_TOOLBAR(tbl) ) {
2721 gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2722 } else {
2723 gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2724 }
2725 gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2726 }
2729 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2730 {
2731 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
2733 {
2734 EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2735 ege_output_action_set_use_markup( act, TRUE );
2736 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2737 g_object_set_data( holder, "mode_action", act );
2738 }
2740 {
2741 EgeAdjustmentAction* eact = 0;
2742 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2743 bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2745 /* Flatsided checkbox */
2746 {
2747 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2749 GtkTreeIter iter;
2750 gtk_list_store_append( model, &iter );
2751 gtk_list_store_set( model, &iter,
2752 0, _("Polygon"),
2753 1, _("Regular polygon (with one handle) instead of a star"),
2754 2, INKSCAPE_ICON_DRAW_POLYGON,
2755 -1 );
2757 gtk_list_store_append( model, &iter );
2758 gtk_list_store_set( model, &iter,
2759 0, _("Star"),
2760 1, _("Star instead of a regular polygon (with one handle)"),
2761 2, INKSCAPE_ICON_DRAW_STAR,
2762 -1 );
2764 EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2765 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2766 g_object_set_data( holder, "flat_action", act );
2768 ege_select_one_action_set_appearance( act, "full" );
2769 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2770 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2771 ege_select_one_action_set_icon_column( act, 2 );
2772 ege_select_one_action_set_icon_size( act, secondarySize );
2773 ege_select_one_action_set_tooltip_column( act, 1 );
2775 ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2776 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2777 }
2779 /* Magnitude */
2780 {
2781 gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2782 gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2783 eact = create_adjustment_action( "MagnitudeAction",
2784 _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2785 "/tools/shapes/star/magnitude", 3,
2786 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2787 3, 1024, 1, 5,
2788 labels, values, G_N_ELEMENTS(labels),
2789 sp_stb_magnitude_value_changed,
2790 1.0, 0 );
2791 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2792 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2793 }
2795 /* Spoke ratio */
2796 {
2797 gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2798 gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2799 eact = create_adjustment_action( "SpokeAction",
2800 _("Spoke ratio"), _("Spoke ratio:"),
2801 // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2802 // Base radius is the same for the closest handle.
2803 _("Base radius to tip radius ratio"),
2804 "/tools/shapes/star/proportion", 0.5,
2805 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2806 0.01, 1.0, 0.01, 0.1,
2807 labels, values, G_N_ELEMENTS(labels),
2808 sp_stb_proportion_value_changed );
2809 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2810 g_object_set_data( holder, "prop_action", eact );
2811 }
2813 if ( !isFlatSided ) {
2814 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2815 } else {
2816 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2817 }
2819 /* Roundedness */
2820 {
2821 gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2822 gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2823 eact = create_adjustment_action( "RoundednessAction",
2824 _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2825 "/tools/shapes/star/rounded", 0.0,
2826 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2827 -10.0, 10.0, 0.01, 0.1,
2828 labels, values, G_N_ELEMENTS(labels),
2829 sp_stb_rounded_value_changed );
2830 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2831 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2832 }
2834 /* Randomization */
2835 {
2836 gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2837 gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2838 eact = create_adjustment_action( "RandomizationAction",
2839 _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2840 "/tools/shapes/star/randomized", 0.0,
2841 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2842 -10.0, 10.0, 0.001, 0.01,
2843 labels, values, G_N_ELEMENTS(labels),
2844 sp_stb_randomized_value_changed, 0.1, 3 );
2845 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2846 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2847 }
2848 }
2850 {
2851 /* Reset */
2852 {
2853 GtkAction* act = gtk_action_new( "StarResetAction",
2854 _("Defaults"),
2855 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2856 GTK_STOCK_CLEAR );
2857 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2858 gtk_action_group_add_action( mainActions, act );
2859 gtk_action_set_sensitive( act, TRUE );
2860 }
2861 }
2863 sigc::connection *connection = new sigc::connection(
2864 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2865 );
2866 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2867 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2868 }
2871 //########################
2872 //## Rect ##
2873 //########################
2875 static void sp_rtb_sensitivize( GObject *tbl )
2876 {
2877 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2878 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2879 GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2881 if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2882 gtk_action_set_sensitive( not_rounded, FALSE );
2883 } else {
2884 gtk_action_set_sensitive( not_rounded, TRUE );
2885 }
2886 }
2889 static void
2890 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2891 void (*setter)(SPRect *, gdouble))
2892 {
2893 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2895 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2896 SPUnit const *unit = tracker->getActiveUnit();
2898 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2899 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2900 prefs->setDouble(Glib::ustring("/tools/shapes/rect/") + value_name, sp_units_get_pixels(adj->value, *unit));
2901 }
2903 // quit if run by the attr_changed listener
2904 if (g_object_get_data( tbl, "freeze" )) {
2905 return;
2906 }
2908 // in turn, prevent listener from responding
2909 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2911 bool modmade = false;
2912 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2913 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2914 if (SP_IS_RECT(items->data)) {
2915 if (adj->value != 0) {
2916 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2917 } else {
2918 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2919 }
2920 modmade = true;
2921 }
2922 }
2924 sp_rtb_sensitivize( tbl );
2926 if (modmade) {
2927 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2928 _("Change rectangle"));
2929 }
2931 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2932 }
2934 static void
2935 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2936 {
2937 sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2938 }
2940 static void
2941 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2942 {
2943 sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2944 }
2946 static void
2947 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2948 {
2949 sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2950 }
2952 static void
2953 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2954 {
2955 sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2956 }
2960 static void
2961 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2962 {
2963 GtkAdjustment *adj = 0;
2965 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2966 gtk_adjustment_set_value(adj, 0.0);
2967 // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2968 gtk_adjustment_value_changed(adj);
2970 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2971 gtk_adjustment_set_value(adj, 0.0);
2972 gtk_adjustment_value_changed(adj);
2974 sp_rtb_sensitivize( obj );
2975 }
2977 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2978 gchar const */*old_value*/, gchar const */*new_value*/,
2979 bool /*is_interactive*/, gpointer data)
2980 {
2981 GObject *tbl = G_OBJECT(data);
2983 // quit if run by the _changed callbacks
2984 if (g_object_get_data( tbl, "freeze" )) {
2985 return;
2986 }
2988 // in turn, prevent callbacks from responding
2989 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2991 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2992 SPUnit const *unit = tracker->getActiveUnit();
2994 gpointer item = g_object_get_data( tbl, "item" );
2995 if (item && SP_IS_RECT(item)) {
2996 {
2997 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2998 gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2999 gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
3000 }
3002 {
3003 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
3004 gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
3005 gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
3006 }
3008 {
3009 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
3010 gdouble width = sp_rect_get_visible_width (SP_RECT(item));
3011 gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
3012 }
3014 {
3015 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
3016 gdouble height = sp_rect_get_visible_height (SP_RECT(item));
3017 gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
3018 }
3019 }
3021 sp_rtb_sensitivize( tbl );
3023 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3024 }
3027 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
3028 NULL, /* child_added */
3029 NULL, /* child_removed */
3030 rect_tb_event_attr_changed,
3031 NULL, /* content_changed */
3032 NULL /* order_changed */
3033 };
3035 /**
3036 * \param selection should not be NULL.
3037 */
3038 static void
3039 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3040 {
3041 int n_selected = 0;
3042 Inkscape::XML::Node *repr = NULL;
3043 SPItem *item = NULL;
3045 if ( g_object_get_data( tbl, "repr" ) ) {
3046 g_object_set_data( tbl, "item", NULL );
3047 }
3048 purge_repr_listener( tbl, tbl );
3050 for (GSList const *items = selection->itemList();
3051 items != NULL;
3052 items = items->next) {
3053 if (SP_IS_RECT((SPItem *) items->data)) {
3054 n_selected++;
3055 item = (SPItem *) items->data;
3056 repr = SP_OBJECT_REPR(item);
3057 }
3058 }
3060 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3062 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3064 if (n_selected == 0) {
3065 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3067 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3068 gtk_action_set_sensitive(w, FALSE);
3069 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3070 gtk_action_set_sensitive(h, FALSE);
3072 } else if (n_selected == 1) {
3073 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3074 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3076 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3077 gtk_action_set_sensitive(w, TRUE);
3078 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3079 gtk_action_set_sensitive(h, TRUE);
3081 if (repr) {
3082 g_object_set_data( tbl, "repr", repr );
3083 g_object_set_data( tbl, "item", item );
3084 Inkscape::GC::anchor(repr);
3085 sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
3086 sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
3087 }
3088 } else {
3089 // FIXME: implement averaging of all parameters for multiple selected
3090 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3091 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3092 sp_rtb_sensitivize( tbl );
3093 }
3094 }
3097 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3098 {
3099 EgeAdjustmentAction* eact = 0;
3100 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3102 {
3103 EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
3104 ege_output_action_set_use_markup( act, TRUE );
3105 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3106 g_object_set_data( holder, "mode_action", act );
3107 }
3109 // rx/ry units menu: create
3110 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
3111 //tracker->addUnit( SP_UNIT_PERCENT, 0 );
3112 // fixme: add % meaning per cent of the width/height
3113 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
3114 g_object_set_data( holder, "tracker", tracker );
3116 /* W */
3117 {
3118 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3119 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3120 eact = create_adjustment_action( "RectWidthAction",
3121 _("Width"), _("W:"), _("Width of rectangle"),
3122 "/tools/shapes/rect/width", 0,
3123 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
3124 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3125 labels, values, G_N_ELEMENTS(labels),
3126 sp_rtb_width_value_changed );
3127 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3128 g_object_set_data( holder, "width_action", eact );
3129 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3130 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3131 }
3133 /* H */
3134 {
3135 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3136 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3137 eact = create_adjustment_action( "RectHeightAction",
3138 _("Height"), _("H:"), _("Height of rectangle"),
3139 "/tools/shapes/rect/height", 0,
3140 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3141 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3142 labels, values, G_N_ELEMENTS(labels),
3143 sp_rtb_height_value_changed );
3144 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3145 g_object_set_data( holder, "height_action", eact );
3146 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3147 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3148 }
3150 /* rx */
3151 {
3152 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3153 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3154 eact = create_adjustment_action( "RadiusXAction",
3155 _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
3156 "/tools/shapes/rect/rx", 0,
3157 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3158 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3159 labels, values, G_N_ELEMENTS(labels),
3160 sp_rtb_rx_value_changed);
3161 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3162 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3163 }
3165 /* ry */
3166 {
3167 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3168 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3169 eact = create_adjustment_action( "RadiusYAction",
3170 _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
3171 "/tools/shapes/rect/ry", 0,
3172 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3173 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3174 labels, values, G_N_ELEMENTS(labels),
3175 sp_rtb_ry_value_changed);
3176 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3177 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3178 }
3180 // add the units menu
3181 {
3182 GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
3183 gtk_action_group_add_action( mainActions, act );
3184 }
3186 /* Reset */
3187 {
3188 InkAction* inky = ink_action_new( "RectResetAction",
3189 _("Not rounded"),
3190 _("Make corners sharp"),
3191 INKSCAPE_ICON_RECTANGLE_MAKE_CORNERS_SHARP,
3192 secondarySize );
3193 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
3194 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3195 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3196 g_object_set_data( holder, "not_rounded", inky );
3197 }
3199 g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
3200 sp_rtb_sensitivize( holder );
3202 sigc::connection *connection = new sigc::connection(
3203 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
3204 );
3205 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3206 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3207 }
3209 //########################
3210 //## 3D Box ##
3211 //########################
3213 // normalize angle so that it lies in the interval [0,360]
3214 static double box3d_normalize_angle (double a) {
3215 double angle = a + ((int) (a/360.0))*360;
3216 if (angle < 0) {
3217 angle += 360.0;
3218 }
3219 return angle;
3220 }
3222 static void
3223 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
3224 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
3225 // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
3226 // have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
3227 // are reset).
3228 bool is_infinite = !persp3d_VP_is_finite(persp->perspective_impl, axis);
3230 if (is_infinite) {
3231 gtk_toggle_action_set_active(tact, TRUE);
3232 gtk_action_set_sensitive(act, TRUE);
3234 double angle = persp3d_get_infinite_angle(persp, axis);
3235 if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
3236 gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
3237 }
3238 } else {
3239 gtk_toggle_action_set_active(tact, FALSE);
3240 gtk_action_set_sensitive(act, FALSE);
3241 }
3242 }
3244 static void
3245 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
3246 if (!persp_repr) {
3247 g_print ("No perspective given to box3d_resync_toolbar().\n");
3248 return;
3249 }
3251 GtkWidget *tbl = GTK_WIDGET(data);
3252 GtkAdjustment *adj = 0;
3253 GtkAction *act = 0;
3254 GtkToggleAction *tact = 0;
3255 Persp3D *persp = persp3d_get_from_repr(persp_repr);
3256 if (!persp) {
3257 // Hmm, is it an error if this happens?
3258 return;
3259 }
3260 {
3261 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
3262 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
3263 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
3265 box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
3266 }
3267 {
3268 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
3269 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
3270 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
3272 box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
3273 }
3274 {
3275 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
3276 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
3277 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
3279 box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
3280 }
3281 }
3283 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3284 gchar const */*old_value*/, gchar const */*new_value*/,
3285 bool /*is_interactive*/, gpointer data)
3286 {
3287 GtkWidget *tbl = GTK_WIDGET(data);
3289 // quit if run by the attr_changed or selection changed listener
3290 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3291 return;
3292 }
3294 // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
3295 // sp_document_maybe_done() when the document is undo insensitive)
3296 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3298 // TODO: Only update the appropriate part of the toolbar
3299 // if (!strcmp(name, "inkscape:vp_z")) {
3300 box3d_resync_toolbar(repr, G_OBJECT(tbl));
3301 // }
3303 Persp3D *persp = persp3d_get_from_repr(repr);
3304 persp3d_update_box_reprs(persp);
3306 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3307 }
3309 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
3310 {
3311 NULL, /* child_added */
3312 NULL, /* child_removed */
3313 box3d_persp_tb_event_attr_changed,
3314 NULL, /* content_changed */
3315 NULL /* order_changed */
3316 };
3318 /**
3319 * \param selection Should not be NULL.
3320 */
3321 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
3322 // Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
3323 static void
3324 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3325 {
3326 // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
3327 // disable the angle entry fields for this direction (otherwise entering a value in them should only
3328 // update the perspectives with infinite VPs and leave the other ones untouched).
3330 Inkscape::XML::Node *persp_repr = NULL;
3331 purge_repr_listener(tbl, tbl);
3333 SPItem *item = selection->singleItem();
3334 if (item && SP_IS_BOX3D(item)) {
3335 // FIXME: Also deal with multiple selected boxes
3336 SPBox3D *box = SP_BOX3D(item);
3337 Persp3D *persp = box3d_get_perspective(box);
3338 persp_repr = SP_OBJECT_REPR(persp);
3339 if (persp_repr) {
3340 g_object_set_data(tbl, "repr", persp_repr);
3341 Inkscape::GC::anchor(persp_repr);
3342 sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
3343 sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
3344 }
3346 inkscape_active_document()->setCurrentPersp3D(persp3d_get_from_repr(persp_repr));
3347 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3348 prefs->setString("/tools/shapes/3dbox/persp", persp_repr->attribute("id"));
3350 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
3351 box3d_resync_toolbar(persp_repr, tbl);
3352 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
3353 }
3354 }
3356 static void
3357 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
3358 {
3359 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3360 SPDocument *document = sp_desktop_document(desktop);
3362 // quit if run by the attr_changed or selection changed listener
3363 if (g_object_get_data( dataKludge, "freeze" )) {
3364 return;
3365 }
3367 // in turn, prevent listener from responding
3368 g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
3370 std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
3371 if (sel_persps.empty()) {
3372 // this can happen when the document is created; we silently ignore it
3373 return;
3374 }
3375 Persp3D *persp = sel_persps.front();
3377 persp->perspective_impl->tmat.set_infinite_direction (axis, adj->value);
3378 SP_OBJECT(persp)->updateRepr();
3380 // TODO: use the correct axis here, too
3381 sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
3383 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
3384 }
3387 static void
3388 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3389 {
3390 box3d_angle_value_changed(adj, dataKludge, Proj::X);
3391 }
3393 static void
3394 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3395 {
3396 box3d_angle_value_changed(adj, dataKludge, Proj::Y);
3397 }
3399 static void
3400 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3401 {
3402 box3d_angle_value_changed(adj, dataKludge, Proj::Z);
3403 }
3406 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
3407 {
3408 // TODO: Take all selected perspectives into account
3409 std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
3410 if (sel_persps.empty()) {
3411 // this can happen when the document is created; we silently ignore it
3412 return;
3413 }
3414 Persp3D *persp = sel_persps.front();
3416 bool set_infinite = gtk_toggle_action_get_active(act);
3417 persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
3418 }
3420 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3421 {
3422 box3d_vp_state_changed(act, box3d_angle, Proj::X);
3423 }
3425 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3426 {
3427 box3d_vp_state_changed(act, box3d_angle, Proj::Y);
3428 }
3430 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3431 {
3432 box3d_vp_state_changed(act, box3d_angle, Proj::Z);
3433 }
3435 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3436 {
3437 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3438 EgeAdjustmentAction* eact = 0;
3439 SPDocument *document = sp_desktop_document (desktop);
3440 Persp3DImpl *persp_impl = document->getCurrentPersp3DImpl();
3442 EgeAdjustmentAction* box3d_angle_x = 0;
3443 EgeAdjustmentAction* box3d_angle_y = 0;
3444 EgeAdjustmentAction* box3d_angle_z = 0;
3446 /* Angle X */
3447 {
3448 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3449 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3450 eact = create_adjustment_action( "3DBoxAngleXAction",
3451 _("Angle in X direction"), _("Angle X:"),
3452 // Translators: PL is short for 'perspective line'
3453 _("Angle of PLs in X direction"),
3454 "/tools/shapes/3dbox/box3d_angle_x", 30,
3455 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
3456 -360.0, 360.0, 1.0, 10.0,
3457 labels, values, G_N_ELEMENTS(labels),
3458 box3d_angle_x_value_changed );
3459 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3460 g_object_set_data( holder, "box3d_angle_x_action", eact );
3461 box3d_angle_x = eact;
3462 }
3464 if (!persp3d_VP_is_finite(persp_impl, Proj::X)) {
3465 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3466 } else {
3467 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3468 }
3471 /* VP X state */
3472 {
3473 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
3474 // Translators: VP is short for 'vanishing point'
3475 _("State of VP in X direction"),
3476 _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
3477 INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3478 Inkscape::ICON_SIZE_DECORATION );
3479 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3480 g_object_set_data( holder, "box3d_vp_x_state_action", act );
3481 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
3482 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3483 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3484 }
3486 /* Angle Y */
3487 {
3488 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3489 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3490 eact = create_adjustment_action( "3DBoxAngleYAction",
3491 _("Angle in Y direction"), _("Angle Y:"),
3492 // Translators: PL is short for 'perspective line'
3493 _("Angle of PLs in Y direction"),
3494 "/tools/shapes/3dbox/box3d_angle_y", 30,
3495 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3496 -360.0, 360.0, 1.0, 10.0,
3497 labels, values, G_N_ELEMENTS(labels),
3498 box3d_angle_y_value_changed );
3499 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3500 g_object_set_data( holder, "box3d_angle_y_action", eact );
3501 box3d_angle_y = eact;
3502 }
3504 if (!persp3d_VP_is_finite(persp_impl, Proj::Y)) {
3505 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3506 } else {
3507 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3508 }
3510 /* VP Y state */
3511 {
3512 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
3513 // Translators: VP is short for 'vanishing point'
3514 _("State of VP in Y direction"),
3515 _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
3516 INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3517 Inkscape::ICON_SIZE_DECORATION );
3518 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3519 g_object_set_data( holder, "box3d_vp_y_state_action", act );
3520 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
3521 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3522 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3523 }
3525 /* Angle Z */
3526 {
3527 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3528 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3529 eact = create_adjustment_action( "3DBoxAngleZAction",
3530 _("Angle in Z direction"), _("Angle Z:"),
3531 // Translators: PL is short for 'perspective line'
3532 _("Angle of PLs in Z direction"),
3533 "/tools/shapes/3dbox/box3d_angle_z", 30,
3534 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3535 -360.0, 360.0, 1.0, 10.0,
3536 labels, values, G_N_ELEMENTS(labels),
3537 box3d_angle_z_value_changed );
3538 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3539 g_object_set_data( holder, "box3d_angle_z_action", eact );
3540 box3d_angle_z = eact;
3541 }
3543 if (!persp3d_VP_is_finite(persp_impl, Proj::Z)) {
3544 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3545 } else {
3546 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3547 }
3549 /* VP Z state */
3550 {
3551 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3552 // Translators: VP is short for 'vanishing point'
3553 _("State of VP in Z direction"),
3554 _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3555 INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3556 Inkscape::ICON_SIZE_DECORATION );
3557 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3558 g_object_set_data( holder, "box3d_vp_z_state_action", act );
3559 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3560 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3561 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3562 }
3564 sigc::connection *connection = new sigc::connection(
3565 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3566 );
3567 g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3568 g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3569 }
3571 //########################
3572 //## Spiral ##
3573 //########################
3575 static void
3576 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, Glib::ustring const &value_name)
3577 {
3578 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3580 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3581 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3582 prefs->setDouble("/tools/shapes/spiral/" + value_name, adj->value);
3583 }
3585 // quit if run by the attr_changed listener
3586 if (g_object_get_data( tbl, "freeze" )) {
3587 return;
3588 }
3590 // in turn, prevent listener from responding
3591 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3593 gchar* namespaced_name = g_strconcat("sodipodi:", value_name.data(), NULL);
3595 bool modmade = false;
3596 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3597 items != NULL;
3598 items = items->next)
3599 {
3600 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3601 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3602 sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3603 SP_OBJECT((SPItem *) items->data)->updateRepr();
3604 modmade = true;
3605 }
3606 }
3608 g_free(namespaced_name);
3610 if (modmade) {
3611 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3612 _("Change spiral"));
3613 }
3615 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3616 }
3618 static void
3619 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3620 {
3621 sp_spl_tb_value_changed(adj, tbl, "revolution");
3622 }
3624 static void
3625 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3626 {
3627 sp_spl_tb_value_changed(adj, tbl, "expansion");
3628 }
3630 static void
3631 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3632 {
3633 sp_spl_tb_value_changed(adj, tbl, "t0");
3634 }
3636 static void
3637 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3638 {
3639 GtkWidget *tbl = GTK_WIDGET(obj);
3641 GtkAdjustment *adj;
3643 // fixme: make settable
3644 gdouble rev = 5;
3645 gdouble exp = 1.0;
3646 gdouble t0 = 0.0;
3648 adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3649 gtk_adjustment_set_value(adj, rev);
3650 gtk_adjustment_value_changed(adj);
3652 adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3653 gtk_adjustment_set_value(adj, exp);
3654 gtk_adjustment_value_changed(adj);
3656 adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3657 gtk_adjustment_set_value(adj, t0);
3658 gtk_adjustment_value_changed(adj);
3660 spinbutton_defocus(GTK_OBJECT(tbl));
3661 }
3664 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3665 gchar const */*old_value*/, gchar const */*new_value*/,
3666 bool /*is_interactive*/, gpointer data)
3667 {
3668 GtkWidget *tbl = GTK_WIDGET(data);
3670 // quit if run by the _changed callbacks
3671 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3672 return;
3673 }
3675 // in turn, prevent callbacks from responding
3676 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3678 GtkAdjustment *adj;
3679 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3680 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3682 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3683 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3685 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3686 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3688 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3689 }
3692 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3693 NULL, /* child_added */
3694 NULL, /* child_removed */
3695 spiral_tb_event_attr_changed,
3696 NULL, /* content_changed */
3697 NULL /* order_changed */
3698 };
3700 static void
3701 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3702 {
3703 int n_selected = 0;
3704 Inkscape::XML::Node *repr = NULL;
3706 purge_repr_listener( tbl, tbl );
3708 for (GSList const *items = selection->itemList();
3709 items != NULL;
3710 items = items->next)
3711 {
3712 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3713 n_selected++;
3714 repr = SP_OBJECT_REPR((SPItem *) items->data);
3715 }
3716 }
3718 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3720 if (n_selected == 0) {
3721 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3722 } else if (n_selected == 1) {
3723 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3725 if (repr) {
3726 g_object_set_data( tbl, "repr", repr );
3727 Inkscape::GC::anchor(repr);
3728 sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3729 sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3730 }
3731 } else {
3732 // FIXME: implement averaging of all parameters for multiple selected
3733 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3734 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3735 }
3736 }
3739 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3740 {
3741 EgeAdjustmentAction* eact = 0;
3742 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3744 {
3745 EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3746 ege_output_action_set_use_markup( act, TRUE );
3747 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3748 g_object_set_data( holder, "mode_action", act );
3749 }
3751 /* Revolution */
3752 {
3753 gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3754 gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3755 eact = create_adjustment_action( "SpiralRevolutionAction",
3756 _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3757 "/tools/shapes/spiral/revolution", 3.0,
3758 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3759 0.01, 1024.0, 0.1, 1.0,
3760 labels, values, G_N_ELEMENTS(labels),
3761 sp_spl_tb_revolution_value_changed, 1, 2);
3762 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3763 }
3765 /* Expansion */
3766 {
3767 gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3768 gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3769 eact = create_adjustment_action( "SpiralExpansionAction",
3770 _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3771 "/tools/shapes/spiral/expansion", 1.0,
3772 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3773 0.0, 1000.0, 0.01, 1.0,
3774 labels, values, G_N_ELEMENTS(labels),
3775 sp_spl_tb_expansion_value_changed);
3776 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3777 }
3779 /* T0 */
3780 {
3781 gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3782 gdouble values[] = {0, 0.5, 0.9};
3783 eact = create_adjustment_action( "SpiralT0Action",
3784 _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3785 "/tools/shapes/spiral/t0", 0.0,
3786 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3787 0.0, 0.999, 0.01, 1.0,
3788 labels, values, G_N_ELEMENTS(labels),
3789 sp_spl_tb_t0_value_changed);
3790 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3791 }
3793 /* Reset */
3794 {
3795 InkAction* inky = ink_action_new( "SpiralResetAction",
3796 _("Defaults"),
3797 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3798 GTK_STOCK_CLEAR,
3799 secondarySize );
3800 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3801 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3802 }
3805 sigc::connection *connection = new sigc::connection(
3806 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3807 );
3808 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3809 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3810 }
3812 //########################
3813 //## Pen/Pencil ##
3814 //########################
3816 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3817 static Glib::ustring const
3818 freehand_tool_name(GObject *dataKludge)
3819 {
3820 SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3821 return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3822 ? "/tools/freehand/pen"
3823 : "/tools/freehand/pencil" );
3824 }
3826 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3827 {
3828 gint mode = ege_select_one_action_get_active(act);
3830 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3831 prefs->setInt(freehand_tool_name(tbl) + "/freehand-mode", mode);
3833 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3835 // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3836 // preparatory work here
3837 if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3838 SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3839 sp_pen_context_set_polyline_mode(pc);
3840 }
3841 }
3843 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3844 {
3845 /* Freehand mode toggle buttons */
3846 {
3847 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3848 guint freehandMode = prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/freehand-mode" : "/tools/freehand/pen/freehand-mode" ), 0);
3849 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3851 {
3852 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3854 GtkTreeIter iter;
3855 gtk_list_store_append( model, &iter );
3856 gtk_list_store_set( model, &iter,
3857 0, _("Bezier"),
3858 1, _("Create regular Bezier path"),
3859 2, INKSCAPE_ICON_PATH_MODE_BEZIER,
3860 -1 );
3862 gtk_list_store_append( model, &iter );
3863 gtk_list_store_set( model, &iter,
3864 0, _("Spiro"),
3865 1, _("Create Spiro path"),
3866 2, INKSCAPE_ICON_PATH_MODE_SPIRO,
3867 -1 );
3869 if (!tool_is_pencil) {
3870 gtk_list_store_append( model, &iter );
3871 gtk_list_store_set( model, &iter,
3872 0, _("Zigzag"),
3873 1, _("Create a sequence of straight line segments"),
3874 2, INKSCAPE_ICON_PATH_MODE_POLYLINE,
3875 -1 );
3877 gtk_list_store_append( model, &iter );
3878 gtk_list_store_set( model, &iter,
3879 0, _("Paraxial"),
3880 1, _("Create a sequence of paraxial line segments"),
3881 2, INKSCAPE_ICON_PATH_MODE_POLYLINE_PARAXIAL,
3882 -1 );
3883 }
3885 EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3886 "FreehandModeActionPencil" :
3887 "FreehandModeActionPen",
3888 (_("Mode:")), (_("Mode of new lines drawn by this tool")), NULL, GTK_TREE_MODEL(model) );
3889 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3891 ege_select_one_action_set_appearance( act, "full" );
3892 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3893 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3894 ege_select_one_action_set_icon_column( act, 2 );
3895 ege_select_one_action_set_icon_size( act, secondarySize );
3896 ege_select_one_action_set_tooltip_column( act, 1 );
3898 ege_select_one_action_set_active( act, freehandMode);
3899 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3900 }
3901 }
3902 }
3904 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3905 gint shape = ege_select_one_action_get_active( act );
3906 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3907 prefs->setInt(freehand_tool_name(dataKludge) + "/shape", shape);
3908 }
3910 /**
3911 * \brief Generate the list of freehand advanced shape option entries.
3912 */
3913 GList * freehand_shape_dropdown_items_list() {
3914 GList *glist = NULL;
3916 glist = g_list_append (glist, _("None"));
3917 glist = g_list_append (glist, _("Triangle in"));
3918 glist = g_list_append (glist, _("Triangle out"));
3919 glist = g_list_append (glist, _("Ellipse"));
3920 glist = g_list_append (glist, _("From clipboard"));
3922 return glist;
3923 }
3925 static void
3926 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3927 /*advanced shape options */
3928 {
3929 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3930 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3932 GList* items = 0;
3933 gint count = 0;
3934 for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3935 {
3936 GtkTreeIter iter;
3937 gtk_list_store_append( model, &iter );
3938 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3939 count++;
3940 }
3941 g_list_free( items );
3942 items = 0;
3943 EgeSelectOneAction* act1 = ege_select_one_action_new(
3944 tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3945 _("Shape:"), (_("Shape of new paths drawn by this tool")), NULL, GTK_TREE_MODEL(model));
3946 g_object_set( act1, "short_label", _("Shape:"), NULL );
3947 ege_select_one_action_set_appearance( act1, "compact" );
3948 ege_select_one_action_set_active( act1, prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/shape" : "/tools/freehand/pen/shape" ), 0) );
3949 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3950 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3951 g_object_set_data( holder, "shape_action", act1 );
3952 }
3953 }
3955 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3956 {
3957 sp_add_freehand_mode_toggle(mainActions, holder, false);
3958 freehand_add_advanced_shape_options(mainActions, holder, false);
3959 }
3962 static void
3963 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3964 {
3965 GtkWidget *tbl = GTK_WIDGET(obj);
3967 GtkAdjustment *adj;
3969 // fixme: make settable
3970 gdouble tolerance = 4;
3972 adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3973 gtk_adjustment_set_value(adj, tolerance);
3974 gtk_adjustment_value_changed(adj);
3976 spinbutton_defocus(GTK_OBJECT(tbl));
3977 }
3979 static void
3980 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3981 {
3982 // quit if run by the attr_changed listener
3983 if (g_object_get_data( tbl, "freeze" )) {
3984 return;
3985 }
3986 // in turn, prevent listener from responding
3987 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3988 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3989 prefs->setDouble("/tools/freehand/pencil/tolerance", adj->value);
3990 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3991 }
3993 /*
3994 class PencilToleranceObserver : public Inkscape::Preferences::Observer {
3995 public:
3996 PencilToleranceObserver(Glib::ustring const &path, GObject *x) : Observer(path), _obj(x)
3997 {
3998 g_object_set_data(_obj, "prefobserver", this);
3999 }
4000 virtual ~PencilToleranceObserver() {
4001 if (g_object_get_data(_obj, "prefobserver") == this) {
4002 g_object_set_data(_obj, "prefobserver", NULL);
4003 }
4004 }
4005 virtual void notify(Inkscape::Preferences::Entry const &val) {
4006 GObject* tbl = _obj;
4007 if (g_object_get_data( tbl, "freeze" )) {
4008 return;
4009 }
4010 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4012 GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl, "tolerance");
4014 double v = val.getDouble(adj->value);
4015 gtk_adjustment_set_value(adj, v);
4016 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4017 }
4018 private:
4019 GObject *_obj;
4020 };
4021 */
4023 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4024 {
4025 sp_add_freehand_mode_toggle(mainActions, holder, true);
4027 EgeAdjustmentAction* eact = 0;
4029 /* Tolerance */
4030 {
4031 gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
4032 gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
4033 eact = create_adjustment_action( "PencilToleranceAction",
4034 _("Smoothing:"), _("Smoothing: "),
4035 _("How much smoothing (simplifying) is applied to the line"),
4036 "/tools/freehand/pencil/tolerance",
4037 3.0,
4038 GTK_WIDGET(desktop->canvas), NULL,
4039 holder, TRUE, "altx-pencil",
4040 1, 100.0, 0.5, 1.0,
4041 labels, values, G_N_ELEMENTS(labels),
4042 sp_pencil_tb_tolerance_value_changed,
4043 1, 2);
4044 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4045 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4046 }
4048 /* advanced shape options */
4049 freehand_add_advanced_shape_options(mainActions, holder, true);
4051 /* Reset */
4052 {
4053 InkAction* inky = ink_action_new( "PencilResetAction",
4054 _("Defaults"),
4055 _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
4056 GTK_STOCK_CLEAR,
4057 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4058 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
4059 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4060 }
4062 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4064 }
4067 //########################
4068 //## Tweak ##
4069 //########################
4071 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4072 {
4073 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4074 prefs->setDouble( "/tools/tweak/width", adj->value * 0.01 );
4075 }
4077 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4078 {
4079 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4080 prefs->setDouble( "/tools/tweak/force", adj->value * 0.01 );
4081 }
4083 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4084 {
4085 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4086 prefs->setBool("/tools/tweak/usepressure", gtk_toggle_action_get_active(act));
4087 }
4089 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4090 {
4091 int mode = ege_select_one_action_get_active( act );
4092 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4093 prefs->setInt("/tools/tweak/mode", mode);
4095 GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
4096 GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
4097 GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
4098 GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
4099 GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
4100 GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
4101 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
4102 if (doh) gtk_action_set_sensitive (doh, TRUE);
4103 if (dos) gtk_action_set_sensitive (dos, TRUE);
4104 if (dol) gtk_action_set_sensitive (dol, TRUE);
4105 if (doo) gtk_action_set_sensitive (doo, TRUE);
4106 if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
4107 if (fid) gtk_action_set_sensitive (fid, FALSE);
4108 } else {
4109 if (doh) gtk_action_set_sensitive (doh, FALSE);
4110 if (dos) gtk_action_set_sensitive (dos, FALSE);
4111 if (dol) gtk_action_set_sensitive (dol, FALSE);
4112 if (doo) gtk_action_set_sensitive (doo, FALSE);
4113 if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
4114 if (fid) gtk_action_set_sensitive (fid, TRUE);
4115 }
4116 }
4118 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4119 {
4120 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4121 prefs->setDouble( "/tools/tweak/fidelity", adj->value * 0.01 );
4122 }
4124 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
4125 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4126 prefs->setBool("/tools/tweak/doh", gtk_toggle_action_get_active(act));
4127 }
4128 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
4129 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4130 prefs->setBool("/tools/tweak/dos", gtk_toggle_action_get_active(act));
4131 }
4132 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
4133 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4134 prefs->setBool("/tools/tweak/dol", gtk_toggle_action_get_active(act));
4135 }
4136 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
4137 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4138 prefs->setBool("/tools/tweak/doo", gtk_toggle_action_get_active(act));
4139 }
4141 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4142 {
4143 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
4144 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4146 {
4147 /* Width */
4148 gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
4149 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4150 EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
4151 _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
4152 "/tools/tweak/width", 15,
4153 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
4154 1, 100, 1.0, 10.0,
4155 labels, values, G_N_ELEMENTS(labels),
4156 sp_tweak_width_value_changed, 0.01, 0, 100 );
4157 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4158 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4159 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4160 }
4163 {
4164 /* Force */
4165 gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
4166 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4167 EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
4168 _("Force"), _("Force:"), _("The force of the tweak action"),
4169 "/tools/tweak/force", 20,
4170 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
4171 1, 100, 1.0, 10.0,
4172 labels, values, G_N_ELEMENTS(labels),
4173 sp_tweak_force_value_changed, 0.01, 0, 100 );
4174 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4175 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4176 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4177 }
4179 /* Mode */
4180 {
4181 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4183 GtkTreeIter iter;
4184 gtk_list_store_append( model, &iter );
4185 gtk_list_store_set( model, &iter,
4186 0, _("Move mode"),
4187 1, _("Move objects in any direction"),
4188 2, INKSCAPE_ICON_OBJECT_TWEAK_PUSH,
4189 -1 );
4191 gtk_list_store_append( model, &iter );
4192 gtk_list_store_set( model, &iter,
4193 0, _("Move in/out mode"),
4194 1, _("Move objects towards cursor; with Shift from cursor"),
4195 2, INKSCAPE_ICON_OBJECT_TWEAK_ATTRACT,
4196 -1 );
4198 gtk_list_store_append( model, &iter );
4199 gtk_list_store_set( model, &iter,
4200 0, _("Move jitter mode"),
4201 1, _("Move objects in random directions"),
4202 2, INKSCAPE_ICON_OBJECT_TWEAK_RANDOMIZE,
4203 -1 );
4205 gtk_list_store_append( model, &iter );
4206 gtk_list_store_set( model, &iter,
4207 0, _("Scale mode"),
4208 1, _("Shrink objects, with Shift enlarge"),
4209 2, INKSCAPE_ICON_OBJECT_TWEAK_SHRINK,
4210 -1 );
4212 gtk_list_store_append( model, &iter );
4213 gtk_list_store_set( model, &iter,
4214 0, _("Rotate mode"),
4215 1, _("Rotate objects, with Shift counterclockwise"),
4216 2, INKSCAPE_ICON_OBJECT_TWEAK_ROTATE,
4217 -1 );
4219 gtk_list_store_append( model, &iter );
4220 gtk_list_store_set( model, &iter,
4221 0, _("Duplicate/delete mode"),
4222 1, _("Duplicate objects, with Shift delete"),
4223 2, INKSCAPE_ICON_OBJECT_TWEAK_DUPLICATE,
4224 -1 );
4226 gtk_list_store_append( model, &iter );
4227 gtk_list_store_set( model, &iter,
4228 0, _("Push mode"),
4229 1, _("Push parts of paths in any direction"),
4230 2, INKSCAPE_ICON_PATH_TWEAK_PUSH,
4231 -1 );
4233 gtk_list_store_append( model, &iter );
4234 gtk_list_store_set( model, &iter,
4235 0, _("Shrink/grow mode"),
4236 1, _("Shrink (inset) parts of paths; with Shift grow (outset)"),
4237 2, INKSCAPE_ICON_PATH_TWEAK_SHRINK,
4238 -1 );
4240 gtk_list_store_append( model, &iter );
4241 gtk_list_store_set( model, &iter,
4242 0, _("Attract/repel mode"),
4243 1, _("Attract parts of paths towards cursor; with Shift from cursor"),
4244 2, INKSCAPE_ICON_PATH_TWEAK_ATTRACT,
4245 -1 );
4247 gtk_list_store_append( model, &iter );
4248 gtk_list_store_set( model, &iter,
4249 0, _("Roughen mode"),
4250 1, _("Roughen parts of paths"),
4251 2, INKSCAPE_ICON_PATH_TWEAK_ROUGHEN,
4252 -1 );
4254 gtk_list_store_append( model, &iter );
4255 gtk_list_store_set( model, &iter,
4256 0, _("Color paint mode"),
4257 1, _("Paint the tool's color upon selected objects"),
4258 2, INKSCAPE_ICON_OBJECT_TWEAK_PAINT,
4259 -1 );
4261 gtk_list_store_append( model, &iter );
4262 gtk_list_store_set( model, &iter,
4263 0, _("Color jitter mode"),
4264 1, _("Jitter the colors of selected objects"),
4265 2, INKSCAPE_ICON_OBJECT_TWEAK_JITTER_COLOR,
4266 -1 );
4268 gtk_list_store_append( model, &iter );
4269 gtk_list_store_set( model, &iter,
4270 0, _("Blur mode"),
4271 1, _("Blur selected objects more; with Shift, blur less"),
4272 2, INKSCAPE_ICON_OBJECT_TWEAK_BLUR,
4273 -1 );
4276 EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4277 g_object_set( act, "short_label", _("Mode:"), NULL );
4278 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4279 g_object_set_data( holder, "mode_action", act );
4281 ege_select_one_action_set_appearance( act, "full" );
4282 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4283 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4284 ege_select_one_action_set_icon_column( act, 2 );
4285 ege_select_one_action_set_icon_size( act, secondarySize );
4286 ege_select_one_action_set_tooltip_column( act, 1 );
4288 gint mode = prefs->getInt("/tools/tweak/mode", 0);
4289 ege_select_one_action_set_active( act, mode );
4290 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
4292 g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
4293 }
4295 guint mode = prefs->getInt("/tools/tweak/mode", 0);
4297 {
4298 EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
4299 ege_output_action_set_use_markup( act, TRUE );
4300 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4301 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4302 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4303 g_object_set_data( holder, "tweak_channels_label", act);
4304 }
4306 {
4307 InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
4308 _("Hue"),
4309 _("In color mode, act on objects' hue"),
4310 NULL,
4311 Inkscape::ICON_SIZE_DECORATION );
4312 //TRANSLATORS: "H" here stands for hue
4313 g_object_set( act, "short_label", _("H"), NULL );
4314 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4315 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
4316 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doh", true) );
4317 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4318 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4319 g_object_set_data( holder, "tweak_doh", act);
4320 }
4321 {
4322 InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
4323 _("Saturation"),
4324 _("In color mode, act on objects' saturation"),
4325 NULL,
4326 Inkscape::ICON_SIZE_DECORATION );
4327 //TRANSLATORS: "S" here stands for Saturation
4328 g_object_set( act, "short_label", _("S"), NULL );
4329 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4330 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
4331 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dos", true) );
4332 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4333 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4334 g_object_set_data( holder, "tweak_dos", act );
4335 }
4336 {
4337 InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
4338 _("Lightness"),
4339 _("In color mode, act on objects' lightness"),
4340 NULL,
4341 Inkscape::ICON_SIZE_DECORATION );
4342 //TRANSLATORS: "L" here stands for Lightness
4343 g_object_set( act, "short_label", _("L"), NULL );
4344 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4345 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
4346 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dol", true) );
4347 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4348 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4349 g_object_set_data( holder, "tweak_dol", act );
4350 }
4351 {
4352 InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
4353 _("Opacity"),
4354 _("In color mode, act on objects' opacity"),
4355 NULL,
4356 Inkscape::ICON_SIZE_DECORATION );
4357 //TRANSLATORS: "O" here stands for Opacity
4358 g_object_set( act, "short_label", _("O"), NULL );
4359 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4360 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
4361 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doo", true) );
4362 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4363 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4364 g_object_set_data( holder, "tweak_doo", act );
4365 }
4367 { /* Fidelity */
4368 gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
4369 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4370 EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
4371 _("Fidelity"), _("Fidelity:"),
4372 _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
4373 "/tools/tweak/fidelity", 50,
4374 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
4375 1, 100, 1.0, 10.0,
4376 labels, values, G_N_ELEMENTS(labels),
4377 sp_tweak_fidelity_value_changed, 0.01, 0, 100 );
4378 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4379 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4380 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
4381 gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
4382 g_object_set_data( holder, "tweak_fidelity", eact );
4383 }
4386 /* Use Pressure button */
4387 {
4388 InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
4389 _("Pressure"),
4390 _("Use the pressure of the input device to alter the force of tweak action"),
4391 INKSCAPE_ICON_DRAW_USE_PRESSURE,
4392 Inkscape::ICON_SIZE_DECORATION );
4393 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4394 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
4395 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/usepressure", true) );
4396 }
4398 }
4401 //########################
4402 //## Spray ##
4403 //########################
4405 static void sp_spray_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4406 {
4407 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4408 prefs->setDouble( "/tools/spray/width", adj->value );
4409 }
4411 static void sp_spray_mean_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4412 {
4413 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4414 prefs->setDouble( "/tools/spray/mean", adj->value );
4415 }
4417 static void sp_spray_standard_deviation_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4418 {
4419 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4420 prefs->setDouble( "/tools/spray/standard_deviation", adj->value );
4421 }
4423 static void sp_spray_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4424 {
4425 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4426 prefs->setBool("/tools/spray/usepressure", gtk_toggle_action_get_active(act));
4427 }
4429 static void sp_spray_mode_changed( EgeSelectOneAction *act, GObject */*tbl*/ )
4430 {
4431 int mode = ege_select_one_action_get_active( act );
4432 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4433 prefs->setInt("/tools/spray/mode", mode);
4434 }
4436 static void sp_spray_population_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4437 {
4438 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4439 prefs->setDouble( "/tools/spray/population", adj->value );
4440 }
4442 static void sp_spray_rotation_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4443 {
4444 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4445 prefs->setDouble( "/tools/spray/rotation_variation", adj->value );
4446 }
4448 static void sp_spray_scale_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4449 {
4450 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4451 prefs->setDouble( "/tools/spray/scale_variation", adj->value );
4452 }
4455 static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4456 {
4457 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
4458 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4460 {
4461 /* Width */
4462 gchar const* labels[] = {_("(narrow spray)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad spray)")};
4463 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4464 EgeAdjustmentAction *eact = create_adjustment_action( "SprayWidthAction",
4465 _("Width"), _("Width:"), _("The width of the spray area (relative to the visible canvas area)"),
4466 "/tools/spray/width", 15,
4467 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spray",
4468 1, 100, 1.0, 10.0,
4469 labels, values, G_N_ELEMENTS(labels),
4470 sp_spray_width_value_changed, 1, 0 );
4471 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4472 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4473 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4474 }
4476 {
4477 /* Mean */
4478 gchar const* labels[] = {_("(minimum mean)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum mean)")};
4479 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4480 EgeAdjustmentAction *eact = create_adjustment_action( "SprayMeanAction",
4481 _("Focus"), _("Focus:"), _("0 to spray a spot. Increase to enlarge the ring radius."),
4482 "/tools/spray/mean", 0,
4483 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-mean",
4484 0, 100, 1.0, 10.0,
4485 labels, values, G_N_ELEMENTS(labels),
4486 sp_spray_mean_value_changed, 1, 0 );
4487 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4488 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4489 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4490 }
4492 {
4493 /* Standard_deviation */
4494 gchar const* labels[] = {_("(minimum scatter)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum scatter)")};
4495 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4496 EgeAdjustmentAction *eact = create_adjustment_action( "SprayStandard_deviationAction",
4497 _("Scatter"), _("Scatter:"), _("Increase to scatter sprayed objects."),
4498 "/tools/spray/standard_deviation", 70,
4499 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-standard_deviation",
4500 1, 100, 1.0, 10.0,
4501 labels, values, G_N_ELEMENTS(labels),
4502 sp_spray_standard_deviation_value_changed, 1, 0 );
4503 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4504 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4505 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4506 }
4508 /* Mode */
4509 {
4510 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4512 GtkTreeIter iter;
4513 gtk_list_store_append( model, &iter );
4514 gtk_list_store_set( model, &iter,
4515 0, _("Spray with copies"),
4516 1, _("Spray copies of the initial selection"),
4517 2, INKSCAPE_ICON_SPRAY_COPY_MODE,
4518 -1 );
4520 gtk_list_store_append( model, &iter );
4521 gtk_list_store_set( model, &iter,
4522 0, _("Spray with clones"),
4523 1, _("Spray clones of the initial selection"),
4524 2, INKSCAPE_ICON_SPRAY_CLONE_MODE,
4525 -1 );
4527 gtk_list_store_append( model, &iter );
4528 gtk_list_store_set( model, &iter,
4529 0, _("Spray single path"),
4530 1, _("Spray objects in a single path"),
4531 2, INKSCAPE_ICON_SPRAY_UNION_MODE,
4532 -1 );
4534 EgeSelectOneAction* act = ege_select_one_action_new( "SprayModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4535 g_object_set( act, "short_label", _("Mode:"), NULL );
4536 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4537 g_object_set_data( holder, "mode_action", act );
4539 ege_select_one_action_set_appearance( act, "full" );
4540 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4541 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4542 ege_select_one_action_set_icon_column( act, 2 );
4543 ege_select_one_action_set_icon_size( act, secondarySize );
4544 ege_select_one_action_set_tooltip_column( act, 1 );
4546 gint mode = prefs->getInt("/tools/spray/mode", 1);
4547 ege_select_one_action_set_active( act, mode );
4548 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_spray_mode_changed), holder );
4550 g_object_set_data( G_OBJECT(holder), "spray_tool_mode", act);
4551 }
4553 { /* Population */
4554 gchar const* labels[] = {_("(low population)"), 0, 0, _("(default)"), 0, 0, _("(high population)")};
4555 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4556 EgeAdjustmentAction *eact = create_adjustment_action( "SprayPopulationAction",
4557 _("Amount"), _("Amount:"),
4558 _("Adjusts the number of items sprayed per clic."),
4559 "/tools/spray/population", 70,
4560 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-population",
4561 1, 100, 1.0, 10.0,
4562 labels, values, G_N_ELEMENTS(labels),
4563 sp_spray_population_value_changed, 1, 0 );
4564 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4565 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4566 g_object_set_data( holder, "spray_population", eact );
4567 }
4569 /* Use Pressure button */
4570 {
4571 InkToggleAction* act = ink_toggle_action_new( "SprayPressureAction",
4572 _("Pressure"),
4573 _("Use the pressure of the input device to alter the amount of sprayed objects."),
4574 "use_pressure",
4575 Inkscape::ICON_SIZE_DECORATION );
4576 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4577 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_spray_pressure_state_changed), NULL);
4578 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/spray/usepressure", true) );
4579 }
4581 { /* Rotation */
4582 gchar const* labels[] = {_("(low rotation variation)"), 0, 0, _("(default)"), 0, 0, _("(high rotation variation)")};
4583 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4584 EgeAdjustmentAction *eact = create_adjustment_action( "SprayRotationAction",
4585 _("Rotation"), _("Rotation:"),
4586 // xgettext:no-c-format
4587 _("Variation of the rotation of the sprayed objects. 0% for the same rotation than the original object."),
4588 "/tools/spray/rotation_variation", 0,
4589 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-rotation",
4590 0, 100, 1.0, 10.0,
4591 labels, values, G_N_ELEMENTS(labels),
4592 sp_spray_rotation_value_changed, 1, 0 );
4593 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4594 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4595 g_object_set_data( holder, "spray_rotation", eact );
4596 }
4598 { /* Scale */
4599 gchar const* labels[] = {_("(low scale variation)"), 0, 0, _("(default)"), 0, 0, _("(high scale variation)")};
4600 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4601 EgeAdjustmentAction *eact = create_adjustment_action( "SprayScaleAction",
4602 _("Scale"), _("Scale:"),
4603 // xgettext:no-c-format
4604 _("Variation in the scale of the sprayed objects. 0% for the same scale than the original object."),
4605 "/tools/spray/scale_variation", 0,
4606 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-scale",
4607 0, 100, 1.0, 10.0,
4608 labels, values, G_N_ELEMENTS(labels),
4609 sp_spray_scale_value_changed, 1, 0 );
4610 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4611 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4612 g_object_set_data( holder, "spray_scale", eact );
4613 }
4617 }
4620 //########################
4621 //## Calligraphy ##
4622 //########################
4623 static void update_presets_list (GObject *tbl)
4624 {
4625 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4626 if (g_object_get_data(tbl, "presets_blocked"))
4627 return;
4629 EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4630 if (!sel) {
4631 // WTF!? This will cause a segfault if ever reached
4632 //ege_select_one_action_set_active(sel, 0);
4633 return;
4634 }
4636 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4638 int ege_index = 1;
4639 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++ege_index) {
4640 bool match = true;
4642 std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(*i);
4643 for (std::vector<Inkscape::Preferences::Entry>::iterator j = preset.begin(); j != preset.end(); ++j) {
4644 Glib::ustring entry_name = j->getEntryName();
4645 if (entry_name == "id" || entry_name == "name") continue;
4647 void *widget = g_object_get_data(tbl, entry_name.data());
4648 if (widget) {
4649 if (GTK_IS_ADJUSTMENT(widget)) {
4650 double v = j->getDouble();
4651 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4652 //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
4653 if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
4654 match = false;
4655 break;
4656 }
4657 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4658 bool v = j->getBool();
4659 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4660 //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
4661 if ( static_cast<bool>(gtk_toggle_action_get_active(toggle)) != v ) {
4662 match = false;
4663 break;
4664 }
4665 }
4666 }
4667 }
4669 if (match) {
4670 // newly added item is at the same index as the
4671 // save command, so we need to change twice for it to take effect
4672 ege_select_one_action_set_active(sel, 0);
4673 ege_select_one_action_set_active(sel, ege_index); // one-based index
4674 return;
4675 }
4676 }
4678 // no match found
4679 ege_select_one_action_set_active(sel, 0);
4680 }
4682 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
4683 {
4684 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4685 prefs->setDouble( "/tools/calligraphic/mass", adj->value );
4686 update_presets_list(tbl);
4687 }
4689 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
4690 {
4691 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4692 prefs->setDouble( "/tools/calligraphic/wiggle", adj->value );
4693 update_presets_list(tbl);
4694 }
4696 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
4697 {
4698 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4699 prefs->setDouble( "/tools/calligraphic/angle", adj->value );
4700 update_presets_list(tbl);
4701 }
4703 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
4704 {
4705 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4706 prefs->setDouble( "/tools/calligraphic/width", adj->value );
4707 update_presets_list(tbl);
4708 }
4710 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
4711 {
4712 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4713 prefs->setDouble("/tools/calligraphic/thinning", adj->value );
4714 update_presets_list(tbl);
4715 }
4717 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
4718 {
4719 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4720 prefs->setDouble( "/tools/calligraphic/flatness", adj->value );
4721 update_presets_list(tbl);
4722 }
4724 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
4725 {
4726 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4727 prefs->setDouble( "/tools/calligraphic/tremor", adj->value );
4728 update_presets_list(tbl);
4729 }
4731 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
4732 {
4733 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4734 prefs->setDouble( "/tools/calligraphic/cap_rounding", adj->value );
4735 update_presets_list(tbl);
4736 }
4738 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject* tbl )
4739 {
4740 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4741 prefs->setBool("/tools/calligraphic/usepressure", gtk_toggle_action_get_active( act ));
4742 update_presets_list(tbl);
4743 }
4745 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject* tbl )
4746 {
4747 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4748 prefs->setBool("/tools/calligraphic/tracebackground", gtk_toggle_action_get_active( act ));
4749 update_presets_list(tbl);
4750 }
4752 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject* tbl )
4753 {
4754 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4755 GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
4756 prefs->setBool("/tools/calligraphic/usetilt", gtk_toggle_action_get_active( act ));
4757 update_presets_list(tbl);
4758 if (calligraphy_angle )
4759 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
4760 }
4763 static gchar const *const widget_names[] = {
4764 "width",
4765 "mass",
4766 "wiggle",
4767 "angle",
4768 "thinning",
4769 "tremor",
4770 "flatness",
4771 "cap_rounding",
4772 "usepressure",
4773 "tracebackground",
4774 "usetilt"
4775 };
4778 static void sp_dcc_build_presets_list(GObject *tbl)
4779 {
4780 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4782 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4783 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4784 gtk_list_store_clear (model);
4786 {
4787 GtkTreeIter iter;
4788 gtk_list_store_append( model, &iter );
4789 gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4790 }
4792 // iterate over all presets to populate the list
4793 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4794 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4795 int ii=1;
4797 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i) {
4798 GtkTreeIter iter;
4799 Glib::ustring preset_name = prefs->getString(*i + "/name");
4800 gtk_list_store_append( model, &iter );
4801 gtk_list_store_set( model, &iter, 0, _(preset_name.data()), 1, ii++, -1 );
4802 }
4804 {
4805 GtkTreeIter iter;
4806 gtk_list_store_append( model, &iter );
4807 gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4808 g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4809 }
4811 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4813 update_presets_list (tbl);
4814 }
4816 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4817 {
4818 using Inkscape::UI::Dialog::CalligraphicProfileRename;
4819 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4820 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4821 if (! desktop) return;
4823 if (g_object_get_data(tbl, "presets_blocked"))
4824 return;
4826 CalligraphicProfileRename::show(desktop);
4827 if ( !CalligraphicProfileRename::applied()) {
4828 // dialog cancelled
4829 update_presets_list (tbl);
4830 return;
4831 }
4832 Glib::ustring profile_name = CalligraphicProfileRename::getProfileName();
4834 if (profile_name.empty()) {
4835 // empty name entered
4836 update_presets_list (tbl);
4837 return;
4838 }
4840 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4842 // If there's a preset with the given name, find it and set save_path appropriately
4843 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4844 int total_presets = presets.size();
4845 int new_index = -1;
4846 Glib::ustring save_path; // profile pref path without a trailing slash
4848 int temp_index = 0;
4849 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++temp_index) {
4850 Glib::ustring name = prefs->getString(*i + "/name");
4851 if (!name.empty() && profile_name == name) {
4852 new_index = temp_index;
4853 save_path = *i;
4854 break;
4855 }
4856 }
4858 if (new_index == -1) {
4859 // no preset with this name, create
4860 new_index = total_presets + 1;
4861 gchar *profile_id = g_strdup_printf("/dcc%d", new_index);
4862 save_path = Glib::ustring("/tools/calligraphic/preset") + profile_id;
4863 g_free(profile_id);
4864 }
4866 for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4867 gchar const *const widget_name = widget_names[i];
4868 void *widget = g_object_get_data(tbl, widget_name);
4869 if (widget) {
4870 if (GTK_IS_ADJUSTMENT(widget)) {
4871 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4872 prefs->setDouble(save_path + "/" + widget_name, gtk_adjustment_get_value(adj));
4873 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4874 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4875 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4876 prefs->setBool(save_path + "/" + widget_name, gtk_toggle_action_get_active(toggle));
4877 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4878 } else {
4879 g_warning("Unknown widget type for preset: %s\n", widget_name);
4880 }
4881 } else {
4882 g_warning("Bad key when writing preset: %s\n", widget_name);
4883 }
4884 }
4885 prefs->setString(save_path + "/name", profile_name);
4887 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4888 sp_dcc_build_presets_list (tbl);
4889 }
4892 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4894 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4896 gint preset_index = ege_select_one_action_get_active( act );
4897 // This is necessary because EgeSelectOneAction spams us with GObject "changed" signal calls
4898 // even when the preset is not changed. It would be good to replace it with something more
4899 // modern. Index 0 means "No preset", so we don't do anything.
4900 if (preset_index == 0) return;
4902 gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4904 if (preset_index == save_presets_index) {
4905 // this is the Save command
4906 sp_dcc_save_profile(NULL, tbl);
4907 return;
4908 }
4910 if (g_object_get_data(tbl, "presets_blocked"))
4911 return;
4913 // preset_index is one-based so we subtract 1
4914 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4915 Glib::ustring preset_path = presets.at(preset_index - 1);
4917 if (!preset_path.empty()) {
4918 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
4920 std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(preset_path);
4922 // Shouldn't this be std::map?
4923 for (std::vector<Inkscape::Preferences::Entry>::iterator i = preset.begin(); i != preset.end(); ++i) {
4924 Glib::ustring entry_name = i->getEntryName();
4925 if (entry_name == "id" || entry_name == "name") continue;
4926 void *widget = g_object_get_data(tbl, entry_name.data());
4927 if (widget) {
4928 if (GTK_IS_ADJUSTMENT(widget)) {
4929 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4930 gtk_adjustment_set_value(adj, i->getDouble());
4931 //std::cout << "set adj " << attr_name << " to " << v << "\n";
4932 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4933 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4934 gtk_toggle_action_set_active(toggle, i->getBool());
4935 //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4936 } else {
4937 g_warning("Unknown widget type for preset: %s\n", entry_name.data());
4938 }
4939 } else {
4940 g_warning("Bad key found in a preset record: %s\n", entry_name.data());
4941 }
4942 }
4943 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4944 }
4945 }
4948 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4949 {
4950 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4951 {
4952 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4954 EgeAdjustmentAction* calligraphy_angle = 0;
4956 {
4957 /* Width */
4958 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4959 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4960 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4961 _("Pen Width"), _("Width:"),
4962 _("The width of the calligraphic pen (relative to the visible canvas area)"),
4963 "/tools/calligraphic/width", 15,
4964 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4965 1, 100, 1.0, 10.0,
4966 labels, values, G_N_ELEMENTS(labels),
4967 sp_ddc_width_value_changed, 1, 0 );
4968 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4969 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4970 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4971 }
4973 {
4974 /* Thinning */
4975 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4976 gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4977 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4978 _("Stroke Thinning"), _("Thinning:"),
4979 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4980 "/tools/calligraphic/thinning", 10,
4981 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4982 -100, 100, 1, 10.0,
4983 labels, values, G_N_ELEMENTS(labels),
4984 sp_ddc_velthin_value_changed, 1, 0);
4985 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4986 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4987 }
4989 {
4990 /* Angle */
4991 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4992 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4993 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4994 _("Pen Angle"), _("Angle:"),
4995 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4996 "/tools/calligraphic/angle", 30,
4997 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4998 -90.0, 90.0, 1.0, 10.0,
4999 labels, values, G_N_ELEMENTS(labels),
5000 sp_ddc_angle_value_changed, 1, 0 );
5001 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5002 g_object_set_data( holder, "angle_action", eact );
5003 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5004 calligraphy_angle = eact;
5005 }
5007 {
5008 /* Fixation */
5009 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
5010 gdouble values[] = {0, 20, 40, 60, 90, 100};
5011 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
5012 _("Fixation"), _("Fixation:"),
5013 _("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"),
5014 "/tools/calligraphic/flatness", 90,
5015 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5016 0.0, 100, 1.0, 10.0,
5017 labels, values, G_N_ELEMENTS(labels),
5018 sp_ddc_flatness_value_changed, 1, 0);
5019 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5020 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5021 }
5023 {
5024 /* Cap Rounding */
5025 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
5026 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
5027 // TRANSLATORS: "cap" means "end" (both start and finish) here
5028 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
5029 _("Cap rounding"), _("Caps:"),
5030 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
5031 "/tools/calligraphic/cap_rounding", 0.0,
5032 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5033 0.0, 5.0, 0.01, 0.1,
5034 labels, values, G_N_ELEMENTS(labels),
5035 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
5036 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5037 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5038 }
5040 {
5041 /* Tremor */
5042 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
5043 gdouble values[] = {0, 10, 20, 40, 60, 100};
5044 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
5045 _("Stroke Tremor"), _("Tremor:"),
5046 _("Increase to make strokes rugged and trembling"),
5047 "/tools/calligraphic/tremor", 0.0,
5048 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5049 0.0, 100, 1, 10.0,
5050 labels, values, G_N_ELEMENTS(labels),
5051 sp_ddc_tremor_value_changed, 1, 0);
5053 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5054 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5055 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5056 }
5058 {
5059 /* Wiggle */
5060 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
5061 gdouble values[] = {0, 20, 40, 60, 100};
5062 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
5063 _("Pen Wiggle"), _("Wiggle:"),
5064 _("Increase to make the pen waver and wiggle"),
5065 "/tools/calligraphic/wiggle", 0.0,
5066 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5067 0.0, 100, 1, 10.0,
5068 labels, values, G_N_ELEMENTS(labels),
5069 sp_ddc_wiggle_value_changed, 1, 0);
5070 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5071 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5072 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5073 }
5075 {
5076 /* Mass */
5077 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
5078 gdouble values[] = {0.0, 2, 10, 20, 50, 100};
5079 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
5080 _("Pen Mass"), _("Mass:"),
5081 _("Increase to make the pen drag behind, as if slowed by inertia"),
5082 "/tools/calligraphic/mass", 2.0,
5083 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5084 0.0, 100, 1, 10.0,
5085 labels, values, G_N_ELEMENTS(labels),
5086 sp_ddc_mass_value_changed, 1, 0);
5087 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5088 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5089 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5090 }
5093 /* Trace Background button */
5094 {
5095 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
5096 _("Trace Background"),
5097 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
5098 INKSCAPE_ICON_DRAW_TRACE_BACKGROUND,
5099 Inkscape::ICON_SIZE_DECORATION );
5100 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5101 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
5102 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/tracebackground", false) );
5103 g_object_set_data( holder, "tracebackground", act );
5104 }
5106 /* Use Pressure button */
5107 {
5108 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
5109 _("Pressure"),
5110 _("Use the pressure of the input device to alter the width of the pen"),
5111 INKSCAPE_ICON_DRAW_USE_PRESSURE,
5112 Inkscape::ICON_SIZE_DECORATION );
5113 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5114 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
5115 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usepressure", true) );
5116 g_object_set_data( holder, "usepressure", act );
5117 }
5119 /* Use Tilt button */
5120 {
5121 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
5122 _("Tilt"),
5123 _("Use the tilt of the input device to alter the angle of the pen's nib"),
5124 INKSCAPE_ICON_DRAW_USE_TILT,
5125 Inkscape::ICON_SIZE_DECORATION );
5126 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5127 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
5128 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs->getBool("/tools/calligraphic/usetilt", true) );
5129 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usetilt", true) );
5130 g_object_set_data( holder, "usetilt", act );
5131 }
5133 /*calligraphic profile */
5134 {
5135 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5136 EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
5137 ege_select_one_action_set_appearance (act1, "compact");
5138 g_object_set_data (holder, "profile_selector", act1 );
5140 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
5142 sp_dcc_build_presets_list (holder);
5144 g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
5145 gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
5146 }
5147 }
5148 }
5151 //########################
5152 //## Circle / Arc ##
5153 //########################
5155 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
5156 {
5157 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
5158 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
5160 if (v1 == 0 && v2 == 0) {
5161 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
5162 gtk_action_set_sensitive( ocb, FALSE );
5163 gtk_action_set_sensitive( make_whole, FALSE );
5164 }
5165 } else {
5166 gtk_action_set_sensitive( ocb, TRUE );
5167 gtk_action_set_sensitive( make_whole, TRUE );
5168 }
5169 }
5171 static void
5172 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
5173 {
5174 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5176 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5177 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5178 prefs->setDouble(Glib::ustring("/tools/shapes/arc/") + value_name, adj->value);
5179 }
5181 // quit if run by the attr_changed listener
5182 if (g_object_get_data( tbl, "freeze" )) {
5183 return;
5184 }
5186 // in turn, prevent listener from responding
5187 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5189 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
5191 bool modmade = false;
5192 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5193 items != NULL;
5194 items = items->next)
5195 {
5196 SPItem *item = SP_ITEM(items->data);
5198 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
5200 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
5201 SPArc *arc = SP_ARC(item);
5203 if (!strcmp(value_name, "start"))
5204 ge->start = (adj->value * M_PI)/ 180;
5205 else
5206 ge->end = (adj->value * M_PI)/ 180;
5208 sp_genericellipse_normalize(ge);
5209 ((SPObject *)arc)->updateRepr();
5210 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
5212 modmade = true;
5213 }
5214 }
5216 g_free(namespaced_name);
5218 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
5220 sp_arctb_sensitivize( tbl, adj->value, other->value );
5222 if (modmade) {
5223 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
5224 _("Arc: Change start/end"));
5225 }
5227 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5228 }
5231 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
5232 {
5233 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
5234 }
5236 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
5237 {
5238 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
5239 }
5242 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
5243 {
5244 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5245 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5246 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5247 prefs->setBool("/tools/shapes/arc/open", ege_select_one_action_get_active(act) != 0);
5248 }
5250 // quit if run by the attr_changed listener
5251 if (g_object_get_data( tbl, "freeze" )) {
5252 return;
5253 }
5255 // in turn, prevent listener from responding
5256 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5258 bool modmade = false;
5260 if ( ege_select_one_action_get_active(act) != 0 ) {
5261 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5262 items != NULL;
5263 items = items->next)
5264 {
5265 if (SP_IS_ARC((SPItem *) items->data)) {
5266 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5267 repr->setAttribute("sodipodi:open", "true");
5268 SP_OBJECT((SPItem *) items->data)->updateRepr();
5269 modmade = true;
5270 }
5271 }
5272 } else {
5273 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5274 items != NULL;
5275 items = items->next)
5276 {
5277 if (SP_IS_ARC((SPItem *) items->data)) {
5278 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5279 repr->setAttribute("sodipodi:open", NULL);
5280 SP_OBJECT((SPItem *) items->data)->updateRepr();
5281 modmade = true;
5282 }
5283 }
5284 }
5286 if (modmade) {
5287 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
5288 _("Arc: Change open/closed"));
5289 }
5291 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5292 }
5294 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
5295 {
5296 GtkAdjustment *adj;
5297 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
5298 gtk_adjustment_set_value(adj, 0.0);
5299 gtk_adjustment_value_changed(adj);
5301 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
5302 gtk_adjustment_set_value(adj, 0.0);
5303 gtk_adjustment_value_changed(adj);
5305 spinbutton_defocus( GTK_OBJECT(obj) );
5306 }
5308 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
5309 gchar const */*old_value*/, gchar const */*new_value*/,
5310 bool /*is_interactive*/, gpointer data)
5311 {
5312 GObject *tbl = G_OBJECT(data);
5314 // quit if run by the _changed callbacks
5315 if (g_object_get_data( tbl, "freeze" )) {
5316 return;
5317 }
5319 // in turn, prevent callbacks from responding
5320 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5322 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
5323 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
5325 GtkAdjustment *adj1,*adj2;
5326 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
5327 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
5328 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
5329 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
5331 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
5333 char const *openstr = NULL;
5334 openstr = repr->attribute("sodipodi:open");
5335 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
5337 if (openstr) {
5338 ege_select_one_action_set_active( ocb, 1 );
5339 } else {
5340 ege_select_one_action_set_active( ocb, 0 );
5341 }
5343 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5344 }
5346 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
5347 NULL, /* child_added */
5348 NULL, /* child_removed */
5349 arc_tb_event_attr_changed,
5350 NULL, /* content_changed */
5351 NULL /* order_changed */
5352 };
5355 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
5356 {
5357 int n_selected = 0;
5358 Inkscape::XML::Node *repr = NULL;
5360 purge_repr_listener( tbl, tbl );
5362 for (GSList const *items = selection->itemList();
5363 items != NULL;
5364 items = items->next)
5365 {
5366 if (SP_IS_ARC((SPItem *) items->data)) {
5367 n_selected++;
5368 repr = SP_OBJECT_REPR((SPItem *) items->data);
5369 }
5370 }
5372 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
5374 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
5375 if (n_selected == 0) {
5376 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
5377 } else if (n_selected == 1) {
5378 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
5379 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5381 if (repr) {
5382 g_object_set_data( tbl, "repr", repr );
5383 Inkscape::GC::anchor(repr);
5384 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
5385 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
5386 }
5387 } else {
5388 // FIXME: implement averaging of all parameters for multiple selected
5389 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
5390 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5391 sp_arctb_sensitivize( tbl, 1, 0 );
5392 }
5393 }
5396 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5397 {
5398 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5400 EgeAdjustmentAction* eact = 0;
5401 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
5404 {
5405 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
5406 ege_output_action_set_use_markup( act, TRUE );
5407 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5408 g_object_set_data( holder, "mode_action", act );
5409 }
5411 /* Start */
5412 {
5413 eact = create_adjustment_action( "ArcStartAction",
5414 _("Start"), _("Start:"),
5415 _("The angle (in degrees) from the horizontal to the arc's start point"),
5416 "/tools/shapes/arc/start", 0.0,
5417 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
5418 -360.0, 360.0, 1.0, 10.0,
5419 0, 0, 0,
5420 sp_arctb_start_value_changed);
5421 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5422 }
5424 /* End */
5425 {
5426 eact = create_adjustment_action( "ArcEndAction",
5427 _("End"), _("End:"),
5428 _("The angle (in degrees) from the horizontal to the arc's end point"),
5429 "/tools/shapes/arc/end", 0.0,
5430 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
5431 -360.0, 360.0, 1.0, 10.0,
5432 0, 0, 0,
5433 sp_arctb_end_value_changed);
5434 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5435 }
5437 /* Segments / Pie checkbox */
5438 {
5439 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5441 GtkTreeIter iter;
5442 gtk_list_store_append( model, &iter );
5443 gtk_list_store_set( model, &iter,
5444 0, _("Closed arc"),
5445 1, _("Switch to segment (closed shape with two radii)"),
5446 2, INKSCAPE_ICON_DRAW_ELLIPSE_SEGMENT,
5447 -1 );
5449 gtk_list_store_append( model, &iter );
5450 gtk_list_store_set( model, &iter,
5451 0, _("Open Arc"),
5452 1, _("Switch to arc (unclosed shape)"),
5453 2, INKSCAPE_ICON_DRAW_ELLIPSE_ARC,
5454 -1 );
5456 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5457 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5458 g_object_set_data( holder, "open_action", act );
5460 ege_select_one_action_set_appearance( act, "full" );
5461 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5462 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5463 ege_select_one_action_set_icon_column( act, 2 );
5464 ege_select_one_action_set_icon_size( act, secondarySize );
5465 ege_select_one_action_set_tooltip_column( act, 1 );
5467 bool isClosed = !prefs->getBool("/tools/shapes/arc/open", false);
5468 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
5469 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
5470 }
5472 /* Make Whole */
5473 {
5474 InkAction* inky = ink_action_new( "ArcResetAction",
5475 _("Make whole"),
5476 _("Make the shape a whole ellipse, not arc or segment"),
5477 INKSCAPE_ICON_DRAW_ELLIPSE_WHOLE,
5478 secondarySize );
5479 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
5480 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5481 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
5482 g_object_set_data( holder, "make_whole", inky );
5483 }
5485 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
5486 // sensitivize make whole and open checkbox
5487 {
5488 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
5489 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
5490 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
5491 }
5494 sigc::connection *connection = new sigc::connection(
5495 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
5496 );
5497 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
5498 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
5499 }
5504 // toggle button callbacks and updaters
5506 //########################
5507 //## Dropper ##
5508 //########################
5510 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
5511 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5512 prefs->setInt( "/tools/dropper/pick", gtk_toggle_action_get_active( act ) );
5513 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
5514 if ( set_action ) {
5515 if ( gtk_toggle_action_get_active( act ) ) {
5516 gtk_action_set_sensitive( set_action, TRUE );
5517 } else {
5518 gtk_action_set_sensitive( set_action, FALSE );
5519 }
5520 }
5522 spinbutton_defocus(GTK_OBJECT(tbl));
5523 }
5525 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
5526 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5527 prefs->setBool( "/tools/dropper/setalpha", gtk_toggle_action_get_active( act ) );
5528 spinbutton_defocus(GTK_OBJECT(tbl));
5529 }
5532 /**
5533 * Dropper auxiliary toolbar construction and setup.
5534 *
5535 * TODO: Would like to add swatch of current color.
5536 * TODO: Add queue of last 5 or so colors selected with new swatches so that
5537 * can drag and drop places. Will provide a nice mixing palette.
5538 */
5539 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
5540 {
5541 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5542 gint pickAlpha = prefs->getInt( "/tools/dropper/pick", 1 );
5544 {
5545 EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
5546 ege_output_action_set_use_markup( act, TRUE );
5547 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5548 }
5550 {
5551 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
5552 _("Pick opacity"),
5553 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
5554 NULL,
5555 Inkscape::ICON_SIZE_DECORATION );
5556 g_object_set( act, "short_label", _("Pick"), NULL );
5557 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5558 g_object_set_data( holder, "pick_action", act );
5559 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
5560 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
5561 }
5563 {
5564 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
5565 _("Assign opacity"),
5566 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
5567 NULL,
5568 Inkscape::ICON_SIZE_DECORATION );
5569 g_object_set( act, "short_label", _("Assign"), NULL );
5570 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5571 g_object_set_data( holder, "set_action", act );
5572 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/dropper/setalpha", true) );
5573 // make sure it's disabled if we're not picking alpha
5574 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
5575 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
5576 }
5577 }
5580 //########################
5581 //## LPETool ##
5582 //########################
5584 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
5586 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
5587 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
5588 {
5589 using namespace Inkscape::LivePathEffect;
5591 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
5592 SPEventContext *ec = desktop->event_context;
5593 if (!SP_IS_LPETOOL_CONTEXT(ec)) {
5594 return;
5595 }
5597 // only take action if run by the attr_changed listener
5598 if (!g_object_get_data(tbl, "freeze")) {
5599 // in turn, prevent listener from responding
5600 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5602 gint mode = ege_select_one_action_get_active(act);
5603 EffectType type = lpesubtools[mode].type;
5605 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5606 bool success = lpetool_try_construction(lc, type);
5607 if (success) {
5608 // since the construction was already performed, we set the state back to inactive
5609 ege_select_one_action_set_active(act, 0);
5610 mode = 0;
5611 } else {
5612 // switch to the chosen subtool
5613 SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
5614 }
5616 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5617 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5618 prefs->setInt( "/tools/lpetool/mode", mode );
5619 }
5621 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
5622 }
5623 }
5625 void sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject */*tbl*/)
5626 {
5627 SPEventContext *ec = selection->desktop()->event_context;
5628 if (!SP_IS_LPETOOL_CONTEXT(ec))
5629 return;
5631 lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
5632 }
5634 void
5635 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
5636 {
5637 using namespace Inkscape::LivePathEffect;
5638 SPEventContext *ec = selection->desktop()->event_context;
5639 if (!SP_IS_LPETOOL_CONTEXT(ec))
5640 return;
5641 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
5643 lpetool_delete_measuring_items(lc);
5644 lpetool_create_measuring_items(lc, selection);
5646 // activate line segment combo box if a single item with LPELineSegment is selected
5647 GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
5648 SPItem *item = selection->singleItem();
5649 if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
5650 SPLPEItem *lpeitem = SP_LPE_ITEM(item);
5651 Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
5652 if (lpe && lpe->effectType() == LINE_SEGMENT) {
5653 LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
5654 g_object_set_data(tbl, "currentlpe", lpe);
5655 g_object_set_data(tbl, "currentlpeitem", lpeitem);
5656 gtk_action_set_sensitive(w, TRUE);
5657 ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
5658 } else {
5659 g_object_set_data(tbl, "currentlpe", NULL);
5660 g_object_set_data(tbl, "currentlpeitem", NULL);
5661 gtk_action_set_sensitive(w, FALSE);
5662 }
5663 } else {
5664 g_object_set_data(tbl, "currentlpe", NULL);
5665 g_object_set_data(tbl, "currentlpeitem", NULL);
5666 gtk_action_set_sensitive(w, FALSE);
5667 }
5668 }
5670 static void
5671 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
5672 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5673 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5675 bool show = gtk_toggle_action_get_active( act );
5676 prefs->setBool("/tools/lpetool/show_bbox", show);
5678 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5679 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5680 lpetool_context_reset_limiting_bbox(lc);
5681 }
5682 }
5684 static void
5685 lpetool_toggle_show_measuring_info (GtkToggleAction *act, GObject *tbl) {
5686 SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
5687 if (!tools_isactive(desktop, TOOLS_LPETOOL))
5688 return;
5690 GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
5691 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5692 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5693 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5694 bool show = gtk_toggle_action_get_active( act );
5695 prefs->setBool("/tools/lpetool/show_measuring_info", show);
5696 lpetool_show_measuring_info(lc, show);
5697 gtk_action_set_sensitive(GTK_ACTION(unitact), show);
5698 }
5699 }
5701 static void lpetool_unit_changed(GtkAction* /*act*/, GObject* tbl) {
5702 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
5703 SPUnit const *unit = tracker->getActiveUnit();
5704 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5705 prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5707 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5708 if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
5709 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5710 lpetool_delete_measuring_items(lc);
5711 lpetool_create_measuring_items(lc);
5712 }
5713 }
5715 static void
5716 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
5717 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5718 Inkscape::Selection *selection = desktop->selection;
5720 Geom::OptRect bbox = selection->bounds();
5722 if (bbox) {
5723 Geom::Point A(bbox->min());
5724 Geom::Point B(bbox->max());
5726 A *= desktop->doc2dt();
5727 B *= desktop->doc2dt();
5729 // TODO: should we provide a way to store points in prefs?
5730 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5731 prefs->setDouble("/tools/lpetool/bbox_upperleftx", A[Geom::X]);
5732 prefs->setDouble("/tools/lpetool/bbox_upperlefty", A[Geom::Y]);
5733 prefs->setDouble("/tools/lpetool/bbox_lowerrightx", B[Geom::X]);
5734 prefs->setDouble("/tools/lpetool/bbox_lowerrighty", B[Geom::Y]);
5736 lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
5737 }
5739 gtk_toggle_action_set_active(act, false);
5740 }
5742 static void
5743 sp_line_segment_build_list(GObject *tbl)
5744 {
5745 g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
5747 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
5748 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
5749 gtk_list_store_clear (model);
5751 // TODO: we add the entries of rht combo box manually; later this should be done automatically
5752 {
5753 GtkTreeIter iter;
5754 gtk_list_store_append( model, &iter );
5755 gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
5756 gtk_list_store_append( model, &iter );
5757 gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
5758 gtk_list_store_append( model, &iter );
5759 gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
5760 gtk_list_store_append( model, &iter );
5761 gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
5762 }
5764 g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5765 }
5767 static void
5768 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
5769 using namespace Inkscape::LivePathEffect;
5771 // quit if run by the attr_changed listener
5772 if (g_object_get_data(tbl, "freeze")) {
5773 return;
5774 }
5776 // in turn, prevent listener from responding
5777 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5779 LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
5780 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5781 if (lpeitem) {
5782 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5783 lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
5784 sp_lpe_item_update_patheffect(lpeitem, true, true);
5785 }
5787 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5788 }
5790 static void
5791 lpetool_open_lpe_dialog (GtkToggleAction *act, gpointer data) {
5792 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5794 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5795 sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5796 }
5797 gtk_toggle_action_set_active(act, false);
5798 }
5800 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5801 {
5802 UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5803 tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5804 g_object_set_data(holder, "tracker", tracker);
5805 SPUnit const *unit = tracker->getActiveUnit();
5807 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5808 prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5810 /** Automatically create a list of LPEs that get added to the toolbar **/
5811 {
5812 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5814 GtkTreeIter iter;
5816 // the first toggle button represents the state that no subtool is active (remove this when
5817 // this can be modeled by EgeSelectOneAction or some other action)
5818 gtk_list_store_append( model, &iter );
5819 gtk_list_store_set( model, &iter,
5820 0, _("All inactive"),
5821 1, _("No geometric tool is active"),
5822 2, "draw-geometry-inactive",
5823 -1 );
5825 Inkscape::LivePathEffect::EffectType type;
5826 for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5827 type = lpesubtools[i].type;
5828 gtk_list_store_append( model, &iter );
5829 gtk_list_store_set( model, &iter,
5830 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5831 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5832 2, lpesubtools[i].icon_name,
5833 -1 );
5834 }
5836 EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5837 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5838 g_object_set_data( holder, "lpetool_mode_action", act );
5840 ege_select_one_action_set_appearance( act, "full" );
5841 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5842 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5843 ege_select_one_action_set_icon_column( act, 2 );
5844 ege_select_one_action_set_tooltip_column( act, 1 );
5846 gint lpeToolMode = prefs->getInt("/tools/lpetool/mode", 0);
5847 ege_select_one_action_set_active( act, lpeToolMode );
5848 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5849 }
5851 /* Show limiting bounding box */
5852 {
5853 InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5854 _("Show limiting bounding box"),
5855 _("Show bounding box (used to cut infinite lines)"),
5856 "show-bounding-box",
5857 Inkscape::ICON_SIZE_DECORATION );
5858 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5859 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5860 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_bbox", true ) );
5861 }
5863 /* Set limiting bounding box to bbox of current selection */
5864 {
5865 InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5866 _("Get limiting bounding box from selection"),
5867 _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5868 "draw-geometry-set-bounding-box",
5869 Inkscape::ICON_SIZE_DECORATION );
5870 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5871 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5872 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5873 }
5876 /* Combo box to choose line segment type */
5877 {
5878 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5879 EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5880 ege_select_one_action_set_appearance (act, "compact");
5881 g_object_set_data (holder, "lpetool_line_segment_action", act );
5883 g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5885 sp_line_segment_build_list (holder);
5887 g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5888 gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5889 gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5890 }
5892 /* Display measuring info for selected items */
5893 {
5894 InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5895 _("Display measuring info"),
5896 _("Display measuring info for selected items"),
5897 "draw-geometry-show-measuring-info",
5898 Inkscape::ICON_SIZE_DECORATION );
5899 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5900 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
5901 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_measuring_info", true ) );
5902 }
5904 // add the units menu
5905 {
5906 GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5907 gtk_action_group_add_action( mainActions, act );
5908 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5909 g_object_set_data(holder, "lpetool_units_action", act);
5910 gtk_action_set_sensitive(act, prefs->getBool("/tools/lpetool/show_measuring_info", true));
5911 }
5913 /* Open LPE dialog (to adapt parameters numerically) */
5914 {
5915 InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
5916 _("Open LPE dialog"),
5917 _("Open LPE dialog (to adapt parameters numerically)"),
5918 "dialog-geometry",
5919 Inkscape::ICON_SIZE_DECORATION );
5920 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5921 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
5922 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5923 }
5925 //watch selection
5926 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5928 sigc::connection *c_selection_modified =
5929 new sigc::connection (sp_desktop_selection (desktop)->connectModified
5930 (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
5931 pool->add_connection ("selection-modified", c_selection_modified);
5933 sigc::connection *c_selection_changed =
5934 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5935 (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5936 pool->add_connection ("selection-changed", c_selection_changed);
5937 }
5939 //########################
5940 //## Eraser ##
5941 //########################
5943 static void sp_erc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
5944 {
5945 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5946 prefs->setDouble( "/tools/eraser/width", adj->value );
5947 update_presets_list(tbl);
5948 }
5950 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5951 {
5952 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5953 bool eraserMode = ege_select_one_action_get_active( act ) != 0;
5954 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5955 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5956 prefs->setBool( "/tools/eraser/mode", eraserMode );
5957 }
5959 // only take action if run by the attr_changed listener
5960 if (!g_object_get_data( tbl, "freeze" )) {
5961 // in turn, prevent listener from responding
5962 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5964 if ( eraserMode != 0 ) {
5965 } else {
5966 }
5967 // TODO finish implementation
5969 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5970 }
5971 }
5973 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5974 {
5975 {
5976 /* Width */
5977 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5978 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5979 EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5980 _("Pen Width"), _("Width:"),
5981 _("The width of the eraser pen (relative to the visible canvas area)"),
5982 "/tools/eraser/width", 15,
5983 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5984 1, 100, 1.0, 10.0,
5985 labels, values, G_N_ELEMENTS(labels),
5986 sp_erc_width_value_changed, 1, 0);
5987 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5988 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5989 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5990 }
5992 {
5993 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5995 GtkTreeIter iter;
5996 gtk_list_store_append( model, &iter );
5997 gtk_list_store_set( model, &iter,
5998 0, _("Delete"),
5999 1, _("Delete objects touched by the eraser"),
6000 2, INKSCAPE_ICON_DRAW_ERASER_DELETE_OBJECTS,
6001 -1 );
6003 gtk_list_store_append( model, &iter );
6004 gtk_list_store_set( model, &iter,
6005 0, _("Cut"),
6006 1, _("Cut out from objects"),
6007 2, INKSCAPE_ICON_PATH_DIFFERENCE,
6008 -1 );
6010 EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
6011 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
6012 g_object_set_data( holder, "eraser_mode_action", act );
6014 ege_select_one_action_set_appearance( act, "full" );
6015 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
6016 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
6017 ege_select_one_action_set_icon_column( act, 2 );
6018 ege_select_one_action_set_tooltip_column( act, 1 );
6020 /// @todo Convert to boolean?
6021 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6022 gint eraserMode = prefs->getBool("/tools/eraser/mode") ? 1 : 0;
6023 ege_select_one_action_set_active( act, eraserMode );
6024 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
6025 }
6027 }
6029 //########################
6030 //## Text Toolbox ##
6031 //########################
6032 /*
6033 static void
6034 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
6035 {
6036 //Call back for letter sizing spinbutton
6037 }
6039 static void
6040 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
6041 {
6042 //Call back for line height spinbutton
6043 }
6045 static void
6046 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
6047 {
6048 //Call back for horizontal kerning spinbutton
6049 }
6051 static void
6052 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
6053 {
6054 //Call back for vertical kerning spinbutton
6055 }
6057 static void
6058 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
6059 {
6060 //Call back for letter rotation spinbutton
6061 }*/
6063 namespace {
6065 void
6066 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
6067 {
6068 // quit if run by the _changed callbacks
6069 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6070 return;
6071 }
6073 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6075 SPStyle *query =
6076 sp_style_new (SP_ACTIVE_DOCUMENT);
6078 int result_family =
6079 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6081 int result_style =
6082 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6084 int result_numbers =
6085 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6087 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6089 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6090 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
6091 // there are no texts in selection, read from prefs
6093 sp_style_read_from_prefs(query, "/tools/text");
6095 if (g_object_get_data(tbl, "text_style_from_prefs")) {
6096 // do not reset the toolbar style from prefs if we already did it last time
6097 sp_style_unref(query);
6098 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6099 return;
6100 }
6101 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
6102 } else {
6103 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
6104 }
6106 if (query->text)
6107 {
6108 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
6109 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6110 gtk_entry_set_text (GTK_ENTRY (entry), "");
6112 } else if (query->text->font_specification.value || query->text->font_family.value) {
6114 Gtk::ComboBoxEntry *combo = (Gtk::ComboBoxEntry *) (g_object_get_data (G_OBJECT (tbl), "family-entry-combo"));
6115 GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6117 // Get the font that corresponds
6118 Glib::ustring familyName;
6120 font_instance * font = font_factory::Default()->FaceFromStyle(query);
6121 if (font) {
6122 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
6123 font->Unref();
6124 font = NULL;
6125 }
6127 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
6129 Gtk::TreeIter iter;
6130 try {
6131 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
6132 Glib::RefPtr<Gtk::TreeModel> model = combo->get_model();
6133 iter = model->get_iter(path);
6134 } catch (...) {
6135 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
6136 sp_style_unref(query);
6137 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6138 return;
6139 }
6141 combo->set_active (iter);
6142 }
6144 //Size
6145 {
6146 GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
6147 gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
6148 gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
6149 g_free(str);
6150 }
6152 //Anchor
6153 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
6154 {
6155 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
6156 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6157 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6158 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6159 }
6160 else
6161 {
6162 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
6163 {
6164 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
6165 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6166 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6167 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6168 }
6169 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
6170 {
6171 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
6172 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6173 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6174 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6175 }
6176 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
6177 {
6178 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
6179 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6180 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6181 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6182 }
6183 }
6185 //Style
6186 {
6187 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
6189 gboolean active = gtk_toggle_button_get_active (button);
6190 gboolean check = ((query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700) && (query->font_weight.computed != SP_CSS_FONT_WEIGHT_NORMAL) && (query->font_weight.computed != SP_CSS_FONT_WEIGHT_LIGHTER));
6192 if (active != check)
6193 {
6194 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6195 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6196 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6197 }
6198 }
6200 {
6201 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
6203 gboolean active = gtk_toggle_button_get_active (button);
6204 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
6206 if (active != check)
6207 {
6208 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6209 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6210 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6211 }
6212 }
6214 //Orientation
6215 //locking both buttons, changing one affect all group (both)
6216 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
6217 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6219 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
6220 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
6222 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
6223 {
6224 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6225 }
6226 else
6227 {
6228 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
6229 }
6230 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6231 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
6232 }
6234 sp_style_unref(query);
6236 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6237 }
6239 void
6240 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
6241 {
6242 sp_text_toolbox_selection_changed (selection, tbl);
6243 }
6245 void
6246 sp_text_toolbox_subselection_changed (gpointer /*tc*/, GObject *tbl)
6247 {
6248 sp_text_toolbox_selection_changed (NULL, tbl);
6249 }
6251 void
6252 sp_text_toolbox_family_changed (GtkComboBoxEntry *,
6253 GObject *tbl)
6254 {
6255 // quit if run by the _changed callbacks
6256 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6257 return;
6258 }
6260 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6262 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6263 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6264 const gchar* family = gtk_entry_get_text (GTK_ENTRY (entry));
6266 //g_print ("family changed to: %s\n", family);
6268 SPStyle *query =
6269 sp_style_new (SP_ACTIVE_DOCUMENT);
6271 int result_fontspec =
6272 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6274 SPCSSAttr *css = sp_repr_css_attr_new ();
6276 // First try to get the font spec from the stored value
6277 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
6279 if (fontSpec.empty()) {
6280 // Construct a new font specification if it does not yet exist
6281 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6282 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6283 fontFromStyle->Unref();
6284 }
6286 if (!fontSpec.empty()) {
6288 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
6290 if (!newFontSpec.empty()) {
6292 if (fontSpec != newFontSpec) {
6294 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
6296 if (font) {
6297 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6299 // Set all the these just in case they were altered when finding the best
6300 // match for the new family and old style...
6302 gchar c[256];
6304 font->Family(c, 256);
6306 sp_repr_css_set_property (css, "font-family", c);
6308 font->Attribute( "weight", c, 256);
6309 sp_repr_css_set_property (css, "font-weight", c);
6311 font->Attribute("style", c, 256);
6312 sp_repr_css_set_property (css, "font-style", c);
6314 font->Attribute("stretch", c, 256);
6315 sp_repr_css_set_property (css, "font-stretch", c);
6317 font->Attribute("variant", c, 256);
6318 sp_repr_css_set_property (css, "font-variant", c);
6320 font->Unref();
6321 }
6322 }
6324 } else {
6325 // If the old font on selection (or default) was not existing on the system,
6326 // ReplaceFontSpecificationFamily does not work. In that case we fall back to blindly
6327 // setting the family reported by the family chooser.
6329 //g_print ("fallback setting family: %s\n", family);
6330 sp_repr_css_set_property (css, "-inkscape-font-specification", family);
6331 sp_repr_css_set_property (css, "font-family", family);
6332 }
6333 }
6335 // If querying returned nothing, set the default style of the tool (for new texts)
6336 if (result_fontspec == QUERY_STYLE_NOTHING)
6337 {
6338 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6339 prefs->mergeStyle("/tools/text/style", css);
6340 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
6341 }
6342 else
6343 {
6344 sp_desktop_set_style (desktop, css, true, true);
6345 }
6347 sp_style_unref(query);
6349 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6350 _("Text: Change font family"));
6351 sp_repr_css_attr_unref (css);
6353 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6355 // unfreeze
6356 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6358 // focus to canvas
6359 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6360 }
6363 void
6364 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
6365 gpointer data)
6366 {
6367 if (g_object_get_data (G_OBJECT (button), "block")) return;
6368 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
6369 int prop = GPOINTER_TO_INT(data);
6371 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6373 // move the x of all texts to preserve the same bbox
6374 Inkscape::Selection *selection = sp_desktop_selection(desktop);
6375 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
6376 if (SP_IS_TEXT((SPItem *) items->data)) {
6377 SPItem *item = SP_ITEM(items->data);
6379 unsigned writing_mode = SP_OBJECT_STYLE(item)->writing_mode.value;
6380 // below, variable names suggest horizontal move, but we check the writing direction
6381 // and move in the corresponding axis
6382 int axis;
6383 if (writing_mode == SP_CSS_WRITING_MODE_LR_TB || writing_mode == SP_CSS_WRITING_MODE_RL_TB) {
6384 axis = NR::X;
6385 } else {
6386 axis = NR::Y;
6387 }
6389 Geom::OptRect bbox
6390 = item->getBounds(Geom::identity(), SPItem::GEOMETRIC_BBOX);
6391 if (!bbox)
6392 continue;
6393 double width = bbox->dimensions()[axis];
6394 // If you want to align within some frame, other than the text's own bbox, calculate
6395 // the left and right (or top and bottom for tb text) slacks of the text inside that
6396 // frame (currently unused)
6397 double left_slack = 0;
6398 double right_slack = 0;
6399 unsigned old_align = SP_OBJECT_STYLE(item)->text_align.value;
6400 double move = 0;
6401 if (old_align == SP_CSS_TEXT_ALIGN_START || old_align == SP_CSS_TEXT_ALIGN_LEFT) {
6402 switch (prop) {
6403 case 0:
6404 move = -left_slack;
6405 break;
6406 case 1:
6407 move = width/2 + (right_slack - left_slack)/2;
6408 break;
6409 case 2:
6410 move = width + right_slack;
6411 break;
6412 }
6413 } else if (old_align == SP_CSS_TEXT_ALIGN_CENTER) {
6414 switch (prop) {
6415 case 0:
6416 move = -width/2 - left_slack;
6417 break;
6418 case 1:
6419 move = (right_slack - left_slack)/2;
6420 break;
6421 case 2:
6422 move = width/2 + right_slack;
6423 break;
6424 }
6425 } else if (old_align == SP_CSS_TEXT_ALIGN_END || old_align == SP_CSS_TEXT_ALIGN_RIGHT) {
6426 switch (prop) {
6427 case 0:
6428 move = -width - left_slack;
6429 break;
6430 case 1:
6431 move = -width/2 + (right_slack - left_slack)/2;
6432 break;
6433 case 2:
6434 move = right_slack;
6435 break;
6436 }
6437 }
6438 Geom::Point XY = SP_TEXT(item)->attributes.firstXY();
6439 if (axis == NR::X) {
6440 XY = XY + Geom::Point (move, 0);
6441 } else {
6442 XY = XY + Geom::Point (0, move);
6443 }
6444 SP_TEXT(item)->attributes.setFirstXY(XY);
6445 SP_OBJECT(item)->updateRepr();
6446 SP_OBJECT(item)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
6447 }
6448 }
6450 SPCSSAttr *css = sp_repr_css_attr_new ();
6451 switch (prop)
6452 {
6453 case 0:
6454 {
6455 sp_repr_css_set_property (css, "text-anchor", "start");
6456 sp_repr_css_set_property (css, "text-align", "start");
6457 break;
6458 }
6459 case 1:
6460 {
6461 sp_repr_css_set_property (css, "text-anchor", "middle");
6462 sp_repr_css_set_property (css, "text-align", "center");
6463 break;
6464 }
6466 case 2:
6467 {
6468 sp_repr_css_set_property (css, "text-anchor", "end");
6469 sp_repr_css_set_property (css, "text-align", "end");
6470 break;
6471 }
6473 case 3:
6474 {
6475 sp_repr_css_set_property (css, "text-anchor", "start");
6476 sp_repr_css_set_property (css, "text-align", "justify");
6477 break;
6478 }
6479 }
6481 SPStyle *query =
6482 sp_style_new (SP_ACTIVE_DOCUMENT);
6483 int result_numbers =
6484 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6486 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6487 if (result_numbers == QUERY_STYLE_NOTHING)
6488 {
6489 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6490 prefs->mergeStyle("/tools/text/style", css);
6491 }
6493 sp_style_unref(query);
6495 sp_desktop_set_style (desktop, css, true, true);
6496 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6497 _("Text: Change alignment"));
6498 sp_repr_css_attr_unref (css);
6500 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6501 }
6503 void
6504 sp_text_toolbox_style_toggled (GtkToggleButton *button,
6505 gpointer data)
6506 {
6507 if (g_object_get_data (G_OBJECT (button), "block")) return;
6509 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6510 SPCSSAttr *css = sp_repr_css_attr_new ();
6511 int prop = GPOINTER_TO_INT(data);
6512 bool active = gtk_toggle_button_get_active (button);
6514 SPStyle *query =
6515 sp_style_new (SP_ACTIVE_DOCUMENT);
6517 int result_fontspec =
6518 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6520 //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6521 //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6522 //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6524 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
6525 Glib::ustring newFontSpec = "";
6527 if (fontSpec.empty()) {
6528 // Construct a new font specification if it does not yet exist
6529 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6530 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6531 fontFromStyle->Unref();
6532 }
6534 bool nochange = true;
6535 switch (prop)
6536 {
6537 case 0:
6538 {
6539 if (!fontSpec.empty()) {
6540 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
6541 if (!newFontSpec.empty()) {
6542 // Don't even set the bold if the font didn't exist on the system
6543 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
6544 nochange = false;
6545 }
6546 }
6547 // set or reset the button according
6548 if(nochange) {
6549 gboolean check = gtk_toggle_button_get_active (button);
6551 if (active != check)
6552 {
6553 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6554 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6555 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6556 }
6557 }
6559 break;
6560 }
6562 case 1:
6563 {
6564 if (!fontSpec.empty()) {
6565 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
6566 if (!newFontSpec.empty()) {
6567 // Don't even set the italic if the font didn't exist on the system
6568 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
6569 nochange = false;
6570 }
6571 }
6572 if(nochange) {
6573 gboolean check = gtk_toggle_button_get_active (button);
6575 if (active != check)
6576 {
6577 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6578 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6579 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6580 }
6581 }
6582 break;
6583 }
6584 }
6586 if (!newFontSpec.empty()) {
6587 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6588 }
6590 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6591 if (result_fontspec == QUERY_STYLE_NOTHING)
6592 {
6593 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6594 prefs->mergeStyle("/tools/text/style", css);
6595 }
6597 sp_style_unref(query);
6599 sp_desktop_set_style (desktop, css, true, true);
6600 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6601 _("Text: Change font style"));
6602 sp_repr_css_attr_unref (css);
6604 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6605 }
6607 void
6608 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
6609 gpointer data)
6610 {
6611 if (g_object_get_data (G_OBJECT (button), "block")) {
6612 return;
6613 }
6615 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6616 SPCSSAttr *css = sp_repr_css_attr_new ();
6617 int prop = GPOINTER_TO_INT(data);
6619 switch (prop)
6620 {
6621 case 0:
6622 {
6623 sp_repr_css_set_property (css, "writing-mode", "lr");
6624 break;
6625 }
6627 case 1:
6628 {
6629 sp_repr_css_set_property (css, "writing-mode", "tb");
6630 break;
6631 }
6632 }
6634 SPStyle *query =
6635 sp_style_new (SP_ACTIVE_DOCUMENT);
6636 int result_numbers =
6637 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6639 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6640 if (result_numbers == QUERY_STYLE_NOTHING)
6641 {
6642 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6643 prefs->mergeStyle("/tools/text/style", css);
6644 }
6646 sp_desktop_set_style (desktop, css, true, true);
6647 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6648 _("Text: Change orientation"));
6649 sp_repr_css_attr_unref (css);
6651 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6652 }
6654 gboolean
6655 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6656 {
6657 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6658 if (!desktop) return FALSE;
6660 switch (get_group0_keyval (event)) {
6661 case GDK_KP_Enter: // chosen
6662 case GDK_Return:
6663 // unfreeze and update, which will defocus
6664 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6665 sp_text_toolbox_family_changed (NULL, tbl);
6666 return TRUE; // I consumed the event
6667 break;
6668 case GDK_Escape:
6669 // defocus
6670 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6671 return TRUE; // I consumed the event
6672 break;
6673 }
6674 return FALSE;
6675 }
6677 gboolean
6678 sp_text_toolbox_family_list_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject */*tbl*/)
6679 {
6680 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6681 if (!desktop) return FALSE;
6683 switch (get_group0_keyval (event)) {
6684 case GDK_KP_Enter:
6685 case GDK_Return:
6686 case GDK_Escape: // defocus
6687 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6688 return TRUE; // I consumed the event
6689 break;
6690 case GDK_w:
6691 case GDK_W:
6692 if (event->state & GDK_CONTROL_MASK) {
6693 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6694 return TRUE; // I consumed the event
6695 }
6696 break;
6697 }
6698 return FALSE;
6699 }
6702 void
6703 sp_text_toolbox_size_changed (GtkComboBox *cbox,
6704 GObject *tbl)
6705 {
6706 // quit if run by the _changed callbacks
6707 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6708 return;
6709 }
6711 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6713 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6715 // If this is not from selecting a size in the list (in which case get_active will give the
6716 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
6717 // process this event. This fixes GTK's stupid insistence on sending an activate change every
6718 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
6719 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed")) {
6720 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6721 return;
6722 }
6724 gdouble value = -1;
6725 {
6726 gchar *endptr;
6727 gchar *const text = gtk_combo_box_get_active_text(cbox);
6728 if (text) {
6729 value = g_strtod(text, &endptr);
6730 if (endptr == text) { // Conversion failed, non-numeric input.
6731 value = -1;
6732 }
6733 g_free(text);
6734 }
6735 }
6736 if (value <= 0) {
6737 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6738 return; // could not parse value
6739 }
6741 SPCSSAttr *css = sp_repr_css_attr_new ();
6742 Inkscape::CSSOStringStream osfs;
6743 osfs << value;
6744 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
6746 SPStyle *query =
6747 sp_style_new (SP_ACTIVE_DOCUMENT);
6748 int result_numbers =
6749 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6751 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6752 if (result_numbers == QUERY_STYLE_NOTHING)
6753 {
6754 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6755 prefs->mergeStyle("/tools/text/style", css);
6756 }
6758 sp_style_unref(query);
6760 sp_desktop_set_style (desktop, css, true, true);
6761 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
6762 _("Text: Change font size"));
6763 sp_repr_css_attr_unref (css);
6765 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6767 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6768 }
6770 gboolean
6771 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
6772 {
6773 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6774 if (!desktop) return FALSE;
6776 if (!g_object_get_data (tbl, "esc-pressed")) {
6777 g_object_set_data (tbl, "enter-pressed", gpointer(1));
6778 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6779 sp_text_toolbox_size_changed (cbox, tbl);
6780 g_object_set_data (tbl, "enter-pressed", gpointer(0));
6781 }
6782 return FALSE; // I consumed the event
6783 }
6786 gboolean
6787 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6788 {
6789 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6790 if (!desktop) return FALSE;
6792 switch (get_group0_keyval (event)) {
6793 case GDK_Escape: // defocus
6794 g_object_set_data (tbl, "esc-pressed", gpointer(1));
6795 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6796 g_object_set_data (tbl, "esc-pressed", gpointer(0));
6797 return TRUE; // I consumed the event
6798 break;
6799 case GDK_Return: // defocus
6800 case GDK_KP_Enter:
6801 g_object_set_data (tbl, "enter-pressed", gpointer(1));
6802 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6803 sp_text_toolbox_size_changed (cbox, tbl);
6804 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6805 g_object_set_data (tbl, "enter-pressed", gpointer(0));
6806 return TRUE; // I consumed the event
6807 break;
6808 }
6809 return FALSE;
6810 }
6812 // While editing font name in the entry, disable family_changed by freezing, otherwise completion
6813 // does not work!
6814 gboolean
6815 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
6816 GdkEventFocus */*event*/,
6817 GObject *tbl)
6818 {
6819 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6820 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1); // select all
6821 return FALSE;
6822 }
6824 gboolean
6825 sp_text_toolbox_entry_focus_out (GtkWidget *entry,
6826 GdkEventFocus */*event*/,
6827 GObject *tbl)
6828 {
6829 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6830 gtk_entry_select_region (GTK_ENTRY (entry), 0, 0); // deselect
6831 return FALSE;
6832 }
6834 void
6835 cell_data_func (GtkCellLayout */*cell_layout*/,
6836 GtkCellRenderer *cell,
6837 GtkTreeModel *tree_model,
6838 GtkTreeIter *iter,
6839 gpointer /*data*/)
6840 {
6841 gchar *family;
6842 gtk_tree_model_get(tree_model, iter, 0, &family, -1);
6843 gchar *const family_escaped = g_markup_escape_text(family, -1);
6845 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6846 int show_sample = prefs->getInt("/tools/text/show_sample_in_list", 1);
6847 if (show_sample) {
6849 Glib::ustring sample = prefs->getString("/tools/text/font_sample");
6850 gchar *const sample_escaped = g_markup_escape_text(sample.data(), -1);
6852 std::stringstream markup;
6853 markup << family_escaped << " <span foreground='darkgray' font_family='"
6854 << family_escaped << "'>" << sample_escaped << "</span>";
6855 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6857 g_free(sample_escaped);
6858 } else {
6859 g_object_set (G_OBJECT (cell), "markup", family_escaped, NULL);
6860 }
6862 g_free(family);
6863 g_free(family_escaped);
6864 }
6866 gboolean text_toolbox_completion_match_selected(GtkEntryCompletion */*widget*/,
6867 GtkTreeModel *model,
6868 GtkTreeIter *iter,
6869 GObject *tbl)
6870 {
6871 // We intercept this signal so as to fire family_changed at once (without it, you'd have to
6872 // press Enter again after choosing a completion)
6873 gchar *family = 0;
6874 gtk_tree_model_get(model, iter, 0, &family, -1);
6876 GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6877 gtk_entry_set_text (GTK_ENTRY (entry), family);
6879 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6880 sp_text_toolbox_family_changed (NULL, tbl);
6881 return TRUE;
6882 }
6885 static void
6886 cbe_add_completion (GtkComboBoxEntry *cbe, GObject *tbl){
6887 GtkEntry *entry;
6888 GtkEntryCompletion *completion;
6889 GtkTreeModel *model;
6891 entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(cbe)));
6892 completion = gtk_entry_completion_new();
6893 model = gtk_combo_box_get_model(GTK_COMBO_BOX(cbe));
6894 gtk_entry_completion_set_model(completion, model);
6895 gtk_entry_completion_set_text_column(completion, 0);
6896 gtk_entry_completion_set_inline_completion(completion, FALSE);
6897 gtk_entry_completion_set_inline_selection(completion, FALSE);
6898 gtk_entry_completion_set_popup_completion(completion, TRUE);
6899 gtk_entry_set_completion(entry, completion);
6901 g_signal_connect (G_OBJECT (completion), "match-selected", G_CALLBACK (text_toolbox_completion_match_selected), tbl);
6903 g_object_unref(completion);
6904 }
6906 void sp_text_toolbox_family_popnotify(GtkComboBox *widget,
6907 void */*property*/,
6908 GObject *tbl)
6909 {
6910 // while the drop-down is open, we disable font family changing, reenabling it only when it closes
6912 gboolean shown;
6913 g_object_get (G_OBJECT(widget), "popup-shown", &shown, NULL);
6914 if (shown) {
6915 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6916 //g_print("POP: notify: SHOWN\n");
6917 } else {
6918 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6920 // stupid GTK doesn't let us attach to events in the drop-down window, so we peek here to
6921 // find out if the drop down was closed by Enter and if so, manually update (only
6922 // necessary on Windows, on Linux it updates itself - what a mess, but we'll manage)
6923 GdkEvent *ev = gtk_get_current_event();
6924 if (ev) {
6925 //g_print ("ev type: %d\n", ev->type);
6926 if (ev->type == GDK_KEY_PRESS) {
6927 switch (get_group0_keyval ((GdkEventKey *) ev)) {
6928 case GDK_KP_Enter: // chosen
6929 case GDK_Return:
6930 {
6931 // make sure the chosen one is inserted into the entry
6932 GtkComboBox *combo = GTK_COMBO_BOX (((Gtk::ComboBox *) (g_object_get_data (tbl, "family-entry-combo")))->gobj());
6933 GtkTreeModel *model = gtk_combo_box_get_model(combo);
6934 GtkTreeIter iter;
6935 gboolean has_active = gtk_combo_box_get_active_iter (combo, &iter);
6936 if (has_active) {
6937 gchar *family;
6938 gtk_tree_model_get(model, &iter, 0, &family, -1);
6939 GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6940 gtk_entry_set_text (GTK_ENTRY (entry), family);
6941 }
6943 // update
6944 sp_text_toolbox_family_changed (NULL, tbl);
6945 break;
6946 }
6947 }
6948 }
6949 }
6951 // regardless of whether we updated, defocus the widget
6952 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6953 if (desktop)
6954 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6955 //g_print("POP: notify: HIDDEN\n");
6956 }
6957 }
6959 GtkWidget*
6960 sp_text_toolbox_new (SPDesktop *desktop)
6961 {
6962 GtkToolbar *tbl = GTK_TOOLBAR(gtk_toolbar_new());
6963 GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("/toolbox/secondary", 1));
6965 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
6966 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
6968 GtkTooltips *tt = gtk_tooltips_new();
6970 ////////////Family
6971 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
6972 Gtk::ComboBoxEntry *font_sel = Gtk::manage(new Gtk::ComboBoxEntry(store));
6974 gtk_rc_parse_string (
6975 "style \"dropdown-as-list-style\"\n"
6976 "{\n"
6977 " GtkComboBox::appears-as-list = 1\n"
6978 "}\n"
6979 "widget \"*.toolbox-fontfamily-list\" style \"dropdown-as-list-style\"");
6980 gtk_widget_set_name(GTK_WIDGET (font_sel->gobj()), "toolbox-fontfamily-list");
6981 gtk_tooltips_set_tip (tt, GTK_WIDGET (font_sel->gobj()), _("Select font family (Alt+X to access)"), "");
6983 g_signal_connect (G_OBJECT (font_sel->gobj()), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
6985 cbe_add_completion(font_sel->gobj(), G_OBJECT(tbl));
6987 gtk_toolbar_append_widget( tbl, (GtkWidget*) font_sel->gobj(), "", "");
6988 g_object_set_data (G_OBJECT (tbl), "family-entry-combo", font_sel);
6990 // expand the field a bit so as to view more of the previews in the drop-down
6991 GtkRequisition req;
6992 gtk_widget_size_request (GTK_WIDGET (font_sel->gobj()), &req);
6993 gtk_widget_set_size_request (GTK_WIDGET (font_sel->gobj()), MIN(req.width + 50, 500), -1);
6995 GtkWidget* entry = (GtkWidget*) font_sel->get_entry()->gobj();
6996 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6998 g_signal_connect (G_OBJECT (font_sel->gobj()), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6999 g_signal_connect (G_OBJECT (font_sel->gobj()), "notify::popup-shown",
7000 G_CALLBACK (sp_text_toolbox_family_popnotify), tbl);
7001 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
7002 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
7003 g_signal_connect (G_OBJECT (entry), "focus-out-event", G_CALLBACK (sp_text_toolbox_entry_focus_out), tbl);
7005 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
7006 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
7008 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
7009 gtk_cell_layout_clear( GTK_CELL_LAYOUT(font_sel->gobj()) );
7010 gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(font_sel->gobj()) , cell , TRUE );
7011 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT(font_sel->gobj()), cell, GtkCellLayoutDataFunc (cell_data_func), NULL, NULL);
7013 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
7014 GtkWidget *box = gtk_event_box_new ();
7015 gtk_container_add (GTK_CONTAINER (box), image);
7016 gtk_toolbar_append_widget( tbl, box, "", "");
7017 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
7018 gtk_tooltips_set_tip (tt, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
7019 gtk_widget_hide (GTK_WIDGET (box));
7020 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
7022 ////////////Size
7023 gchar const *const sizes[] = {
7024 "4", "6", "8", "9", "10", "11", "12", "13", "14",
7025 "16", "18", "20", "22", "24", "28",
7026 "32", "36", "40", "48", "56", "64", "72", "144"
7027 };
7029 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
7030 for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
7031 gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
7032 }
7033 gtk_widget_set_size_request (cbox, 80, -1);
7034 gtk_toolbar_append_widget( tbl, cbox, "", "");
7035 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
7036 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
7037 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
7038 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
7040 ////////////Text anchor
7041 GtkWidget *group = gtk_radio_button_new (NULL);
7042 GtkWidget *row = gtk_hbox_new (FALSE, 4);
7043 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
7045 // left
7046 GtkWidget *rbutton = group;
7047 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7048 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
7049 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7051 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7052 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
7053 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
7054 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
7056 // center
7057 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7058 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7059 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
7060 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7062 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7063 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
7064 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
7065 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
7067 // right
7068 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7069 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7070 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
7071 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7073 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7074 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
7075 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
7076 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
7078 // fill
7079 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7080 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7081 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
7082 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7084 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7085 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
7086 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
7087 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
7089 gtk_toolbar_append_widget( tbl, row, "", "");
7091 //spacer
7092 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
7094 ////////////Text style
7095 row = gtk_hbox_new (FALSE, 4);
7097 // bold
7098 rbutton = gtk_toggle_button_new ();
7099 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7100 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
7101 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7102 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
7104 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7105 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
7106 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
7108 // italic
7109 rbutton = gtk_toggle_button_new ();
7110 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7111 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
7112 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7113 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
7115 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7116 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
7117 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
7119 gtk_toolbar_append_widget( tbl, row, "", "");
7121 //spacer
7122 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
7124 // Text orientation
7125 group = gtk_radio_button_new (NULL);
7126 row = gtk_hbox_new (FALSE, 4);
7127 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
7129 // horizontal
7130 rbutton = group;
7131 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7132 gtk_container_add (GTK_CONTAINER (rbutton),
7133 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_HORIZONTAL));
7134 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7135 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
7137 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7138 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
7139 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
7141 // vertical
7142 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7143 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7144 gtk_container_add (GTK_CONTAINER (rbutton),
7145 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_VERTICAL));
7146 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7147 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
7149 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7150 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
7151 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
7152 gtk_toolbar_append_widget( tbl, row, "", "" );
7155 //watch selection
7156 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
7158 sigc::connection *c_selection_changed =
7159 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
7160 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
7161 pool->add_connection ("selection-changed", c_selection_changed);
7163 sigc::connection *c_selection_modified =
7164 new sigc::connection (sp_desktop_selection (desktop)->connectModified
7165 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
7166 pool->add_connection ("selection-modified", c_selection_modified);
7168 sigc::connection *c_subselection_changed =
7169 new sigc::connection (desktop->connectToolSubselectionChanged
7170 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
7171 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
7173 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
7176 gtk_widget_show_all( GTK_WIDGET(tbl) );
7178 return GTK_WIDGET(tbl);
7179 } // end of sp_text_toolbox_new()
7181 }//<unnamed> namespace
7184 //#########################
7185 //## Connector ##
7186 //#########################
7188 static void sp_connector_mode_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7189 {
7190 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7191 prefs->setBool("/tools/connector/mode",
7192 gtk_toggle_action_get_active( act ));
7193 }
7195 static void sp_connector_path_set_avoid(void)
7196 {
7197 cc_selection_set_avoid(true);
7198 }
7201 static void sp_connector_path_set_ignore(void)
7202 {
7203 cc_selection_set_avoid(false);
7204 }
7206 static void sp_connector_orthogonal_toggled( GtkToggleAction* act, GObject *tbl )
7207 {
7208 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7209 Inkscape::Selection * selection = sp_desktop_selection(desktop);
7210 SPDocument *doc = sp_desktop_document(desktop);
7212 if (!sp_document_get_undo_sensitive(doc))
7213 {
7214 return;
7215 }
7218 // quit if run by the _changed callbacks
7219 if (g_object_get_data( tbl, "freeze" )) {
7220 return;
7221 }
7223 // in turn, prevent callbacks from responding
7224 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7226 bool is_orthog = gtk_toggle_action_get_active( act );
7227 gchar orthog_str[] = "orthogonal";
7228 gchar polyline_str[] = "polyline";
7229 gchar *value = is_orthog ? orthog_str : polyline_str ;
7231 bool modmade = false;
7232 GSList *l = (GSList *) selection->itemList();
7233 while (l) {
7234 SPItem *item = (SPItem *) l->data;
7236 if (cc_item_is_connector(item)) {
7237 sp_object_setAttribute(item, "inkscape:connector-type",
7238 value, false);
7239 item->avoidRef->handleSettingChange();
7240 modmade = true;
7241 }
7242 l = l->next;
7243 }
7245 if (!modmade)
7246 {
7247 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7248 prefs->setBool("/tools/connector/orthogonal", is_orthog);
7249 }
7251 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7252 is_orthog ? _("Set connector type: orthogonal"): _("Set connector type: polyline"));
7254 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7255 }
7257 static void connector_curvature_changed(GtkAdjustment *adj, GObject* tbl)
7258 {
7259 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7260 Inkscape::Selection * selection = sp_desktop_selection(desktop);
7261 SPDocument *doc = sp_desktop_document(desktop);
7263 if (!sp_document_get_undo_sensitive(doc))
7264 {
7265 return;
7266 }
7269 // quit if run by the _changed callbacks
7270 if (g_object_get_data( tbl, "freeze" )) {
7271 return;
7272 }
7274 // in turn, prevent callbacks from responding
7275 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7277 gdouble newValue = gtk_adjustment_get_value(adj);
7278 gchar value[G_ASCII_DTOSTR_BUF_SIZE];
7279 g_ascii_dtostr(value, G_ASCII_DTOSTR_BUF_SIZE, newValue);
7281 bool modmade = false;
7282 GSList *l = (GSList *) selection->itemList();
7283 while (l) {
7284 SPItem *item = (SPItem *) l->data;
7286 if (cc_item_is_connector(item)) {
7287 sp_object_setAttribute(item, "inkscape:connector-curvature",
7288 value, false);
7289 item->avoidRef->handleSettingChange();
7290 modmade = true;
7291 }
7292 l = l->next;
7293 }
7295 if (!modmade)
7296 {
7297 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7298 prefs->setDouble(Glib::ustring("/tools/connector/curvature"), newValue);
7299 }
7301 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7302 _("Change connector curvature"));
7304 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7305 }
7308 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
7309 {
7310 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7311 SPDocument *doc = sp_desktop_document(desktop);
7313 if (!sp_document_get_undo_sensitive(doc))
7314 {
7315 return;
7316 }
7318 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7320 if ( !repr->attribute("inkscape:connector-spacing") &&
7321 ( adj->value == defaultConnSpacing )) {
7322 // Don't need to update the repr if the attribute doesn't
7323 // exist and it is being set to the default value -- as will
7324 // happen at startup.
7325 return;
7326 }
7328 // quit if run by the attr_changed listener
7329 if (g_object_get_data( tbl, "freeze" )) {
7330 return;
7331 }
7333 // in turn, prevent listener from responding
7334 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
7336 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
7337 SP_OBJECT(desktop->namedview)->updateRepr();
7339 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
7340 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
7341 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
7342 Geom::Matrix m = Geom::identity();
7343 avoid_item_move(&m, item);
7344 }
7346 if (items) {
7347 g_slist_free(items);
7348 }
7350 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7351 _("Change connector spacing"));
7353 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7354 }
7356 static void sp_connector_graph_layout(void)
7357 {
7358 if (!SP_ACTIVE_DESKTOP) return;
7359 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7361 // hack for clones, see comment in align-and-distribute.cpp
7362 int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7363 prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7365 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
7367 prefs->setInt("/options/clonecompensation/value", saved_compensation);
7369 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
7370 }
7372 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7373 {
7374 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7375 prefs->setBool("/tools/connector/directedlayout",
7376 gtk_toggle_action_get_active( act ));
7377 }
7379 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7380 {
7381 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7382 prefs->setBool("/tools/connector/avoidoverlaplayout",
7383 gtk_toggle_action_get_active( act ));
7384 }
7387 static void connector_length_changed(GtkAdjustment *adj, GObject* /*tbl*/)
7388 {
7389 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7390 prefs->setDouble("/tools/connector/length", adj->value);
7391 }
7393 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
7394 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
7395 bool /*is_interactive*/, gpointer data)
7396 {
7397 GtkWidget *tbl = GTK_WIDGET(data);
7399 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
7400 return;
7401 }
7402 if (strcmp(name, "inkscape:connector-spacing") == 0)
7403 {
7404 GtkAdjustment *adj = (GtkAdjustment*)
7405 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
7406 gdouble spacing = defaultConnSpacing;
7407 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
7409 gtk_adjustment_set_value(adj, spacing);
7410 gtk_adjustment_value_changed(adj);
7411 }
7413 spinbutton_defocus(GTK_OBJECT(tbl));
7414 }
7416 static void sp_connector_new_connection_point(GtkWidget *, GObject *tbl)
7417 {
7418 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7419 SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7421 if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE)
7422 cc_create_connection_point(cc);
7423 }
7425 static void sp_connector_remove_connection_point(GtkWidget *, GObject *tbl)
7426 {
7427 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7428 SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7430 if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE)
7431 cc_remove_connection_point(cc);
7432 }
7434 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
7435 NULL, /* child_added */
7436 NULL, /* child_removed */
7437 connector_tb_event_attr_changed,
7438 NULL, /* content_changed */
7439 NULL /* order_changed */
7440 };
7442 static void sp_connector_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
7443 {
7444 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "curvature" ) );
7445 GtkToggleAction *act = GTK_TOGGLE_ACTION( g_object_get_data( tbl, "orthogonal" ) );
7446 SPItem *item = selection->singleItem();
7447 if (SP_IS_PATH(item))
7448 {
7449 gdouble curvature = SP_PATH(item)->connEndPair.getCurvature();
7450 bool is_orthog = SP_PATH(item)->connEndPair.isOrthogonal();
7451 gtk_toggle_action_set_active(act, is_orthog);
7452 gtk_adjustment_set_value(adj, curvature);
7453 }
7455 }
7457 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
7458 {
7459 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7460 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
7462 // Editing mode toggle button
7463 {
7464 InkToggleAction* act = ink_toggle_action_new( "ConnectorEditModeAction",
7465 _("EditMode"),
7466 _("Switch between connection point editing and connector drawing mode"),
7467 INKSCAPE_ICON_CONNECTOR_EDIT,
7468 Inkscape::ICON_SIZE_DECORATION );
7469 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7471 bool tbuttonstate = prefs->getBool("/tools/connector/mode");
7472 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7473 g_object_set_data( holder, "mode", act );
7474 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_mode_toggled), holder );
7475 }
7478 {
7479 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
7480 _("Avoid"),
7481 _("Make connectors avoid selected objects"),
7482 INKSCAPE_ICON_CONNECTOR_AVOID,
7483 secondarySize );
7484 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
7485 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7486 }
7488 {
7489 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
7490 _("Ignore"),
7491 _("Make connectors ignore selected objects"),
7492 INKSCAPE_ICON_CONNECTOR_IGNORE,
7493 secondarySize );
7494 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
7495 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7496 }
7498 // Orthogonal connectors toggle button
7499 {
7500 InkToggleAction* act = ink_toggle_action_new( "ConnectorOrthogonalAction",
7501 _("Orthogonal"),
7502 _("Make connector orthogonal or polyline"),
7503 INKSCAPE_ICON_CONNECTOR_ORTHOGONAL,
7504 Inkscape::ICON_SIZE_DECORATION );
7505 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7507 bool tbuttonstate = prefs->getBool("/tools/connector/orthogonal");
7508 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7509 g_object_set_data( holder, "orthogonal", act );
7510 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_orthogonal_toggled), holder );
7511 }
7513 EgeAdjustmentAction* eact = 0;
7514 // Curvature spinbox
7515 eact = create_adjustment_action( "ConnectorCurvatureAction",
7516 _("Connector Curvature"), _("Curvature:"),
7517 _("The amount of connectors curvature"),
7518 "/tools/connector/curvature", defaultConnCurvature,
7519 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-curvature",
7520 0, 100, 1.0, 10.0,
7521 0, 0, 0,
7522 connector_curvature_changed, 1, 0 );
7523 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7525 // Spacing spinbox
7526 eact = create_adjustment_action( "ConnectorSpacingAction",
7527 _("Connector Spacing"), _("Spacing:"),
7528 _("The amount of space left around objects by auto-routing connectors"),
7529 "/tools/connector/spacing", defaultConnSpacing,
7530 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
7531 0, 100, 1.0, 10.0,
7532 0, 0, 0,
7533 connector_spacing_changed, 1, 0 );
7534 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7536 // Graph (connector network) layout
7537 {
7538 InkAction* inky = ink_action_new( "ConnectorGraphAction",
7539 _("Graph"),
7540 _("Nicely arrange selected connector network"),
7541 INKSCAPE_ICON_DISTRIBUTE_GRAPH,
7542 secondarySize );
7543 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
7544 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7545 }
7547 // Default connector length spinbox
7548 eact = create_adjustment_action( "ConnectorLengthAction",
7549 _("Connector Length"), _("Length:"),
7550 _("Ideal length for connectors when layout is applied"),
7551 "/tools/connector/length", 100,
7552 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
7553 10, 1000, 10.0, 100.0,
7554 0, 0, 0,
7555 connector_length_changed, 1, 0 );
7556 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7559 // Directed edges toggle button
7560 {
7561 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
7562 _("Downwards"),
7563 _("Make connectors with end-markers (arrows) point downwards"),
7564 INKSCAPE_ICON_DISTRIBUTE_GRAPH_DIRECTED,
7565 Inkscape::ICON_SIZE_DECORATION );
7566 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7568 bool tbuttonstate = prefs->getBool("/tools/connector/directedlayout");
7569 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7571 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
7572 sigc::connection *connection = new sigc::connection(sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_connector_toolbox_selection_changed), (GObject *)holder))
7573 );
7574 }
7576 // Avoid overlaps toggle button
7577 {
7578 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
7579 _("Remove overlaps"),
7580 _("Do not allow overlapping shapes"),
7581 INKSCAPE_ICON_DISTRIBUTE_REMOVE_OVERLAPS,
7582 Inkscape::ICON_SIZE_DECORATION );
7583 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7585 bool tbuttonstate = prefs->getBool("/tools/connector/avoidoverlaplayout");
7586 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), (tbuttonstate ? TRUE : FALSE ));
7588 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
7589 }
7592 // New connection point button
7593 {
7594 InkAction* inky = ink_action_new( "ConnectorNewConnPointAction",
7595 _("New connection point"),
7596 _("Add a new connection point to the currently selected item"),
7597 INKSCAPE_ICON_CONNECTOR_NEW_CONNPOINT,
7598 secondarySize );
7599 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_new_connection_point), holder );
7600 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7601 }
7603 // Remove selected connection point button
7605 {
7606 InkAction* inky = ink_action_new( "ConnectorRemoveConnPointAction",
7607 _("Remove connection point"),
7608 _("Remove the currently selected connection point"),
7609 INKSCAPE_ICON_CONNECTOR_REMOVE_CONNPOINT,
7610 secondarySize );
7611 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_remove_connection_point), holder );
7612 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7613 }
7616 // Code to watch for changes to the connector-spacing attribute in
7617 // the XML.
7618 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7619 g_assert(repr != NULL);
7621 purge_repr_listener( holder, holder );
7623 if (repr) {
7624 g_object_set_data( holder, "repr", repr );
7625 Inkscape::GC::anchor(repr);
7626 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
7627 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
7628 }
7629 } // end of sp_connector_toolbox_prep()
7632 //#########################
7633 //## Paintbucket ##
7634 //#########################
7636 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
7637 {
7638 gint channels = ege_select_one_action_get_active( act );
7639 flood_channels_set_channels( channels );
7640 }
7642 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
7643 {
7644 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7645 prefs->setInt("/tools/paintbucket/threshold", (gint)adj->value);
7646 }
7648 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
7649 {
7650 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7651 prefs->setBool("/tools/paintbucket/autogap", ege_select_one_action_get_active( act ));
7652 }
7654 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
7655 {
7656 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
7657 SPUnit const *unit = tracker->getActiveUnit();
7658 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7660 prefs->setDouble("/tools/paintbucket/offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
7661 prefs->setString("/tools/paintbucket/offsetunits", sp_unit_get_abbreviation(unit));
7662 }
7664 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
7665 {
7666 // FIXME: make defaults settable via Inkscape Options
7667 struct KeyValue {
7668 char const *key;
7669 double value;
7670 } const key_values[] = {
7671 {"threshold", 15},
7672 {"offset", 0.0}
7673 };
7675 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
7676 KeyValue const &kv = key_values[i];
7677 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
7678 if ( adj ) {
7679 gtk_adjustment_set_value(adj, kv.value);
7680 }
7681 }
7683 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
7684 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
7685 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
7686 ege_select_one_action_set_active( autogap_action, 0 );
7687 }
7689 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
7690 {
7691 EgeAdjustmentAction* eact = 0;
7692 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7694 {
7695 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7697 GList* items = 0;
7698 gint count = 0;
7699 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
7700 {
7701 GtkTreeIter iter;
7702 gtk_list_store_append( model, &iter );
7703 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7704 count++;
7705 }
7706 g_list_free( items );
7707 items = 0;
7708 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
7709 g_object_set( act1, "short_label", _("Fill by:"), NULL );
7710 ege_select_one_action_set_appearance( act1, "compact" );
7711 ege_select_one_action_set_active( act1, prefs->getInt("/tools/paintbucket/channels", 0) );
7712 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
7713 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
7714 g_object_set_data( holder, "channels_action", act1 );
7715 }
7717 // Spacing spinbox
7718 {
7719 eact = create_adjustment_action(
7720 "ThresholdAction",
7721 _("Fill Threshold"), _("Threshold:"),
7722 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
7723 "/tools/paintbucket/threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
7724 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
7725 0, 0, 0,
7726 paintbucket_threshold_changed, 1, 0 );
7728 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
7729 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7730 }
7732 // Create the units menu.
7733 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
7734 Glib::ustring stored_unit = prefs->getString("/tools/paintbucket/offsetunits");
7735 if (!stored_unit.empty())
7736 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit.data()));
7737 g_object_set_data( holder, "tracker", tracker );
7738 {
7739 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
7740 gtk_action_group_add_action( mainActions, act );
7741 }
7743 // Offset spinbox
7744 {
7745 eact = create_adjustment_action(
7746 "OffsetAction",
7747 _("Grow/shrink by"), _("Grow/shrink by:"),
7748 _("The amount to grow (positive) or shrink (negative) the created fill path"),
7749 "/tools/paintbucket/offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
7750 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
7751 0, 0, 0,
7752 paintbucket_offset_changed, 1, 2);
7753 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
7755 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7756 }
7758 /* Auto Gap */
7759 {
7760 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7762 GList* items = 0;
7763 gint count = 0;
7764 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
7765 {
7766 GtkTreeIter iter;
7767 gtk_list_store_append( model, &iter );
7768 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7769 count++;
7770 }
7771 g_list_free( items );
7772 items = 0;
7773 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
7774 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
7775 ege_select_one_action_set_appearance( act2, "compact" );
7776 ege_select_one_action_set_active( act2, prefs->getBool("/tools/paintbucket/autogap") );
7777 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
7778 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
7779 g_object_set_data( holder, "autogap_action", act2 );
7780 }
7782 /* Reset */
7783 {
7784 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
7785 _("Defaults"),
7786 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
7787 GTK_STOCK_CLEAR );
7788 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
7789 gtk_action_group_add_action( mainActions, act );
7790 gtk_action_set_sensitive( act, TRUE );
7791 }
7793 }
7795 /*
7796 Local Variables:
7797 mode:c++
7798 c-file-style:"stroustrup"
7799 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
7800 indent-tabs-mode:nil
7801 fill-column:99
7802 End:
7803 */
7804 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :