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