e0a489f2d7f34bd602f5887b077685b9baceee57
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 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4565 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4566 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4567 g_object_set_data( holder, "spray_population", eact );
4568 }
4570 /* Use Pressure button */
4571 {
4572 InkToggleAction* act = ink_toggle_action_new( "SprayPressureAction",
4573 _("Pressure"),
4574 _("Use the pressure of the input device to alter the amount of sprayed objects."),
4575 "use_pressure",
4576 Inkscape::ICON_SIZE_DECORATION );
4577 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4578 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_spray_pressure_state_changed), NULL);
4579 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/spray/usepressure", true) );
4580 }
4582 { /* Rotation */
4583 gchar const* labels[] = {_("(low rotation variation)"), 0, 0, _("(default)"), 0, 0, _("(high rotation variation)")};
4584 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4585 EgeAdjustmentAction *eact = create_adjustment_action( "SprayRotationAction",
4586 _("Rotation"), _("Rotation:"),
4587 // xgettext:no-c-format
4588 _("Variation of the rotation of the sprayed objects. 0% for the same rotation than the original object."),
4589 "/tools/spray/rotation_variation", 0,
4590 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-rotation",
4591 0, 100, 1.0, 10.0,
4592 labels, values, G_N_ELEMENTS(labels),
4593 sp_spray_rotation_value_changed, 1, 0 );
4594 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4595 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4596 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4597 g_object_set_data( holder, "spray_rotation", eact );
4598 }
4600 { /* Scale */
4601 gchar const* labels[] = {_("(low scale variation)"), 0, 0, _("(default)"), 0, 0, _("(high scale variation)")};
4602 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4603 EgeAdjustmentAction *eact = create_adjustment_action( "SprayScaleAction",
4604 _("Scale"), _("Scale:"),
4605 // xgettext:no-c-format
4606 _("Variation in the scale of the sprayed objects. 0% for the same scale than the original object."),
4607 "/tools/spray/scale_variation", 0,
4608 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-scale",
4609 0, 100, 1.0, 10.0,
4610 labels, values, G_N_ELEMENTS(labels),
4611 sp_spray_scale_value_changed, 1, 0 );
4612 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4613 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4614 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4615 g_object_set_data( holder, "spray_scale", eact );
4616 }
4620 }
4623 //########################
4624 //## Calligraphy ##
4625 //########################
4626 static void update_presets_list (GObject *tbl)
4627 {
4628 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4629 if (g_object_get_data(tbl, "presets_blocked"))
4630 return;
4632 EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4633 if (!sel) {
4634 // WTF!? This will cause a segfault if ever reached
4635 //ege_select_one_action_set_active(sel, 0);
4636 return;
4637 }
4639 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4641 int ege_index = 1;
4642 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++ege_index) {
4643 bool match = true;
4645 std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(*i);
4646 for (std::vector<Inkscape::Preferences::Entry>::iterator j = preset.begin(); j != preset.end(); ++j) {
4647 Glib::ustring entry_name = j->getEntryName();
4648 if (entry_name == "id" || entry_name == "name") continue;
4650 void *widget = g_object_get_data(tbl, entry_name.data());
4651 if (widget) {
4652 if (GTK_IS_ADJUSTMENT(widget)) {
4653 double v = j->getDouble();
4654 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4655 //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
4656 if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
4657 match = false;
4658 break;
4659 }
4660 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4661 bool v = j->getBool();
4662 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4663 //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
4664 if ( static_cast<bool>(gtk_toggle_action_get_active(toggle)) != v ) {
4665 match = false;
4666 break;
4667 }
4668 }
4669 }
4670 }
4672 if (match) {
4673 // newly added item is at the same index as the
4674 // save command, so we need to change twice for it to take effect
4675 ege_select_one_action_set_active(sel, 0);
4676 ege_select_one_action_set_active(sel, ege_index); // one-based index
4677 return;
4678 }
4679 }
4681 // no match found
4682 ege_select_one_action_set_active(sel, 0);
4683 }
4685 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
4686 {
4687 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4688 prefs->setDouble( "/tools/calligraphic/mass", adj->value );
4689 update_presets_list(tbl);
4690 }
4692 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
4693 {
4694 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4695 prefs->setDouble( "/tools/calligraphic/wiggle", adj->value );
4696 update_presets_list(tbl);
4697 }
4699 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
4700 {
4701 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4702 prefs->setDouble( "/tools/calligraphic/angle", adj->value );
4703 update_presets_list(tbl);
4704 }
4706 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
4707 {
4708 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4709 prefs->setDouble( "/tools/calligraphic/width", adj->value );
4710 update_presets_list(tbl);
4711 }
4713 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
4714 {
4715 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4716 prefs->setDouble("/tools/calligraphic/thinning", adj->value );
4717 update_presets_list(tbl);
4718 }
4720 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
4721 {
4722 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4723 prefs->setDouble( "/tools/calligraphic/flatness", adj->value );
4724 update_presets_list(tbl);
4725 }
4727 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
4728 {
4729 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4730 prefs->setDouble( "/tools/calligraphic/tremor", adj->value );
4731 update_presets_list(tbl);
4732 }
4734 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
4735 {
4736 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4737 prefs->setDouble( "/tools/calligraphic/cap_rounding", adj->value );
4738 update_presets_list(tbl);
4739 }
4741 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject* tbl )
4742 {
4743 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4744 prefs->setBool("/tools/calligraphic/usepressure", gtk_toggle_action_get_active( act ));
4745 update_presets_list(tbl);
4746 }
4748 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject* tbl )
4749 {
4750 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4751 prefs->setBool("/tools/calligraphic/tracebackground", gtk_toggle_action_get_active( act ));
4752 update_presets_list(tbl);
4753 }
4755 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject* tbl )
4756 {
4757 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4758 GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
4759 prefs->setBool("/tools/calligraphic/usetilt", gtk_toggle_action_get_active( act ));
4760 update_presets_list(tbl);
4761 if (calligraphy_angle )
4762 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
4763 }
4766 static gchar const *const widget_names[] = {
4767 "width",
4768 "mass",
4769 "wiggle",
4770 "angle",
4771 "thinning",
4772 "tremor",
4773 "flatness",
4774 "cap_rounding",
4775 "usepressure",
4776 "tracebackground",
4777 "usetilt"
4778 };
4781 static void sp_dcc_build_presets_list(GObject *tbl)
4782 {
4783 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4785 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4786 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4787 gtk_list_store_clear (model);
4789 {
4790 GtkTreeIter iter;
4791 gtk_list_store_append( model, &iter );
4792 gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4793 }
4795 // iterate over all presets to populate the list
4796 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4797 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4798 int ii=1;
4800 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i) {
4801 GtkTreeIter iter;
4802 Glib::ustring preset_name = prefs->getString(*i + "/name");
4803 gtk_list_store_append( model, &iter );
4804 gtk_list_store_set( model, &iter, 0, _(preset_name.data()), 1, ii++, -1 );
4805 }
4807 {
4808 GtkTreeIter iter;
4809 gtk_list_store_append( model, &iter );
4810 gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4811 g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4812 }
4814 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4816 update_presets_list (tbl);
4817 }
4819 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4820 {
4821 using Inkscape::UI::Dialog::CalligraphicProfileRename;
4822 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4823 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4824 if (! desktop) return;
4826 if (g_object_get_data(tbl, "presets_blocked"))
4827 return;
4829 CalligraphicProfileRename::show(desktop);
4830 if ( !CalligraphicProfileRename::applied()) {
4831 // dialog cancelled
4832 update_presets_list (tbl);
4833 return;
4834 }
4835 Glib::ustring profile_name = CalligraphicProfileRename::getProfileName();
4837 if (profile_name.empty()) {
4838 // empty name entered
4839 update_presets_list (tbl);
4840 return;
4841 }
4843 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4845 // If there's a preset with the given name, find it and set save_path appropriately
4846 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4847 int total_presets = presets.size();
4848 int new_index = -1;
4849 Glib::ustring save_path; // profile pref path without a trailing slash
4851 int temp_index = 0;
4852 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++temp_index) {
4853 Glib::ustring name = prefs->getString(*i + "/name");
4854 if (!name.empty() && profile_name == name) {
4855 new_index = temp_index;
4856 save_path = *i;
4857 break;
4858 }
4859 }
4861 if (new_index == -1) {
4862 // no preset with this name, create
4863 new_index = total_presets + 1;
4864 gchar *profile_id = g_strdup_printf("/dcc%d", new_index);
4865 save_path = Glib::ustring("/tools/calligraphic/preset") + profile_id;
4866 g_free(profile_id);
4867 }
4869 for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4870 gchar const *const widget_name = widget_names[i];
4871 void *widget = g_object_get_data(tbl, widget_name);
4872 if (widget) {
4873 if (GTK_IS_ADJUSTMENT(widget)) {
4874 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4875 prefs->setDouble(save_path + "/" + widget_name, gtk_adjustment_get_value(adj));
4876 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4877 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4878 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4879 prefs->setBool(save_path + "/" + widget_name, gtk_toggle_action_get_active(toggle));
4880 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4881 } else {
4882 g_warning("Unknown widget type for preset: %s\n", widget_name);
4883 }
4884 } else {
4885 g_warning("Bad key when writing preset: %s\n", widget_name);
4886 }
4887 }
4888 prefs->setString(save_path + "/name", profile_name);
4890 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4891 sp_dcc_build_presets_list (tbl);
4892 }
4895 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4897 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4899 gint preset_index = ege_select_one_action_get_active( act );
4900 // This is necessary because EgeSelectOneAction spams us with GObject "changed" signal calls
4901 // even when the preset is not changed. It would be good to replace it with something more
4902 // modern. Index 0 means "No preset", so we don't do anything.
4903 if (preset_index == 0) return;
4905 gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4907 if (preset_index == save_presets_index) {
4908 // this is the Save command
4909 sp_dcc_save_profile(NULL, tbl);
4910 return;
4911 }
4913 if (g_object_get_data(tbl, "presets_blocked"))
4914 return;
4916 // preset_index is one-based so we subtract 1
4917 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4918 Glib::ustring preset_path = presets.at(preset_index - 1);
4920 if (!preset_path.empty()) {
4921 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
4923 std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(preset_path);
4925 // Shouldn't this be std::map?
4926 for (std::vector<Inkscape::Preferences::Entry>::iterator i = preset.begin(); i != preset.end(); ++i) {
4927 Glib::ustring entry_name = i->getEntryName();
4928 if (entry_name == "id" || entry_name == "name") continue;
4929 void *widget = g_object_get_data(tbl, entry_name.data());
4930 if (widget) {
4931 if (GTK_IS_ADJUSTMENT(widget)) {
4932 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4933 gtk_adjustment_set_value(adj, i->getDouble());
4934 //std::cout << "set adj " << attr_name << " to " << v << "\n";
4935 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4936 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4937 gtk_toggle_action_set_active(toggle, i->getBool());
4938 //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4939 } else {
4940 g_warning("Unknown widget type for preset: %s\n", entry_name.data());
4941 }
4942 } else {
4943 g_warning("Bad key found in a preset record: %s\n", entry_name.data());
4944 }
4945 }
4946 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4947 }
4948 }
4951 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4952 {
4953 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4954 {
4955 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4957 EgeAdjustmentAction* calligraphy_angle = 0;
4959 {
4960 /* Width */
4961 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4962 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4963 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4964 _("Pen Width"), _("Width:"),
4965 _("The width of the calligraphic pen (relative to the visible canvas area)"),
4966 "/tools/calligraphic/width", 15,
4967 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4968 1, 100, 1.0, 10.0,
4969 labels, values, G_N_ELEMENTS(labels),
4970 sp_ddc_width_value_changed, 1, 0 );
4971 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4972 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4973 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4974 }
4976 {
4977 /* Thinning */
4978 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4979 gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4980 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4981 _("Stroke Thinning"), _("Thinning:"),
4982 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4983 "/tools/calligraphic/thinning", 10,
4984 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4985 -100, 100, 1, 10.0,
4986 labels, values, G_N_ELEMENTS(labels),
4987 sp_ddc_velthin_value_changed, 1, 0);
4988 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4989 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4990 }
4992 {
4993 /* Angle */
4994 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4995 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4996 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4997 _("Pen Angle"), _("Angle:"),
4998 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4999 "/tools/calligraphic/angle", 30,
5000 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
5001 -90.0, 90.0, 1.0, 10.0,
5002 labels, values, G_N_ELEMENTS(labels),
5003 sp_ddc_angle_value_changed, 1, 0 );
5004 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5005 g_object_set_data( holder, "angle_action", eact );
5006 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5007 calligraphy_angle = eact;
5008 }
5010 {
5011 /* Fixation */
5012 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
5013 gdouble values[] = {0, 20, 40, 60, 90, 100};
5014 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
5015 _("Fixation"), _("Fixation:"),
5016 _("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"),
5017 "/tools/calligraphic/flatness", 90,
5018 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5019 0.0, 100, 1.0, 10.0,
5020 labels, values, G_N_ELEMENTS(labels),
5021 sp_ddc_flatness_value_changed, 1, 0);
5022 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5023 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5024 }
5026 {
5027 /* Cap Rounding */
5028 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
5029 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
5030 // TRANSLATORS: "cap" means "end" (both start and finish) here
5031 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
5032 _("Cap rounding"), _("Caps:"),
5033 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
5034 "/tools/calligraphic/cap_rounding", 0.0,
5035 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5036 0.0, 5.0, 0.01, 0.1,
5037 labels, values, G_N_ELEMENTS(labels),
5038 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
5039 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5040 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5041 }
5043 {
5044 /* Tremor */
5045 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
5046 gdouble values[] = {0, 10, 20, 40, 60, 100};
5047 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
5048 _("Stroke Tremor"), _("Tremor:"),
5049 _("Increase to make strokes rugged and trembling"),
5050 "/tools/calligraphic/tremor", 0.0,
5051 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5052 0.0, 100, 1, 10.0,
5053 labels, values, G_N_ELEMENTS(labels),
5054 sp_ddc_tremor_value_changed, 1, 0);
5056 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5057 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5058 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5059 }
5061 {
5062 /* Wiggle */
5063 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
5064 gdouble values[] = {0, 20, 40, 60, 100};
5065 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
5066 _("Pen Wiggle"), _("Wiggle:"),
5067 _("Increase to make the pen waver and wiggle"),
5068 "/tools/calligraphic/wiggle", 0.0,
5069 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5070 0.0, 100, 1, 10.0,
5071 labels, values, G_N_ELEMENTS(labels),
5072 sp_ddc_wiggle_value_changed, 1, 0);
5073 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5074 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5075 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5076 }
5078 {
5079 /* Mass */
5080 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
5081 gdouble values[] = {0.0, 2, 10, 20, 50, 100};
5082 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
5083 _("Pen Mass"), _("Mass:"),
5084 _("Increase to make the pen drag behind, as if slowed by inertia"),
5085 "/tools/calligraphic/mass", 2.0,
5086 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5087 0.0, 100, 1, 10.0,
5088 labels, values, G_N_ELEMENTS(labels),
5089 sp_ddc_mass_value_changed, 1, 0);
5090 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5091 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5092 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5093 }
5096 /* Trace Background button */
5097 {
5098 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
5099 _("Trace Background"),
5100 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
5101 INKSCAPE_ICON_DRAW_TRACE_BACKGROUND,
5102 Inkscape::ICON_SIZE_DECORATION );
5103 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5104 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
5105 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/tracebackground", false) );
5106 g_object_set_data( holder, "tracebackground", act );
5107 }
5109 /* Use Pressure button */
5110 {
5111 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
5112 _("Pressure"),
5113 _("Use the pressure of the input device to alter the width of the pen"),
5114 INKSCAPE_ICON_DRAW_USE_PRESSURE,
5115 Inkscape::ICON_SIZE_DECORATION );
5116 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5117 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
5118 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usepressure", true) );
5119 g_object_set_data( holder, "usepressure", act );
5120 }
5122 /* Use Tilt button */
5123 {
5124 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
5125 _("Tilt"),
5126 _("Use the tilt of the input device to alter the angle of the pen's nib"),
5127 INKSCAPE_ICON_DRAW_USE_TILT,
5128 Inkscape::ICON_SIZE_DECORATION );
5129 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5130 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
5131 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs->getBool("/tools/calligraphic/usetilt", true) );
5132 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usetilt", true) );
5133 g_object_set_data( holder, "usetilt", act );
5134 }
5136 /*calligraphic profile */
5137 {
5138 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5139 EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
5140 ege_select_one_action_set_appearance (act1, "compact");
5141 g_object_set_data (holder, "profile_selector", act1 );
5143 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
5145 sp_dcc_build_presets_list (holder);
5147 g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
5148 gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
5149 }
5150 }
5151 }
5154 //########################
5155 //## Circle / Arc ##
5156 //########################
5158 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
5159 {
5160 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
5161 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
5163 if (v1 == 0 && v2 == 0) {
5164 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
5165 gtk_action_set_sensitive( ocb, FALSE );
5166 gtk_action_set_sensitive( make_whole, FALSE );
5167 }
5168 } else {
5169 gtk_action_set_sensitive( ocb, TRUE );
5170 gtk_action_set_sensitive( make_whole, TRUE );
5171 }
5172 }
5174 static void
5175 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
5176 {
5177 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5179 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5180 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5181 prefs->setDouble(Glib::ustring("/tools/shapes/arc/") + value_name, adj->value);
5182 }
5184 // quit if run by the attr_changed listener
5185 if (g_object_get_data( tbl, "freeze" )) {
5186 return;
5187 }
5189 // in turn, prevent listener from responding
5190 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5192 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
5194 bool modmade = false;
5195 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5196 items != NULL;
5197 items = items->next)
5198 {
5199 SPItem *item = SP_ITEM(items->data);
5201 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
5203 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
5204 SPArc *arc = SP_ARC(item);
5206 if (!strcmp(value_name, "start"))
5207 ge->start = (adj->value * M_PI)/ 180;
5208 else
5209 ge->end = (adj->value * M_PI)/ 180;
5211 sp_genericellipse_normalize(ge);
5212 ((SPObject *)arc)->updateRepr();
5213 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
5215 modmade = true;
5216 }
5217 }
5219 g_free(namespaced_name);
5221 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
5223 sp_arctb_sensitivize( tbl, adj->value, other->value );
5225 if (modmade) {
5226 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
5227 _("Arc: Change start/end"));
5228 }
5230 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5231 }
5234 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
5235 {
5236 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
5237 }
5239 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
5240 {
5241 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
5242 }
5245 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
5246 {
5247 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5248 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5249 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5250 prefs->setBool("/tools/shapes/arc/open", ege_select_one_action_get_active(act) != 0);
5251 }
5253 // quit if run by the attr_changed listener
5254 if (g_object_get_data( tbl, "freeze" )) {
5255 return;
5256 }
5258 // in turn, prevent listener from responding
5259 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5261 bool modmade = false;
5263 if ( ege_select_one_action_get_active(act) != 0 ) {
5264 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5265 items != NULL;
5266 items = items->next)
5267 {
5268 if (SP_IS_ARC((SPItem *) items->data)) {
5269 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5270 repr->setAttribute("sodipodi:open", "true");
5271 SP_OBJECT((SPItem *) items->data)->updateRepr();
5272 modmade = true;
5273 }
5274 }
5275 } else {
5276 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5277 items != NULL;
5278 items = items->next)
5279 {
5280 if (SP_IS_ARC((SPItem *) items->data)) {
5281 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5282 repr->setAttribute("sodipodi:open", NULL);
5283 SP_OBJECT((SPItem *) items->data)->updateRepr();
5284 modmade = true;
5285 }
5286 }
5287 }
5289 if (modmade) {
5290 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
5291 _("Arc: Change open/closed"));
5292 }
5294 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5295 }
5297 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
5298 {
5299 GtkAdjustment *adj;
5300 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
5301 gtk_adjustment_set_value(adj, 0.0);
5302 gtk_adjustment_value_changed(adj);
5304 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
5305 gtk_adjustment_set_value(adj, 0.0);
5306 gtk_adjustment_value_changed(adj);
5308 spinbutton_defocus( GTK_OBJECT(obj) );
5309 }
5311 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
5312 gchar const */*old_value*/, gchar const */*new_value*/,
5313 bool /*is_interactive*/, gpointer data)
5314 {
5315 GObject *tbl = G_OBJECT(data);
5317 // quit if run by the _changed callbacks
5318 if (g_object_get_data( tbl, "freeze" )) {
5319 return;
5320 }
5322 // in turn, prevent callbacks from responding
5323 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5325 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
5326 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
5328 GtkAdjustment *adj1,*adj2;
5329 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
5330 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
5331 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
5332 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
5334 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
5336 char const *openstr = NULL;
5337 openstr = repr->attribute("sodipodi:open");
5338 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
5340 if (openstr) {
5341 ege_select_one_action_set_active( ocb, 1 );
5342 } else {
5343 ege_select_one_action_set_active( ocb, 0 );
5344 }
5346 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5347 }
5349 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
5350 NULL, /* child_added */
5351 NULL, /* child_removed */
5352 arc_tb_event_attr_changed,
5353 NULL, /* content_changed */
5354 NULL /* order_changed */
5355 };
5358 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
5359 {
5360 int n_selected = 0;
5361 Inkscape::XML::Node *repr = NULL;
5363 purge_repr_listener( tbl, tbl );
5365 for (GSList const *items = selection->itemList();
5366 items != NULL;
5367 items = items->next)
5368 {
5369 if (SP_IS_ARC((SPItem *) items->data)) {
5370 n_selected++;
5371 repr = SP_OBJECT_REPR((SPItem *) items->data);
5372 }
5373 }
5375 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
5377 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
5378 if (n_selected == 0) {
5379 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
5380 } else if (n_selected == 1) {
5381 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
5382 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5384 if (repr) {
5385 g_object_set_data( tbl, "repr", repr );
5386 Inkscape::GC::anchor(repr);
5387 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
5388 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
5389 }
5390 } else {
5391 // FIXME: implement averaging of all parameters for multiple selected
5392 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
5393 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5394 sp_arctb_sensitivize( tbl, 1, 0 );
5395 }
5396 }
5399 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5400 {
5401 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5403 EgeAdjustmentAction* eact = 0;
5404 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
5407 {
5408 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
5409 ege_output_action_set_use_markup( act, TRUE );
5410 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5411 g_object_set_data( holder, "mode_action", act );
5412 }
5414 /* Start */
5415 {
5416 eact = create_adjustment_action( "ArcStartAction",
5417 _("Start"), _("Start:"),
5418 _("The angle (in degrees) from the horizontal to the arc's start point"),
5419 "/tools/shapes/arc/start", 0.0,
5420 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
5421 -360.0, 360.0, 1.0, 10.0,
5422 0, 0, 0,
5423 sp_arctb_start_value_changed);
5424 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5425 }
5427 /* End */
5428 {
5429 eact = create_adjustment_action( "ArcEndAction",
5430 _("End"), _("End:"),
5431 _("The angle (in degrees) from the horizontal to the arc's end point"),
5432 "/tools/shapes/arc/end", 0.0,
5433 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
5434 -360.0, 360.0, 1.0, 10.0,
5435 0, 0, 0,
5436 sp_arctb_end_value_changed);
5437 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5438 }
5440 /* Segments / Pie checkbox */
5441 {
5442 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5444 GtkTreeIter iter;
5445 gtk_list_store_append( model, &iter );
5446 gtk_list_store_set( model, &iter,
5447 0, _("Closed arc"),
5448 1, _("Switch to segment (closed shape with two radii)"),
5449 2, INKSCAPE_ICON_DRAW_ELLIPSE_SEGMENT,
5450 -1 );
5452 gtk_list_store_append( model, &iter );
5453 gtk_list_store_set( model, &iter,
5454 0, _("Open Arc"),
5455 1, _("Switch to arc (unclosed shape)"),
5456 2, INKSCAPE_ICON_DRAW_ELLIPSE_ARC,
5457 -1 );
5459 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5460 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5461 g_object_set_data( holder, "open_action", act );
5463 ege_select_one_action_set_appearance( act, "full" );
5464 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5465 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5466 ege_select_one_action_set_icon_column( act, 2 );
5467 ege_select_one_action_set_icon_size( act, secondarySize );
5468 ege_select_one_action_set_tooltip_column( act, 1 );
5470 bool isClosed = !prefs->getBool("/tools/shapes/arc/open", false);
5471 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
5472 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
5473 }
5475 /* Make Whole */
5476 {
5477 InkAction* inky = ink_action_new( "ArcResetAction",
5478 _("Make whole"),
5479 _("Make the shape a whole ellipse, not arc or segment"),
5480 INKSCAPE_ICON_DRAW_ELLIPSE_WHOLE,
5481 secondarySize );
5482 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
5483 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5484 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
5485 g_object_set_data( holder, "make_whole", inky );
5486 }
5488 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
5489 // sensitivize make whole and open checkbox
5490 {
5491 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
5492 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
5493 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
5494 }
5497 sigc::connection *connection = new sigc::connection(
5498 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
5499 );
5500 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
5501 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
5502 }
5507 // toggle button callbacks and updaters
5509 //########################
5510 //## Dropper ##
5511 //########################
5513 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
5514 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5515 prefs->setInt( "/tools/dropper/pick", gtk_toggle_action_get_active( act ) );
5516 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
5517 if ( set_action ) {
5518 if ( gtk_toggle_action_get_active( act ) ) {
5519 gtk_action_set_sensitive( set_action, TRUE );
5520 } else {
5521 gtk_action_set_sensitive( set_action, FALSE );
5522 }
5523 }
5525 spinbutton_defocus(GTK_OBJECT(tbl));
5526 }
5528 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
5529 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5530 prefs->setBool( "/tools/dropper/setalpha", gtk_toggle_action_get_active( act ) );
5531 spinbutton_defocus(GTK_OBJECT(tbl));
5532 }
5535 /**
5536 * Dropper auxiliary toolbar construction and setup.
5537 *
5538 * TODO: Would like to add swatch of current color.
5539 * TODO: Add queue of last 5 or so colors selected with new swatches so that
5540 * can drag and drop places. Will provide a nice mixing palette.
5541 */
5542 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
5543 {
5544 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5545 gint pickAlpha = prefs->getInt( "/tools/dropper/pick", 1 );
5547 {
5548 EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
5549 ege_output_action_set_use_markup( act, TRUE );
5550 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5551 }
5553 {
5554 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
5555 _("Pick opacity"),
5556 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
5557 NULL,
5558 Inkscape::ICON_SIZE_DECORATION );
5559 g_object_set( act, "short_label", _("Pick"), NULL );
5560 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5561 g_object_set_data( holder, "pick_action", act );
5562 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
5563 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
5564 }
5566 {
5567 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
5568 _("Assign opacity"),
5569 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
5570 NULL,
5571 Inkscape::ICON_SIZE_DECORATION );
5572 g_object_set( act, "short_label", _("Assign"), NULL );
5573 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5574 g_object_set_data( holder, "set_action", act );
5575 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/dropper/setalpha", true) );
5576 // make sure it's disabled if we're not picking alpha
5577 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
5578 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
5579 }
5580 }
5583 //########################
5584 //## LPETool ##
5585 //########################
5587 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
5589 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
5590 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
5591 {
5592 using namespace Inkscape::LivePathEffect;
5594 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
5595 SPEventContext *ec = desktop->event_context;
5596 if (!SP_IS_LPETOOL_CONTEXT(ec)) {
5597 return;
5598 }
5600 // only take action if run by the attr_changed listener
5601 if (!g_object_get_data(tbl, "freeze")) {
5602 // in turn, prevent listener from responding
5603 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5605 gint mode = ege_select_one_action_get_active(act);
5606 EffectType type = lpesubtools[mode].type;
5608 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5609 bool success = lpetool_try_construction(lc, type);
5610 if (success) {
5611 // since the construction was already performed, we set the state back to inactive
5612 ege_select_one_action_set_active(act, 0);
5613 mode = 0;
5614 } else {
5615 // switch to the chosen subtool
5616 SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
5617 }
5619 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5620 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5621 prefs->setInt( "/tools/lpetool/mode", mode );
5622 }
5624 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
5625 }
5626 }
5628 void sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject */*tbl*/)
5629 {
5630 SPEventContext *ec = selection->desktop()->event_context;
5631 if (!SP_IS_LPETOOL_CONTEXT(ec))
5632 return;
5634 lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
5635 }
5637 void
5638 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
5639 {
5640 using namespace Inkscape::LivePathEffect;
5641 SPEventContext *ec = selection->desktop()->event_context;
5642 if (!SP_IS_LPETOOL_CONTEXT(ec))
5643 return;
5644 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
5646 lpetool_delete_measuring_items(lc);
5647 lpetool_create_measuring_items(lc, selection);
5649 // activate line segment combo box if a single item with LPELineSegment is selected
5650 GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
5651 SPItem *item = selection->singleItem();
5652 if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
5653 SPLPEItem *lpeitem = SP_LPE_ITEM(item);
5654 Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
5655 if (lpe && lpe->effectType() == LINE_SEGMENT) {
5656 LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
5657 g_object_set_data(tbl, "currentlpe", lpe);
5658 g_object_set_data(tbl, "currentlpeitem", lpeitem);
5659 gtk_action_set_sensitive(w, TRUE);
5660 ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
5661 } else {
5662 g_object_set_data(tbl, "currentlpe", NULL);
5663 g_object_set_data(tbl, "currentlpeitem", NULL);
5664 gtk_action_set_sensitive(w, FALSE);
5665 }
5666 } else {
5667 g_object_set_data(tbl, "currentlpe", NULL);
5668 g_object_set_data(tbl, "currentlpeitem", NULL);
5669 gtk_action_set_sensitive(w, FALSE);
5670 }
5671 }
5673 static void
5674 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
5675 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5676 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5678 bool show = gtk_toggle_action_get_active( act );
5679 prefs->setBool("/tools/lpetool/show_bbox", show);
5681 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5682 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5683 lpetool_context_reset_limiting_bbox(lc);
5684 }
5685 }
5687 static void
5688 lpetool_toggle_show_measuring_info (GtkToggleAction *act, GObject *tbl) {
5689 SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
5690 if (!tools_isactive(desktop, TOOLS_LPETOOL))
5691 return;
5693 GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
5694 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5695 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5696 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5697 bool show = gtk_toggle_action_get_active( act );
5698 prefs->setBool("/tools/lpetool/show_measuring_info", show);
5699 lpetool_show_measuring_info(lc, show);
5700 gtk_action_set_sensitive(GTK_ACTION(unitact), show);
5701 }
5702 }
5704 static void lpetool_unit_changed(GtkAction* /*act*/, GObject* tbl) {
5705 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
5706 SPUnit const *unit = tracker->getActiveUnit();
5707 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5708 prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5710 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5711 if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
5712 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5713 lpetool_delete_measuring_items(lc);
5714 lpetool_create_measuring_items(lc);
5715 }
5716 }
5718 static void
5719 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
5720 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5721 Inkscape::Selection *selection = desktop->selection;
5723 Geom::OptRect bbox = selection->bounds();
5725 if (bbox) {
5726 Geom::Point A(bbox->min());
5727 Geom::Point B(bbox->max());
5729 A *= desktop->doc2dt();
5730 B *= desktop->doc2dt();
5732 // TODO: should we provide a way to store points in prefs?
5733 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5734 prefs->setDouble("/tools/lpetool/bbox_upperleftx", A[Geom::X]);
5735 prefs->setDouble("/tools/lpetool/bbox_upperlefty", A[Geom::Y]);
5736 prefs->setDouble("/tools/lpetool/bbox_lowerrightx", B[Geom::X]);
5737 prefs->setDouble("/tools/lpetool/bbox_lowerrighty", B[Geom::Y]);
5739 lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
5740 }
5742 gtk_toggle_action_set_active(act, false);
5743 }
5745 static void
5746 sp_line_segment_build_list(GObject *tbl)
5747 {
5748 g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
5750 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
5751 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
5752 gtk_list_store_clear (model);
5754 // TODO: we add the entries of rht combo box manually; later this should be done automatically
5755 {
5756 GtkTreeIter iter;
5757 gtk_list_store_append( model, &iter );
5758 gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
5759 gtk_list_store_append( model, &iter );
5760 gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
5761 gtk_list_store_append( model, &iter );
5762 gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
5763 gtk_list_store_append( model, &iter );
5764 gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
5765 }
5767 g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5768 }
5770 static void
5771 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
5772 using namespace Inkscape::LivePathEffect;
5774 // quit if run by the attr_changed listener
5775 if (g_object_get_data(tbl, "freeze")) {
5776 return;
5777 }
5779 // in turn, prevent listener from responding
5780 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5782 LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
5783 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5784 if (lpeitem) {
5785 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5786 lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
5787 sp_lpe_item_update_patheffect(lpeitem, true, true);
5788 }
5790 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5791 }
5793 static void
5794 lpetool_open_lpe_dialog (GtkToggleAction *act, gpointer data) {
5795 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5797 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5798 sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5799 }
5800 gtk_toggle_action_set_active(act, false);
5801 }
5803 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5804 {
5805 UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5806 tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5807 g_object_set_data(holder, "tracker", tracker);
5808 SPUnit const *unit = tracker->getActiveUnit();
5810 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5811 prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5813 /** Automatically create a list of LPEs that get added to the toolbar **/
5814 {
5815 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5817 GtkTreeIter iter;
5819 // the first toggle button represents the state that no subtool is active (remove this when
5820 // this can be modeled by EgeSelectOneAction or some other action)
5821 gtk_list_store_append( model, &iter );
5822 gtk_list_store_set( model, &iter,
5823 0, _("All inactive"),
5824 1, _("No geometric tool is active"),
5825 2, "draw-geometry-inactive",
5826 -1 );
5828 Inkscape::LivePathEffect::EffectType type;
5829 for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5830 type = lpesubtools[i].type;
5831 gtk_list_store_append( model, &iter );
5832 gtk_list_store_set( model, &iter,
5833 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5834 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5835 2, lpesubtools[i].icon_name,
5836 -1 );
5837 }
5839 EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5840 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5841 g_object_set_data( holder, "lpetool_mode_action", act );
5843 ege_select_one_action_set_appearance( act, "full" );
5844 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5845 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5846 ege_select_one_action_set_icon_column( act, 2 );
5847 ege_select_one_action_set_tooltip_column( act, 1 );
5849 gint lpeToolMode = prefs->getInt("/tools/lpetool/mode", 0);
5850 ege_select_one_action_set_active( act, lpeToolMode );
5851 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5852 }
5854 /* Show limiting bounding box */
5855 {
5856 InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5857 _("Show limiting bounding box"),
5858 _("Show bounding box (used to cut infinite lines)"),
5859 "show-bounding-box",
5860 Inkscape::ICON_SIZE_DECORATION );
5861 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5862 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5863 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_bbox", true ) );
5864 }
5866 /* Set limiting bounding box to bbox of current selection */
5867 {
5868 InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5869 _("Get limiting bounding box from selection"),
5870 _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5871 "draw-geometry-set-bounding-box",
5872 Inkscape::ICON_SIZE_DECORATION );
5873 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5874 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5875 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5876 }
5879 /* Combo box to choose line segment type */
5880 {
5881 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5882 EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5883 ege_select_one_action_set_appearance (act, "compact");
5884 g_object_set_data (holder, "lpetool_line_segment_action", act );
5886 g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5888 sp_line_segment_build_list (holder);
5890 g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5891 gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5892 gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5893 }
5895 /* Display measuring info for selected items */
5896 {
5897 InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5898 _("Display measuring info"),
5899 _("Display measuring info for selected items"),
5900 "draw-geometry-show-measuring-info",
5901 Inkscape::ICON_SIZE_DECORATION );
5902 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5903 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
5904 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_measuring_info", true ) );
5905 }
5907 // add the units menu
5908 {
5909 GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5910 gtk_action_group_add_action( mainActions, act );
5911 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5912 g_object_set_data(holder, "lpetool_units_action", act);
5913 gtk_action_set_sensitive(act, prefs->getBool("/tools/lpetool/show_measuring_info", true));
5914 }
5916 /* Open LPE dialog (to adapt parameters numerically) */
5917 {
5918 InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
5919 _("Open LPE dialog"),
5920 _("Open LPE dialog (to adapt parameters numerically)"),
5921 "dialog-geometry",
5922 Inkscape::ICON_SIZE_DECORATION );
5923 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5924 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
5925 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5926 }
5928 //watch selection
5929 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5931 sigc::connection *c_selection_modified =
5932 new sigc::connection (sp_desktop_selection (desktop)->connectModified
5933 (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
5934 pool->add_connection ("selection-modified", c_selection_modified);
5936 sigc::connection *c_selection_changed =
5937 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5938 (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5939 pool->add_connection ("selection-changed", c_selection_changed);
5940 }
5942 //########################
5943 //## Eraser ##
5944 //########################
5946 static void sp_erc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
5947 {
5948 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5949 prefs->setDouble( "/tools/eraser/width", adj->value );
5950 update_presets_list(tbl);
5951 }
5953 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5954 {
5955 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5956 bool eraserMode = ege_select_one_action_get_active( act ) != 0;
5957 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5958 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5959 prefs->setBool( "/tools/eraser/mode", eraserMode );
5960 }
5962 // only take action if run by the attr_changed listener
5963 if (!g_object_get_data( tbl, "freeze" )) {
5964 // in turn, prevent listener from responding
5965 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5967 if ( eraserMode != 0 ) {
5968 } else {
5969 }
5970 // TODO finish implementation
5972 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5973 }
5974 }
5976 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5977 {
5978 {
5979 /* Width */
5980 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5981 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5982 EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5983 _("Pen Width"), _("Width:"),
5984 _("The width of the eraser pen (relative to the visible canvas area)"),
5985 "/tools/eraser/width", 15,
5986 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5987 1, 100, 1.0, 10.0,
5988 labels, values, G_N_ELEMENTS(labels),
5989 sp_erc_width_value_changed, 1, 0);
5990 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5991 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5992 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5993 }
5995 {
5996 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5998 GtkTreeIter iter;
5999 gtk_list_store_append( model, &iter );
6000 gtk_list_store_set( model, &iter,
6001 0, _("Delete"),
6002 1, _("Delete objects touched by the eraser"),
6003 2, INKSCAPE_ICON_DRAW_ERASER_DELETE_OBJECTS,
6004 -1 );
6006 gtk_list_store_append( model, &iter );
6007 gtk_list_store_set( model, &iter,
6008 0, _("Cut"),
6009 1, _("Cut out from objects"),
6010 2, INKSCAPE_ICON_PATH_DIFFERENCE,
6011 -1 );
6013 EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
6014 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
6015 g_object_set_data( holder, "eraser_mode_action", act );
6017 ege_select_one_action_set_appearance( act, "full" );
6018 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
6019 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
6020 ege_select_one_action_set_icon_column( act, 2 );
6021 ege_select_one_action_set_tooltip_column( act, 1 );
6023 /// @todo Convert to boolean?
6024 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6025 gint eraserMode = prefs->getBool("/tools/eraser/mode") ? 1 : 0;
6026 ege_select_one_action_set_active( act, eraserMode );
6027 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
6028 }
6030 }
6032 //########################
6033 //## Text Toolbox ##
6034 //########################
6035 /*
6036 static void
6037 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
6038 {
6039 //Call back for letter sizing spinbutton
6040 }
6042 static void
6043 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
6044 {
6045 //Call back for line height spinbutton
6046 }
6048 static void
6049 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
6050 {
6051 //Call back for horizontal kerning spinbutton
6052 }
6054 static void
6055 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
6056 {
6057 //Call back for vertical kerning spinbutton
6058 }
6060 static void
6061 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
6062 {
6063 //Call back for letter rotation spinbutton
6064 }*/
6066 namespace {
6068 void
6069 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
6070 {
6071 // quit if run by the _changed callbacks
6072 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6073 return;
6074 }
6076 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6078 SPStyle *query =
6079 sp_style_new (SP_ACTIVE_DOCUMENT);
6081 int result_family =
6082 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6084 int result_style =
6085 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6087 int result_numbers =
6088 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6090 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6092 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6093 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
6094 // there are no texts in selection, read from prefs
6096 sp_style_read_from_prefs(query, "/tools/text");
6098 if (g_object_get_data(tbl, "text_style_from_prefs")) {
6099 // do not reset the toolbar style from prefs if we already did it last time
6100 sp_style_unref(query);
6101 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6102 return;
6103 }
6104 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
6105 } else {
6106 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
6107 }
6109 if (query->text)
6110 {
6111 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
6112 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6113 gtk_entry_set_text (GTK_ENTRY (entry), "");
6115 } else if (query->text->font_specification.value || query->text->font_family.value) {
6117 Gtk::ComboBoxEntry *combo = (Gtk::ComboBoxEntry *) (g_object_get_data (G_OBJECT (tbl), "family-entry-combo"));
6118 GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6120 // Get the font that corresponds
6121 Glib::ustring familyName;
6123 font_instance * font = font_factory::Default()->FaceFromStyle(query);
6124 if (font) {
6125 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
6126 font->Unref();
6127 font = NULL;
6128 }
6130 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
6132 Gtk::TreeIter iter;
6133 try {
6134 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
6135 Glib::RefPtr<Gtk::TreeModel> model = combo->get_model();
6136 iter = model->get_iter(path);
6137 } catch (...) {
6138 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
6139 sp_style_unref(query);
6140 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6141 return;
6142 }
6144 combo->set_active (iter);
6145 }
6147 //Size
6148 {
6149 GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
6150 gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
6151 gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
6152 g_free(str);
6153 }
6155 //Anchor
6156 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
6157 {
6158 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
6159 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6160 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6161 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6162 }
6163 else
6164 {
6165 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
6166 {
6167 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
6168 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6169 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6170 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6171 }
6172 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
6173 {
6174 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
6175 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6176 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6177 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6178 }
6179 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
6180 {
6181 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
6182 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6183 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6184 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6185 }
6186 }
6188 //Style
6189 {
6190 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
6192 gboolean active = gtk_toggle_button_get_active (button);
6193 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));
6195 if (active != check)
6196 {
6197 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6198 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6199 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6200 }
6201 }
6203 {
6204 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
6206 gboolean active = gtk_toggle_button_get_active (button);
6207 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
6209 if (active != check)
6210 {
6211 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6212 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6213 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6214 }
6215 }
6217 //Orientation
6218 //locking both buttons, changing one affect all group (both)
6219 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
6220 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6222 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
6223 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
6225 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
6226 {
6227 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6228 }
6229 else
6230 {
6231 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
6232 }
6233 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6234 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
6235 }
6237 sp_style_unref(query);
6239 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6240 }
6242 void
6243 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
6244 {
6245 sp_text_toolbox_selection_changed (selection, tbl);
6246 }
6248 void
6249 sp_text_toolbox_subselection_changed (gpointer /*tc*/, GObject *tbl)
6250 {
6251 sp_text_toolbox_selection_changed (NULL, tbl);
6252 }
6254 void
6255 sp_text_toolbox_family_changed (GtkComboBoxEntry *,
6256 GObject *tbl)
6257 {
6258 // quit if run by the _changed callbacks
6259 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6260 return;
6261 }
6263 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6265 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6266 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6267 const gchar* family = gtk_entry_get_text (GTK_ENTRY (entry));
6269 //g_print ("family changed to: %s\n", family);
6271 SPStyle *query =
6272 sp_style_new (SP_ACTIVE_DOCUMENT);
6274 int result_fontspec =
6275 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6277 SPCSSAttr *css = sp_repr_css_attr_new ();
6279 // First try to get the font spec from the stored value
6280 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
6282 if (fontSpec.empty()) {
6283 // Construct a new font specification if it does not yet exist
6284 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6285 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6286 fontFromStyle->Unref();
6287 }
6289 if (!fontSpec.empty()) {
6291 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
6293 if (!newFontSpec.empty()) {
6295 if (fontSpec != newFontSpec) {
6297 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
6299 if (font) {
6300 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6302 // Set all the these just in case they were altered when finding the best
6303 // match for the new family and old style...
6305 gchar c[256];
6307 font->Family(c, 256);
6309 sp_repr_css_set_property (css, "font-family", c);
6311 font->Attribute( "weight", c, 256);
6312 sp_repr_css_set_property (css, "font-weight", c);
6314 font->Attribute("style", c, 256);
6315 sp_repr_css_set_property (css, "font-style", c);
6317 font->Attribute("stretch", c, 256);
6318 sp_repr_css_set_property (css, "font-stretch", c);
6320 font->Attribute("variant", c, 256);
6321 sp_repr_css_set_property (css, "font-variant", c);
6323 font->Unref();
6324 }
6325 }
6327 } else {
6328 // If the old font on selection (or default) was not existing on the system,
6329 // ReplaceFontSpecificationFamily does not work. In that case we fall back to blindly
6330 // setting the family reported by the family chooser.
6332 //g_print ("fallback setting family: %s\n", family);
6333 sp_repr_css_set_property (css, "-inkscape-font-specification", family);
6334 sp_repr_css_set_property (css, "font-family", family);
6335 }
6336 }
6338 // If querying returned nothing, set the default style of the tool (for new texts)
6339 if (result_fontspec == QUERY_STYLE_NOTHING)
6340 {
6341 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6342 prefs->mergeStyle("/tools/text/style", css);
6343 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
6344 }
6345 else
6346 {
6347 sp_desktop_set_style (desktop, css, true, true);
6348 }
6350 sp_style_unref(query);
6352 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6353 _("Text: Change font family"));
6354 sp_repr_css_attr_unref (css);
6356 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6358 // unfreeze
6359 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6361 // focus to canvas
6362 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6363 }
6366 void
6367 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
6368 gpointer data)
6369 {
6370 if (g_object_get_data (G_OBJECT (button), "block")) return;
6371 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
6372 int prop = GPOINTER_TO_INT(data);
6374 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6376 // move the x of all texts to preserve the same bbox
6377 Inkscape::Selection *selection = sp_desktop_selection(desktop);
6378 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
6379 if (SP_IS_TEXT((SPItem *) items->data)) {
6380 SPItem *item = SP_ITEM(items->data);
6382 unsigned writing_mode = SP_OBJECT_STYLE(item)->writing_mode.value;
6383 // below, variable names suggest horizontal move, but we check the writing direction
6384 // and move in the corresponding axis
6385 int axis;
6386 if (writing_mode == SP_CSS_WRITING_MODE_LR_TB || writing_mode == SP_CSS_WRITING_MODE_RL_TB) {
6387 axis = NR::X;
6388 } else {
6389 axis = NR::Y;
6390 }
6392 Geom::OptRect bbox
6393 = item->getBounds(Geom::identity(), SPItem::GEOMETRIC_BBOX);
6394 if (!bbox)
6395 continue;
6396 double width = bbox->dimensions()[axis];
6397 // If you want to align within some frame, other than the text's own bbox, calculate
6398 // the left and right (or top and bottom for tb text) slacks of the text inside that
6399 // frame (currently unused)
6400 double left_slack = 0;
6401 double right_slack = 0;
6402 unsigned old_align = SP_OBJECT_STYLE(item)->text_align.value;
6403 double move = 0;
6404 if (old_align == SP_CSS_TEXT_ALIGN_START || old_align == SP_CSS_TEXT_ALIGN_LEFT) {
6405 switch (prop) {
6406 case 0:
6407 move = -left_slack;
6408 break;
6409 case 1:
6410 move = width/2 + (right_slack - left_slack)/2;
6411 break;
6412 case 2:
6413 move = width + right_slack;
6414 break;
6415 }
6416 } else if (old_align == SP_CSS_TEXT_ALIGN_CENTER) {
6417 switch (prop) {
6418 case 0:
6419 move = -width/2 - left_slack;
6420 break;
6421 case 1:
6422 move = (right_slack - left_slack)/2;
6423 break;
6424 case 2:
6425 move = width/2 + right_slack;
6426 break;
6427 }
6428 } else if (old_align == SP_CSS_TEXT_ALIGN_END || old_align == SP_CSS_TEXT_ALIGN_RIGHT) {
6429 switch (prop) {
6430 case 0:
6431 move = -width - left_slack;
6432 break;
6433 case 1:
6434 move = -width/2 + (right_slack - left_slack)/2;
6435 break;
6436 case 2:
6437 move = right_slack;
6438 break;
6439 }
6440 }
6441 Geom::Point XY = SP_TEXT(item)->attributes.firstXY();
6442 if (axis == NR::X) {
6443 XY = XY + Geom::Point (move, 0);
6444 } else {
6445 XY = XY + Geom::Point (0, move);
6446 }
6447 SP_TEXT(item)->attributes.setFirstXY(XY);
6448 SP_OBJECT(item)->updateRepr();
6449 SP_OBJECT(item)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
6450 }
6451 }
6453 SPCSSAttr *css = sp_repr_css_attr_new ();
6454 switch (prop)
6455 {
6456 case 0:
6457 {
6458 sp_repr_css_set_property (css, "text-anchor", "start");
6459 sp_repr_css_set_property (css, "text-align", "start");
6460 break;
6461 }
6462 case 1:
6463 {
6464 sp_repr_css_set_property (css, "text-anchor", "middle");
6465 sp_repr_css_set_property (css, "text-align", "center");
6466 break;
6467 }
6469 case 2:
6470 {
6471 sp_repr_css_set_property (css, "text-anchor", "end");
6472 sp_repr_css_set_property (css, "text-align", "end");
6473 break;
6474 }
6476 case 3:
6477 {
6478 sp_repr_css_set_property (css, "text-anchor", "start");
6479 sp_repr_css_set_property (css, "text-align", "justify");
6480 break;
6481 }
6482 }
6484 SPStyle *query =
6485 sp_style_new (SP_ACTIVE_DOCUMENT);
6486 int result_numbers =
6487 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6489 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6490 if (result_numbers == QUERY_STYLE_NOTHING)
6491 {
6492 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6493 prefs->mergeStyle("/tools/text/style", css);
6494 }
6496 sp_style_unref(query);
6498 sp_desktop_set_style (desktop, css, true, true);
6499 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6500 _("Text: Change alignment"));
6501 sp_repr_css_attr_unref (css);
6503 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6504 }
6506 void
6507 sp_text_toolbox_style_toggled (GtkToggleButton *button,
6508 gpointer data)
6509 {
6510 if (g_object_get_data (G_OBJECT (button), "block")) return;
6512 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6513 SPCSSAttr *css = sp_repr_css_attr_new ();
6514 int prop = GPOINTER_TO_INT(data);
6515 bool active = gtk_toggle_button_get_active (button);
6517 SPStyle *query =
6518 sp_style_new (SP_ACTIVE_DOCUMENT);
6520 int result_fontspec =
6521 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6523 //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6524 //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6525 //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6527 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
6528 Glib::ustring newFontSpec = "";
6530 if (fontSpec.empty()) {
6531 // Construct a new font specification if it does not yet exist
6532 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6533 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6534 fontFromStyle->Unref();
6535 }
6537 bool nochange = true;
6538 switch (prop)
6539 {
6540 case 0:
6541 {
6542 if (!fontSpec.empty()) {
6543 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
6544 if (!newFontSpec.empty()) {
6545 // Don't even set the bold if the font didn't exist on the system
6546 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
6547 nochange = false;
6548 }
6549 }
6550 // set or reset the button according
6551 if(nochange) {
6552 gboolean check = gtk_toggle_button_get_active (button);
6554 if (active != check)
6555 {
6556 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6557 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6558 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6559 }
6560 }
6562 break;
6563 }
6565 case 1:
6566 {
6567 if (!fontSpec.empty()) {
6568 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
6569 if (!newFontSpec.empty()) {
6570 // Don't even set the italic if the font didn't exist on the system
6571 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
6572 nochange = false;
6573 }
6574 }
6575 if(nochange) {
6576 gboolean check = gtk_toggle_button_get_active (button);
6578 if (active != check)
6579 {
6580 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6581 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6582 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6583 }
6584 }
6585 break;
6586 }
6587 }
6589 if (!newFontSpec.empty()) {
6590 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6591 }
6593 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6594 if (result_fontspec == QUERY_STYLE_NOTHING)
6595 {
6596 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6597 prefs->mergeStyle("/tools/text/style", css);
6598 }
6600 sp_style_unref(query);
6602 sp_desktop_set_style (desktop, css, true, true);
6603 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6604 _("Text: Change font style"));
6605 sp_repr_css_attr_unref (css);
6607 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6608 }
6610 void
6611 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
6612 gpointer data)
6613 {
6614 if (g_object_get_data (G_OBJECT (button), "block")) {
6615 return;
6616 }
6618 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6619 SPCSSAttr *css = sp_repr_css_attr_new ();
6620 int prop = GPOINTER_TO_INT(data);
6622 switch (prop)
6623 {
6624 case 0:
6625 {
6626 sp_repr_css_set_property (css, "writing-mode", "lr");
6627 break;
6628 }
6630 case 1:
6631 {
6632 sp_repr_css_set_property (css, "writing-mode", "tb");
6633 break;
6634 }
6635 }
6637 SPStyle *query =
6638 sp_style_new (SP_ACTIVE_DOCUMENT);
6639 int result_numbers =
6640 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6642 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6643 if (result_numbers == QUERY_STYLE_NOTHING)
6644 {
6645 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6646 prefs->mergeStyle("/tools/text/style", css);
6647 }
6649 sp_desktop_set_style (desktop, css, true, true);
6650 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6651 _("Text: Change orientation"));
6652 sp_repr_css_attr_unref (css);
6654 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6655 }
6657 gboolean
6658 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6659 {
6660 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6661 if (!desktop) return FALSE;
6663 switch (get_group0_keyval (event)) {
6664 case GDK_KP_Enter: // chosen
6665 case GDK_Return:
6666 // unfreeze and update, which will defocus
6667 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6668 sp_text_toolbox_family_changed (NULL, tbl);
6669 return TRUE; // I consumed the event
6670 break;
6671 case GDK_Escape:
6672 // defocus
6673 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6674 return TRUE; // I consumed the event
6675 break;
6676 }
6677 return FALSE;
6678 }
6680 gboolean
6681 sp_text_toolbox_family_list_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject */*tbl*/)
6682 {
6683 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6684 if (!desktop) return FALSE;
6686 switch (get_group0_keyval (event)) {
6687 case GDK_KP_Enter:
6688 case GDK_Return:
6689 case GDK_Escape: // defocus
6690 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6691 return TRUE; // I consumed the event
6692 break;
6693 case GDK_w:
6694 case GDK_W:
6695 if (event->state & GDK_CONTROL_MASK) {
6696 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6697 return TRUE; // I consumed the event
6698 }
6699 break;
6700 }
6701 return FALSE;
6702 }
6705 void
6706 sp_text_toolbox_size_changed (GtkComboBox *cbox,
6707 GObject *tbl)
6708 {
6709 // quit if run by the _changed callbacks
6710 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6711 return;
6712 }
6714 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6716 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6718 // If this is not from selecting a size in the list (in which case get_active will give the
6719 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
6720 // process this event. This fixes GTK's stupid insistence on sending an activate change every
6721 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
6722 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed")) {
6723 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6724 return;
6725 }
6727 gdouble value = -1;
6728 {
6729 gchar *endptr;
6730 gchar *const text = gtk_combo_box_get_active_text(cbox);
6731 if (text) {
6732 value = g_strtod(text, &endptr);
6733 if (endptr == text) { // Conversion failed, non-numeric input.
6734 value = -1;
6735 }
6736 g_free(text);
6737 }
6738 }
6739 if (value <= 0) {
6740 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6741 return; // could not parse value
6742 }
6744 SPCSSAttr *css = sp_repr_css_attr_new ();
6745 Inkscape::CSSOStringStream osfs;
6746 osfs << value;
6747 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
6749 SPStyle *query =
6750 sp_style_new (SP_ACTIVE_DOCUMENT);
6751 int result_numbers =
6752 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6754 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6755 if (result_numbers == QUERY_STYLE_NOTHING)
6756 {
6757 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6758 prefs->mergeStyle("/tools/text/style", css);
6759 }
6761 sp_style_unref(query);
6763 sp_desktop_set_style (desktop, css, true, true);
6764 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
6765 _("Text: Change font size"));
6766 sp_repr_css_attr_unref (css);
6768 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6770 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6771 }
6773 gboolean
6774 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
6775 {
6776 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6777 if (!desktop) return FALSE;
6779 if (!g_object_get_data (tbl, "esc-pressed")) {
6780 g_object_set_data (tbl, "enter-pressed", gpointer(1));
6781 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6782 sp_text_toolbox_size_changed (cbox, tbl);
6783 g_object_set_data (tbl, "enter-pressed", gpointer(0));
6784 }
6785 return FALSE; // I consumed the event
6786 }
6789 gboolean
6790 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6791 {
6792 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6793 if (!desktop) return FALSE;
6795 switch (get_group0_keyval (event)) {
6796 case GDK_Escape: // defocus
6797 g_object_set_data (tbl, "esc-pressed", gpointer(1));
6798 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6799 g_object_set_data (tbl, "esc-pressed", gpointer(0));
6800 return TRUE; // I consumed the event
6801 break;
6802 case GDK_Return: // defocus
6803 case GDK_KP_Enter:
6804 g_object_set_data (tbl, "enter-pressed", gpointer(1));
6805 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6806 sp_text_toolbox_size_changed (cbox, tbl);
6807 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6808 g_object_set_data (tbl, "enter-pressed", gpointer(0));
6809 return TRUE; // I consumed the event
6810 break;
6811 }
6812 return FALSE;
6813 }
6815 // While editing font name in the entry, disable family_changed by freezing, otherwise completion
6816 // does not work!
6817 gboolean
6818 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
6819 GdkEventFocus */*event*/,
6820 GObject *tbl)
6821 {
6822 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6823 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1); // select all
6824 return FALSE;
6825 }
6827 gboolean
6828 sp_text_toolbox_entry_focus_out (GtkWidget *entry,
6829 GdkEventFocus */*event*/,
6830 GObject *tbl)
6831 {
6832 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6833 gtk_entry_select_region (GTK_ENTRY (entry), 0, 0); // deselect
6834 return FALSE;
6835 }
6837 void
6838 cell_data_func (GtkCellLayout */*cell_layout*/,
6839 GtkCellRenderer *cell,
6840 GtkTreeModel *tree_model,
6841 GtkTreeIter *iter,
6842 gpointer /*data*/)
6843 {
6844 gchar *family;
6845 gtk_tree_model_get(tree_model, iter, 0, &family, -1);
6846 gchar *const family_escaped = g_markup_escape_text(family, -1);
6848 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6849 int show_sample = prefs->getInt("/tools/text/show_sample_in_list", 1);
6850 if (show_sample) {
6852 Glib::ustring sample = prefs->getString("/tools/text/font_sample");
6853 gchar *const sample_escaped = g_markup_escape_text(sample.data(), -1);
6855 std::stringstream markup;
6856 markup << family_escaped << " <span foreground='darkgray' font_family='"
6857 << family_escaped << "'>" << sample_escaped << "</span>";
6858 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6860 g_free(sample_escaped);
6861 } else {
6862 g_object_set (G_OBJECT (cell), "markup", family_escaped, NULL);
6863 }
6865 g_free(family);
6866 g_free(family_escaped);
6867 }
6869 gboolean text_toolbox_completion_match_selected(GtkEntryCompletion */*widget*/,
6870 GtkTreeModel *model,
6871 GtkTreeIter *iter,
6872 GObject *tbl)
6873 {
6874 // We intercept this signal so as to fire family_changed at once (without it, you'd have to
6875 // press Enter again after choosing a completion)
6876 gchar *family = 0;
6877 gtk_tree_model_get(model, iter, 0, &family, -1);
6879 GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6880 gtk_entry_set_text (GTK_ENTRY (entry), family);
6882 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6883 sp_text_toolbox_family_changed (NULL, tbl);
6884 return TRUE;
6885 }
6888 static void
6889 cbe_add_completion (GtkComboBoxEntry *cbe, GObject *tbl){
6890 GtkEntry *entry;
6891 GtkEntryCompletion *completion;
6892 GtkTreeModel *model;
6894 entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(cbe)));
6895 completion = gtk_entry_completion_new();
6896 model = gtk_combo_box_get_model(GTK_COMBO_BOX(cbe));
6897 gtk_entry_completion_set_model(completion, model);
6898 gtk_entry_completion_set_text_column(completion, 0);
6899 gtk_entry_completion_set_inline_completion(completion, FALSE);
6900 gtk_entry_completion_set_inline_selection(completion, FALSE);
6901 gtk_entry_completion_set_popup_completion(completion, TRUE);
6902 gtk_entry_set_completion(entry, completion);
6904 g_signal_connect (G_OBJECT (completion), "match-selected", G_CALLBACK (text_toolbox_completion_match_selected), tbl);
6906 g_object_unref(completion);
6907 }
6909 void sp_text_toolbox_family_popnotify(GtkComboBox *widget,
6910 void */*property*/,
6911 GObject *tbl)
6912 {
6913 // while the drop-down is open, we disable font family changing, reenabling it only when it closes
6915 gboolean shown;
6916 g_object_get (G_OBJECT(widget), "popup-shown", &shown, NULL);
6917 if (shown) {
6918 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6919 //g_print("POP: notify: SHOWN\n");
6920 } else {
6921 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6923 // stupid GTK doesn't let us attach to events in the drop-down window, so we peek here to
6924 // find out if the drop down was closed by Enter and if so, manually update (only
6925 // necessary on Windows, on Linux it updates itself - what a mess, but we'll manage)
6926 GdkEvent *ev = gtk_get_current_event();
6927 if (ev) {
6928 //g_print ("ev type: %d\n", ev->type);
6929 if (ev->type == GDK_KEY_PRESS) {
6930 switch (get_group0_keyval ((GdkEventKey *) ev)) {
6931 case GDK_KP_Enter: // chosen
6932 case GDK_Return:
6933 {
6934 // make sure the chosen one is inserted into the entry
6935 GtkComboBox *combo = GTK_COMBO_BOX (((Gtk::ComboBox *) (g_object_get_data (tbl, "family-entry-combo")))->gobj());
6936 GtkTreeModel *model = gtk_combo_box_get_model(combo);
6937 GtkTreeIter iter;
6938 gboolean has_active = gtk_combo_box_get_active_iter (combo, &iter);
6939 if (has_active) {
6940 gchar *family;
6941 gtk_tree_model_get(model, &iter, 0, &family, -1);
6942 GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6943 gtk_entry_set_text (GTK_ENTRY (entry), family);
6944 }
6946 // update
6947 sp_text_toolbox_family_changed (NULL, tbl);
6948 break;
6949 }
6950 }
6951 }
6952 }
6954 // regardless of whether we updated, defocus the widget
6955 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6956 if (desktop)
6957 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6958 //g_print("POP: notify: HIDDEN\n");
6959 }
6960 }
6962 GtkWidget*
6963 sp_text_toolbox_new (SPDesktop *desktop)
6964 {
6965 GtkToolbar *tbl = GTK_TOOLBAR(gtk_toolbar_new());
6966 GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("/toolbox/secondary", 1));
6968 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
6969 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
6971 GtkTooltips *tt = gtk_tooltips_new();
6973 ////////////Family
6974 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
6975 Gtk::ComboBoxEntry *font_sel = Gtk::manage(new Gtk::ComboBoxEntry(store));
6977 gtk_rc_parse_string (
6978 "style \"dropdown-as-list-style\"\n"
6979 "{\n"
6980 " GtkComboBox::appears-as-list = 1\n"
6981 "}\n"
6982 "widget \"*.toolbox-fontfamily-list\" style \"dropdown-as-list-style\"");
6983 gtk_widget_set_name(GTK_WIDGET (font_sel->gobj()), "toolbox-fontfamily-list");
6984 gtk_tooltips_set_tip (tt, GTK_WIDGET (font_sel->gobj()), _("Select font family (Alt+X to access)"), "");
6986 g_signal_connect (G_OBJECT (font_sel->gobj()), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
6988 cbe_add_completion(font_sel->gobj(), G_OBJECT(tbl));
6990 gtk_toolbar_append_widget( tbl, (GtkWidget*) font_sel->gobj(), "", "");
6991 g_object_set_data (G_OBJECT (tbl), "family-entry-combo", font_sel);
6993 // expand the field a bit so as to view more of the previews in the drop-down
6994 GtkRequisition req;
6995 gtk_widget_size_request (GTK_WIDGET (font_sel->gobj()), &req);
6996 gtk_widget_set_size_request (GTK_WIDGET (font_sel->gobj()), MIN(req.width + 50, 500), -1);
6998 GtkWidget* entry = (GtkWidget*) font_sel->get_entry()->gobj();
6999 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
7001 g_signal_connect (G_OBJECT (font_sel->gobj()), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
7002 g_signal_connect (G_OBJECT (font_sel->gobj()), "notify::popup-shown",
7003 G_CALLBACK (sp_text_toolbox_family_popnotify), tbl);
7004 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
7005 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
7006 g_signal_connect (G_OBJECT (entry), "focus-out-event", G_CALLBACK (sp_text_toolbox_entry_focus_out), tbl);
7008 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
7009 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
7011 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
7012 gtk_cell_layout_clear( GTK_CELL_LAYOUT(font_sel->gobj()) );
7013 gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(font_sel->gobj()) , cell , TRUE );
7014 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT(font_sel->gobj()), cell, GtkCellLayoutDataFunc (cell_data_func), NULL, NULL);
7016 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
7017 GtkWidget *box = gtk_event_box_new ();
7018 gtk_container_add (GTK_CONTAINER (box), image);
7019 gtk_toolbar_append_widget( tbl, box, "", "");
7020 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
7021 gtk_tooltips_set_tip (tt, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
7022 gtk_widget_hide (GTK_WIDGET (box));
7023 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
7025 ////////////Size
7026 gchar const *const sizes[] = {
7027 "4", "6", "8", "9", "10", "11", "12", "13", "14",
7028 "16", "18", "20", "22", "24", "28",
7029 "32", "36", "40", "48", "56", "64", "72", "144"
7030 };
7032 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
7033 for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
7034 gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
7035 }
7036 gtk_widget_set_size_request (cbox, 80, -1);
7037 gtk_toolbar_append_widget( tbl, cbox, "", "");
7038 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
7039 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
7040 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
7041 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
7043 ////////////Text anchor
7044 GtkWidget *group = gtk_radio_button_new (NULL);
7045 GtkWidget *row = gtk_hbox_new (FALSE, 4);
7046 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
7048 // left
7049 GtkWidget *rbutton = group;
7050 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7051 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
7052 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7054 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7055 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
7056 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
7057 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
7059 // center
7060 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7061 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7062 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
7063 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7065 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7066 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
7067 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
7068 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
7070 // right
7071 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7072 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7073 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
7074 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7076 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7077 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
7078 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
7079 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
7081 // fill
7082 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7083 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7084 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
7085 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7087 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7088 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
7089 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
7090 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
7092 gtk_toolbar_append_widget( tbl, row, "", "");
7094 //spacer
7095 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
7097 ////////////Text style
7098 row = gtk_hbox_new (FALSE, 4);
7100 // bold
7101 rbutton = gtk_toggle_button_new ();
7102 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7103 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
7104 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7105 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
7107 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7108 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
7109 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
7111 // italic
7112 rbutton = gtk_toggle_button_new ();
7113 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7114 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
7115 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7116 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
7118 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7119 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
7120 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
7122 gtk_toolbar_append_widget( tbl, row, "", "");
7124 //spacer
7125 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
7127 // Text orientation
7128 group = gtk_radio_button_new (NULL);
7129 row = gtk_hbox_new (FALSE, 4);
7130 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
7132 // horizontal
7133 rbutton = group;
7134 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7135 gtk_container_add (GTK_CONTAINER (rbutton),
7136 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_HORIZONTAL));
7137 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7138 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
7140 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7141 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
7142 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
7144 // vertical
7145 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7146 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7147 gtk_container_add (GTK_CONTAINER (rbutton),
7148 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_VERTICAL));
7149 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7150 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
7152 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7153 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
7154 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
7155 gtk_toolbar_append_widget( tbl, row, "", "" );
7158 //watch selection
7159 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
7161 sigc::connection *c_selection_changed =
7162 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
7163 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
7164 pool->add_connection ("selection-changed", c_selection_changed);
7166 sigc::connection *c_selection_modified =
7167 new sigc::connection (sp_desktop_selection (desktop)->connectModified
7168 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
7169 pool->add_connection ("selection-modified", c_selection_modified);
7171 sigc::connection *c_subselection_changed =
7172 new sigc::connection (desktop->connectToolSubselectionChanged
7173 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
7174 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
7176 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
7179 gtk_widget_show_all( GTK_WIDGET(tbl) );
7181 return GTK_WIDGET(tbl);
7182 } // end of sp_text_toolbox_new()
7184 }//<unnamed> namespace
7187 //#########################
7188 //## Connector ##
7189 //#########################
7191 static void sp_connector_mode_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7192 {
7193 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7194 prefs->setBool("/tools/connector/mode",
7195 gtk_toggle_action_get_active( act ));
7196 }
7198 static void sp_connector_path_set_avoid(void)
7199 {
7200 cc_selection_set_avoid(true);
7201 }
7204 static void sp_connector_path_set_ignore(void)
7205 {
7206 cc_selection_set_avoid(false);
7207 }
7209 static void sp_connector_orthogonal_toggled( GtkToggleAction* act, GObject *tbl )
7210 {
7211 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7212 Inkscape::Selection * selection = sp_desktop_selection(desktop);
7213 SPDocument *doc = sp_desktop_document(desktop);
7215 if (!sp_document_get_undo_sensitive(doc))
7216 {
7217 return;
7218 }
7221 // quit if run by the _changed callbacks
7222 if (g_object_get_data( tbl, "freeze" )) {
7223 return;
7224 }
7226 // in turn, prevent callbacks from responding
7227 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7229 bool is_orthog = gtk_toggle_action_get_active( act );
7230 gchar orthog_str[] = "orthogonal";
7231 gchar polyline_str[] = "polyline";
7232 gchar *value = is_orthog ? orthog_str : polyline_str ;
7234 bool modmade = false;
7235 GSList *l = (GSList *) selection->itemList();
7236 while (l) {
7237 SPItem *item = (SPItem *) l->data;
7239 if (cc_item_is_connector(item)) {
7240 sp_object_setAttribute(item, "inkscape:connector-type",
7241 value, false);
7242 item->avoidRef->handleSettingChange();
7243 modmade = true;
7244 }
7245 l = l->next;
7246 }
7248 if (!modmade)
7249 {
7250 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7251 prefs->setBool("/tools/connector/orthogonal", is_orthog);
7252 }
7254 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7255 is_orthog ? _("Set connector type: orthogonal"): _("Set connector type: polyline"));
7257 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7258 }
7260 static void connector_curvature_changed(GtkAdjustment *adj, GObject* tbl)
7261 {
7262 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7263 Inkscape::Selection * selection = sp_desktop_selection(desktop);
7264 SPDocument *doc = sp_desktop_document(desktop);
7266 if (!sp_document_get_undo_sensitive(doc))
7267 {
7268 return;
7269 }
7272 // quit if run by the _changed callbacks
7273 if (g_object_get_data( tbl, "freeze" )) {
7274 return;
7275 }
7277 // in turn, prevent callbacks from responding
7278 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7280 gdouble newValue = gtk_adjustment_get_value(adj);
7281 gchar value[G_ASCII_DTOSTR_BUF_SIZE];
7282 g_ascii_dtostr(value, G_ASCII_DTOSTR_BUF_SIZE, newValue);
7284 bool modmade = false;
7285 GSList *l = (GSList *) selection->itemList();
7286 while (l) {
7287 SPItem *item = (SPItem *) l->data;
7289 if (cc_item_is_connector(item)) {
7290 sp_object_setAttribute(item, "inkscape:connector-curvature",
7291 value, false);
7292 item->avoidRef->handleSettingChange();
7293 modmade = true;
7294 }
7295 l = l->next;
7296 }
7298 if (!modmade)
7299 {
7300 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7301 prefs->setDouble(Glib::ustring("/tools/connector/curvature"), newValue);
7302 }
7304 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7305 _("Change connector curvature"));
7307 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7308 }
7311 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
7312 {
7313 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7314 SPDocument *doc = sp_desktop_document(desktop);
7316 if (!sp_document_get_undo_sensitive(doc))
7317 {
7318 return;
7319 }
7321 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7323 if ( !repr->attribute("inkscape:connector-spacing") &&
7324 ( adj->value == defaultConnSpacing )) {
7325 // Don't need to update the repr if the attribute doesn't
7326 // exist and it is being set to the default value -- as will
7327 // happen at startup.
7328 return;
7329 }
7331 // quit if run by the attr_changed listener
7332 if (g_object_get_data( tbl, "freeze" )) {
7333 return;
7334 }
7336 // in turn, prevent listener from responding
7337 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
7339 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
7340 SP_OBJECT(desktop->namedview)->updateRepr();
7342 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
7343 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
7344 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
7345 Geom::Matrix m = Geom::identity();
7346 avoid_item_move(&m, item);
7347 }
7349 if (items) {
7350 g_slist_free(items);
7351 }
7353 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7354 _("Change connector spacing"));
7356 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7357 }
7359 static void sp_connector_graph_layout(void)
7360 {
7361 if (!SP_ACTIVE_DESKTOP) return;
7362 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7364 // hack for clones, see comment in align-and-distribute.cpp
7365 int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7366 prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7368 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
7370 prefs->setInt("/options/clonecompensation/value", saved_compensation);
7372 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
7373 }
7375 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7376 {
7377 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7378 prefs->setBool("/tools/connector/directedlayout",
7379 gtk_toggle_action_get_active( act ));
7380 }
7382 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7383 {
7384 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7385 prefs->setBool("/tools/connector/avoidoverlaplayout",
7386 gtk_toggle_action_get_active( act ));
7387 }
7390 static void connector_length_changed(GtkAdjustment *adj, GObject* /*tbl*/)
7391 {
7392 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7393 prefs->setDouble("/tools/connector/length", adj->value);
7394 }
7396 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
7397 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
7398 bool /*is_interactive*/, gpointer data)
7399 {
7400 GtkWidget *tbl = GTK_WIDGET(data);
7402 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
7403 return;
7404 }
7405 if (strcmp(name, "inkscape:connector-spacing") == 0)
7406 {
7407 GtkAdjustment *adj = (GtkAdjustment*)
7408 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
7409 gdouble spacing = defaultConnSpacing;
7410 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
7412 gtk_adjustment_set_value(adj, spacing);
7413 gtk_adjustment_value_changed(adj);
7414 }
7416 spinbutton_defocus(GTK_OBJECT(tbl));
7417 }
7419 static void sp_connector_new_connection_point(GtkWidget *, GObject *tbl)
7420 {
7421 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7422 SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7424 if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE)
7425 cc_create_connection_point(cc);
7426 }
7428 static void sp_connector_remove_connection_point(GtkWidget *, GObject *tbl)
7429 {
7430 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7431 SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7433 if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE)
7434 cc_remove_connection_point(cc);
7435 }
7437 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
7438 NULL, /* child_added */
7439 NULL, /* child_removed */
7440 connector_tb_event_attr_changed,
7441 NULL, /* content_changed */
7442 NULL /* order_changed */
7443 };
7445 static void sp_connector_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
7446 {
7447 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "curvature" ) );
7448 GtkToggleAction *act = GTK_TOGGLE_ACTION( g_object_get_data( tbl, "orthogonal" ) );
7449 SPItem *item = selection->singleItem();
7450 if (SP_IS_PATH(item))
7451 {
7452 gdouble curvature = SP_PATH(item)->connEndPair.getCurvature();
7453 bool is_orthog = SP_PATH(item)->connEndPair.isOrthogonal();
7454 gtk_toggle_action_set_active(act, is_orthog);
7455 gtk_adjustment_set_value(adj, curvature);
7456 }
7458 }
7460 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
7461 {
7462 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7463 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
7465 // Editing mode toggle button
7466 {
7467 InkToggleAction* act = ink_toggle_action_new( "ConnectorEditModeAction",
7468 _("EditMode"),
7469 _("Switch between connection point editing and connector drawing mode"),
7470 INKSCAPE_ICON_CONNECTOR_EDIT,
7471 Inkscape::ICON_SIZE_DECORATION );
7472 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7474 bool tbuttonstate = prefs->getBool("/tools/connector/mode");
7475 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7476 g_object_set_data( holder, "mode", act );
7477 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_mode_toggled), holder );
7478 }
7481 {
7482 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
7483 _("Avoid"),
7484 _("Make connectors avoid selected objects"),
7485 INKSCAPE_ICON_CONNECTOR_AVOID,
7486 secondarySize );
7487 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
7488 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7489 }
7491 {
7492 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
7493 _("Ignore"),
7494 _("Make connectors ignore selected objects"),
7495 INKSCAPE_ICON_CONNECTOR_IGNORE,
7496 secondarySize );
7497 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
7498 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7499 }
7501 // Orthogonal connectors toggle button
7502 {
7503 InkToggleAction* act = ink_toggle_action_new( "ConnectorOrthogonalAction",
7504 _("Orthogonal"),
7505 _("Make connector orthogonal or polyline"),
7506 INKSCAPE_ICON_CONNECTOR_ORTHOGONAL,
7507 Inkscape::ICON_SIZE_DECORATION );
7508 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7510 bool tbuttonstate = prefs->getBool("/tools/connector/orthogonal");
7511 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7512 g_object_set_data( holder, "orthogonal", act );
7513 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_orthogonal_toggled), holder );
7514 }
7516 EgeAdjustmentAction* eact = 0;
7517 // Curvature spinbox
7518 eact = create_adjustment_action( "ConnectorCurvatureAction",
7519 _("Connector Curvature"), _("Curvature:"),
7520 _("The amount of connectors curvature"),
7521 "/tools/connector/curvature", defaultConnCurvature,
7522 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-curvature",
7523 0, 100, 1.0, 10.0,
7524 0, 0, 0,
7525 connector_curvature_changed, 1, 0 );
7526 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7528 // Spacing spinbox
7529 eact = create_adjustment_action( "ConnectorSpacingAction",
7530 _("Connector Spacing"), _("Spacing:"),
7531 _("The amount of space left around objects by auto-routing connectors"),
7532 "/tools/connector/spacing", defaultConnSpacing,
7533 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
7534 0, 100, 1.0, 10.0,
7535 0, 0, 0,
7536 connector_spacing_changed, 1, 0 );
7537 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7539 // Graph (connector network) layout
7540 {
7541 InkAction* inky = ink_action_new( "ConnectorGraphAction",
7542 _("Graph"),
7543 _("Nicely arrange selected connector network"),
7544 INKSCAPE_ICON_DISTRIBUTE_GRAPH,
7545 secondarySize );
7546 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
7547 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7548 }
7550 // Default connector length spinbox
7551 eact = create_adjustment_action( "ConnectorLengthAction",
7552 _("Connector Length"), _("Length:"),
7553 _("Ideal length for connectors when layout is applied"),
7554 "/tools/connector/length", 100,
7555 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
7556 10, 1000, 10.0, 100.0,
7557 0, 0, 0,
7558 connector_length_changed, 1, 0 );
7559 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7562 // Directed edges toggle button
7563 {
7564 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
7565 _("Downwards"),
7566 _("Make connectors with end-markers (arrows) point downwards"),
7567 INKSCAPE_ICON_DISTRIBUTE_GRAPH_DIRECTED,
7568 Inkscape::ICON_SIZE_DECORATION );
7569 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7571 bool tbuttonstate = prefs->getBool("/tools/connector/directedlayout");
7572 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7574 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
7575 sigc::connection *connection = new sigc::connection(sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_connector_toolbox_selection_changed), (GObject *)holder))
7576 );
7577 }
7579 // Avoid overlaps toggle button
7580 {
7581 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
7582 _("Remove overlaps"),
7583 _("Do not allow overlapping shapes"),
7584 INKSCAPE_ICON_DISTRIBUTE_REMOVE_OVERLAPS,
7585 Inkscape::ICON_SIZE_DECORATION );
7586 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7588 bool tbuttonstate = prefs->getBool("/tools/connector/avoidoverlaplayout");
7589 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), (tbuttonstate ? TRUE : FALSE ));
7591 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
7592 }
7595 // New connection point button
7596 {
7597 InkAction* inky = ink_action_new( "ConnectorNewConnPointAction",
7598 _("New connection point"),
7599 _("Add a new connection point to the currently selected item"),
7600 INKSCAPE_ICON_CONNECTOR_NEW_CONNPOINT,
7601 secondarySize );
7602 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_new_connection_point), holder );
7603 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7604 }
7606 // Remove selected connection point button
7608 {
7609 InkAction* inky = ink_action_new( "ConnectorRemoveConnPointAction",
7610 _("Remove connection point"),
7611 _("Remove the currently selected connection point"),
7612 INKSCAPE_ICON_CONNECTOR_REMOVE_CONNPOINT,
7613 secondarySize );
7614 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_remove_connection_point), holder );
7615 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7616 }
7619 // Code to watch for changes to the connector-spacing attribute in
7620 // the XML.
7621 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7622 g_assert(repr != NULL);
7624 purge_repr_listener( holder, holder );
7626 if (repr) {
7627 g_object_set_data( holder, "repr", repr );
7628 Inkscape::GC::anchor(repr);
7629 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
7630 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
7631 }
7632 } // end of sp_connector_toolbox_prep()
7635 //#########################
7636 //## Paintbucket ##
7637 //#########################
7639 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
7640 {
7641 gint channels = ege_select_one_action_get_active( act );
7642 flood_channels_set_channels( channels );
7643 }
7645 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
7646 {
7647 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7648 prefs->setInt("/tools/paintbucket/threshold", (gint)adj->value);
7649 }
7651 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
7652 {
7653 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7654 prefs->setBool("/tools/paintbucket/autogap", ege_select_one_action_get_active( act ));
7655 }
7657 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
7658 {
7659 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
7660 SPUnit const *unit = tracker->getActiveUnit();
7661 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7663 prefs->setDouble("/tools/paintbucket/offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
7664 prefs->setString("/tools/paintbucket/offsetunits", sp_unit_get_abbreviation(unit));
7665 }
7667 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
7668 {
7669 // FIXME: make defaults settable via Inkscape Options
7670 struct KeyValue {
7671 char const *key;
7672 double value;
7673 } const key_values[] = {
7674 {"threshold", 15},
7675 {"offset", 0.0}
7676 };
7678 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
7679 KeyValue const &kv = key_values[i];
7680 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
7681 if ( adj ) {
7682 gtk_adjustment_set_value(adj, kv.value);
7683 }
7684 }
7686 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
7687 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
7688 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
7689 ege_select_one_action_set_active( autogap_action, 0 );
7690 }
7692 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
7693 {
7694 EgeAdjustmentAction* eact = 0;
7695 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7697 {
7698 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7700 GList* items = 0;
7701 gint count = 0;
7702 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
7703 {
7704 GtkTreeIter iter;
7705 gtk_list_store_append( model, &iter );
7706 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7707 count++;
7708 }
7709 g_list_free( items );
7710 items = 0;
7711 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
7712 g_object_set( act1, "short_label", _("Fill by:"), NULL );
7713 ege_select_one_action_set_appearance( act1, "compact" );
7714 ege_select_one_action_set_active( act1, prefs->getInt("/tools/paintbucket/channels", 0) );
7715 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
7716 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
7717 g_object_set_data( holder, "channels_action", act1 );
7718 }
7720 // Spacing spinbox
7721 {
7722 eact = create_adjustment_action(
7723 "ThresholdAction",
7724 _("Fill Threshold"), _("Threshold:"),
7725 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
7726 "/tools/paintbucket/threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
7727 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
7728 0, 0, 0,
7729 paintbucket_threshold_changed, 1, 0 );
7731 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
7732 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7733 }
7735 // Create the units menu.
7736 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
7737 Glib::ustring stored_unit = prefs->getString("/tools/paintbucket/offsetunits");
7738 if (!stored_unit.empty())
7739 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit.data()));
7740 g_object_set_data( holder, "tracker", tracker );
7741 {
7742 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
7743 gtk_action_group_add_action( mainActions, act );
7744 }
7746 // Offset spinbox
7747 {
7748 eact = create_adjustment_action(
7749 "OffsetAction",
7750 _("Grow/shrink by"), _("Grow/shrink by:"),
7751 _("The amount to grow (positive) or shrink (negative) the created fill path"),
7752 "/tools/paintbucket/offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
7753 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
7754 0, 0, 0,
7755 paintbucket_offset_changed, 1, 2);
7756 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
7758 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7759 }
7761 /* Auto Gap */
7762 {
7763 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7765 GList* items = 0;
7766 gint count = 0;
7767 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
7768 {
7769 GtkTreeIter iter;
7770 gtk_list_store_append( model, &iter );
7771 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7772 count++;
7773 }
7774 g_list_free( items );
7775 items = 0;
7776 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
7777 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
7778 ege_select_one_action_set_appearance( act2, "compact" );
7779 ege_select_one_action_set_active( act2, prefs->getBool("/tools/paintbucket/autogap") );
7780 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
7781 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
7782 g_object_set_data( holder, "autogap_action", act2 );
7783 }
7785 /* Reset */
7786 {
7787 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
7788 _("Defaults"),
7789 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
7790 GTK_STOCK_CLEAR );
7791 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
7792 gtk_action_group_add_action( mainActions, act );
7793 gtk_action_set_sensitive( act, TRUE );
7794 }
7796 }
7798 /*
7799 Local Variables:
7800 mode:c++
7801 c-file-style:"stroustrup"
7802 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
7803 indent-tabs-mode:nil
7804 fill-column:99
7805 End:
7806 */
7807 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :