f5f1094f01980fa400efac2814d600313564c0f1
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 " <toolitem action='SprayWidthAction' />"
311 " <separator />"
312 " <toolitem action='SprayPressureAction' />"
313 " <separator />"
314 " <toolitem action='SprayPopulationAction' />"
315 " <separator />"
316 " <toolitem action='SprayMeanAction' />"
317 " <toolitem action='SprayStandard_deviationAction' />"
318 " <separator />"
319 " <toolitem action='DialogSprayOption' />"
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_SPRAY_OPTION,
747 SP_VERB_DIALOG_DISPLAY,
748 SP_VERB_DIALOG_FILL_STROKE,
749 SP_VERB_DIALOG_NAMEDVIEW,
750 SP_VERB_DIALOG_TEXT,
751 SP_VERB_DIALOG_XML_EDITOR,
752 SP_VERB_DIALOG_LAYERS,
753 SP_VERB_EDIT_CLONE,
754 SP_VERB_EDIT_COPY,
755 SP_VERB_EDIT_CUT,
756 SP_VERB_EDIT_DUPLICATE,
757 SP_VERB_EDIT_PASTE,
758 SP_VERB_EDIT_REDO,
759 SP_VERB_EDIT_UNDO,
760 SP_VERB_EDIT_UNLINK_CLONE,
761 SP_VERB_FILE_EXPORT,
762 SP_VERB_FILE_IMPORT,
763 SP_VERB_FILE_NEW,
764 SP_VERB_FILE_OPEN,
765 SP_VERB_FILE_PRINT,
766 SP_VERB_FILE_SAVE,
767 SP_VERB_OBJECT_TO_CURVE,
768 SP_VERB_SELECTION_GROUP,
769 SP_VERB_SELECTION_OUTLINE,
770 SP_VERB_SELECTION_UNGROUP,
771 SP_VERB_ZOOM_1_1,
772 SP_VERB_ZOOM_1_2,
773 SP_VERB_ZOOM_2_1,
774 SP_VERB_ZOOM_DRAWING,
775 SP_VERB_ZOOM_IN,
776 SP_VERB_ZOOM_NEXT,
777 SP_VERB_ZOOM_OUT,
778 SP_VERB_ZOOM_PAGE,
779 SP_VERB_ZOOM_PAGE_WIDTH,
780 SP_VERB_ZOOM_PREV,
781 SP_VERB_ZOOM_SELECTION,
782 };
784 Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
786 static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
787 Glib::RefPtr<Gtk::ActionGroup> mainActions;
788 if ( groups.find(desktop) != groups.end() ) {
789 mainActions = groups[desktop];
790 }
792 if ( !mainActions ) {
793 mainActions = Gtk::ActionGroup::create("main");
794 groups[desktop] = mainActions;
795 }
797 for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
798 Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
799 if ( verb ) {
800 if (!mainActions->get_action(verb->get_id())) {
801 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
802 mainActions->add(Glib::wrap(act));
803 }
804 }
805 }
807 if ( !mainActions->get_action("ToolZoom") ) {
808 GtkTooltips *tt = gtk_tooltips_new();
809 for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
810 Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
811 if ( va ) {
812 mainActions->add(va);
813 if ( i == 0 ) {
814 va->set_active(true);
815 }
816 }
817 }
818 }
821 return mainActions;
822 }
825 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
826 {
827 gtk_widget_set_size_request( widget,
828 widget->allocation.width,
829 widget->allocation.height );
830 }
832 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
833 {
834 gtk_widget_set_size_request( widget, -1, -1 );
835 }
839 GtkWidget *
840 sp_tool_toolbox_new()
841 {
842 GtkTooltips *tt = gtk_tooltips_new();
843 GtkWidget* tb = gtk_toolbar_new();
844 gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
845 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
847 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
848 g_object_set_data(G_OBJECT(tb), "tooltips", tt);
850 gtk_widget_set_sensitive(tb, FALSE);
852 GtkWidget *hb = gtk_handle_box_new();
853 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
854 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
855 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
857 gtk_container_add(GTK_CONTAINER(hb), tb);
858 gtk_widget_show(GTK_WIDGET(tb));
860 sigc::connection* conn = new sigc::connection;
861 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
863 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
864 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
866 return hb;
867 }
869 GtkWidget *
870 sp_aux_toolbox_new()
871 {
872 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
874 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
876 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
878 gtk_widget_set_sensitive(tb, FALSE);
880 GtkWidget *hb = gtk_handle_box_new();
881 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
882 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
883 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
885 gtk_container_add(GTK_CONTAINER(hb), tb);
886 gtk_widget_show(GTK_WIDGET(tb));
888 sigc::connection* conn = new sigc::connection;
889 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
891 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
892 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
894 return hb;
895 }
897 //####################################
898 //# Commands Bar
899 //####################################
901 GtkWidget *
902 sp_commands_toolbox_new()
903 {
904 GtkWidget *tb = gtk_toolbar_new();
906 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
907 gtk_widget_set_sensitive(tb, FALSE);
909 GtkWidget *hb = gtk_handle_box_new();
910 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
911 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
912 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
914 gtk_container_add(GTK_CONTAINER(hb), tb);
915 gtk_widget_show(GTK_WIDGET(tb));
917 sigc::connection* conn = new sigc::connection;
918 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
920 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
921 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
923 return hb;
924 }
926 GtkWidget *
927 sp_snap_toolbox_new()
928 {
929 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
930 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
931 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
933 //GtkWidget *tb = gtk_toolbar_new();
934 //g_object_set_data(G_OBJECT(tb), "desktop", NULL);
936 gtk_widget_set_sensitive(tb, FALSE);
938 GtkWidget *hb = gtk_handle_box_new();
939 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
940 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
941 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
943 gtk_container_add(GTK_CONTAINER(hb), tb);
944 gtk_widget_show(GTK_WIDGET(tb));
946 sigc::connection* conn = new sigc::connection;
947 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
949 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
950 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
952 return hb;
953 }
955 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
956 gchar const *label, gchar const *shortLabel, gchar const *tooltip,
957 Glib::ustring const &path, gdouble def,
958 GtkWidget *focusTarget,
959 GtkWidget *us,
960 GObject *dataKludge,
961 gboolean altx, gchar const *altx_mark,
962 gdouble lower, gdouble upper, gdouble step, gdouble page,
963 gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
964 void (*callback)(GtkAdjustment *, GObject *),
965 gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
966 {
967 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
968 GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs->getDouble(path, def) * factor,
969 lower, upper, step, page, 0 ) );
970 if (us) {
971 sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
972 }
974 gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
976 EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
977 if ( shortLabel ) {
978 g_object_set( act, "short_label", shortLabel, NULL );
979 }
981 if ( (descrCount > 0) && descrLabels && descrValues ) {
982 ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
983 }
985 if ( focusTarget ) {
986 ege_adjustment_action_set_focuswidget( act, focusTarget );
987 }
989 if ( altx && altx_mark ) {
990 g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
991 }
993 if ( dataKludge ) {
994 // Rather lame, but it's the only place where we need to get the entry name
995 // but we don't have an Entry
996 g_object_set_data( dataKludge, prefs->getEntry(path).getEntryName().data(), adj );
997 }
999 // Using a cast just to make sure we pass in the right kind of function pointer
1000 g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
1002 return act;
1003 }
1006 //####################################
1007 //# node editing callbacks
1008 //####################################
1010 /**
1011 * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
1012 */
1013 static ShapeEditor *get_current_shape_editor()
1014 {
1015 if (!SP_ACTIVE_DESKTOP) {
1016 return NULL;
1017 }
1019 SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
1021 if (!SP_IS_NODE_CONTEXT(event_context)) {
1022 return NULL;
1023 }
1025 return event_context->shape_editor;
1026 }
1029 void
1030 sp_node_path_edit_add(void)
1031 {
1032 ShapeEditor *shape_editor = get_current_shape_editor();
1033 if (shape_editor) shape_editor->add_node();
1034 }
1036 void
1037 sp_node_path_edit_delete(void)
1038 {
1039 ShapeEditor *shape_editor = get_current_shape_editor();
1040 if (shape_editor) shape_editor->delete_nodes_preserving_shape();
1041 }
1043 void
1044 sp_node_path_edit_delete_segment(void)
1045 {
1046 ShapeEditor *shape_editor = get_current_shape_editor();
1047 if (shape_editor) shape_editor->delete_segment();
1048 }
1050 void
1051 sp_node_path_edit_break(void)
1052 {
1053 ShapeEditor *shape_editor = get_current_shape_editor();
1054 if (shape_editor) shape_editor->break_at_nodes();
1055 }
1057 void
1058 sp_node_path_edit_join(void)
1059 {
1060 ShapeEditor *shape_editor = get_current_shape_editor();
1061 if (shape_editor) shape_editor->join_nodes();
1062 }
1064 void
1065 sp_node_path_edit_join_segment(void)
1066 {
1067 ShapeEditor *shape_editor = get_current_shape_editor();
1068 if (shape_editor) shape_editor->join_segments();
1069 }
1071 void
1072 sp_node_path_edit_toline(void)
1073 {
1074 ShapeEditor *shape_editor = get_current_shape_editor();
1075 if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1076 }
1078 void
1079 sp_node_path_edit_tocurve(void)
1080 {
1081 ShapeEditor *shape_editor = get_current_shape_editor();
1082 if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1083 }
1085 void
1086 sp_node_path_edit_cusp(void)
1087 {
1088 ShapeEditor *shape_editor = get_current_shape_editor();
1089 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1090 }
1092 void
1093 sp_node_path_edit_smooth(void)
1094 {
1095 ShapeEditor *shape_editor = get_current_shape_editor();
1096 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1097 }
1099 void
1100 sp_node_path_edit_symmetrical(void)
1101 {
1102 ShapeEditor *shape_editor = get_current_shape_editor();
1103 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1104 }
1106 void
1107 sp_node_path_edit_auto(void)
1108 {
1109 ShapeEditor *shape_editor = get_current_shape_editor();
1110 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_AUTO);
1111 }
1113 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1114 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1115 bool show = gtk_toggle_action_get_active( act );
1116 prefs->setBool("/tools/nodes/show_handles", show);
1117 ShapeEditor *shape_editor = get_current_shape_editor();
1118 if (shape_editor) shape_editor->show_handles(show);
1119 }
1121 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1122 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1123 bool show = gtk_toggle_action_get_active( act );
1124 prefs->setBool("/tools/nodes/show_helperpath", show);
1125 ShapeEditor *shape_editor = get_current_shape_editor();
1126 if (shape_editor) shape_editor->show_helperpath(show);
1127 }
1129 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1130 sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1131 }
1133 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1134 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1135 }
1137 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1138 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1139 }
1141 /* is called when the node selection is modified */
1142 static void
1143 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1144 {
1145 GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1146 GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1147 GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1148 GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1150 // quit if run by the attr_changed listener
1151 if (g_object_get_data( tbl, "freeze" )) {
1152 return;
1153 }
1155 // in turn, prevent listener from responding
1156 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1158 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1159 SPUnit const *unit = tracker->getActiveUnit();
1161 ShapeEditor *shape_editor = get_current_shape_editor();
1162 if (shape_editor && shape_editor->has_nodepath()) {
1163 Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1164 int n_selected = 0;
1165 if (nodepath) {
1166 n_selected = nodepath->numSelected();
1167 }
1169 if (n_selected == 0) {
1170 gtk_action_set_sensitive(xact, FALSE);
1171 gtk_action_set_sensitive(yact, FALSE);
1172 } else {
1173 gtk_action_set_sensitive(xact, TRUE);
1174 gtk_action_set_sensitive(yact, TRUE);
1175 Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1176 Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1178 if (n_selected == 1) {
1179 Geom::Point sel_node = nodepath->singleSelectedCoords();
1180 if (oldx != sel_node[Geom::X] || oldy != sel_node[Geom::Y]) {
1181 gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[Geom::X], *unit));
1182 gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[Geom::Y], *unit));
1183 }
1184 } else {
1185 boost::optional<Geom::Coord> x = sp_node_selected_common_coord(nodepath, Geom::X);
1186 boost::optional<Geom::Coord> y = sp_node_selected_common_coord(nodepath, Geom::Y);
1187 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1188 /* Note: Currently x and y will always have a value, even if the coordinates of the
1189 selected nodes don't coincide (in this case we use the coordinates of the center
1190 of the bounding box). So the entries are never set to zero. */
1191 // FIXME: Maybe we should clear the entry if several nodes are selected
1192 // instead of providing a kind of average value
1193 gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1194 gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1195 }
1196 }
1197 }
1198 } else {
1199 // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1200 gtk_action_set_sensitive(xact, FALSE);
1201 gtk_action_set_sensitive(yact, FALSE);
1202 }
1204 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1205 }
1207 static void
1208 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1209 {
1210 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1211 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1213 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1214 SPUnit const *unit = tracker->getActiveUnit();
1216 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1217 prefs->setDouble(Glib::ustring("/tools/nodes/") + value_name, sp_units_get_pixels(adj->value, *unit));
1218 }
1220 // quit if run by the attr_changed listener
1221 if (g_object_get_data( tbl, "freeze" )) {
1222 return;
1223 }
1225 // in turn, prevent listener from responding
1226 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1228 ShapeEditor *shape_editor = get_current_shape_editor();
1229 if (shape_editor && shape_editor->has_nodepath()) {
1230 double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1231 if (!strcmp(value_name, "x")) {
1232 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::X);
1233 }
1234 if (!strcmp(value_name, "y")) {
1235 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::Y);
1236 }
1237 }
1239 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1240 }
1242 static void
1243 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1244 {
1245 sp_node_path_value_changed(adj, tbl, "x");
1246 }
1248 static void
1249 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1250 {
1251 sp_node_path_value_changed(adj, tbl, "y");
1252 }
1254 void
1255 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1256 {
1257 {
1258 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1259 SPItem *item = selection->singleItem();
1260 if (item && SP_IS_LPE_ITEM(item)) {
1261 if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1262 gtk_action_set_sensitive(w, TRUE);
1263 } else {
1264 gtk_action_set_sensitive(w, FALSE);
1265 }
1266 } else {
1267 gtk_action_set_sensitive(w, FALSE);
1268 }
1269 }
1271 {
1272 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1273 SPItem *item = selection->singleItem();
1274 if (item && item->clip_ref && item->clip_ref->getObject()) {
1275 gtk_action_set_sensitive(w, TRUE);
1276 } else {
1277 gtk_action_set_sensitive(w, FALSE);
1278 }
1279 }
1281 {
1282 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1283 SPItem *item = selection->singleItem();
1284 if (item && item->mask_ref && item->mask_ref->getObject()) {
1285 gtk_action_set_sensitive(w, TRUE);
1286 } else {
1287 gtk_action_set_sensitive(w, FALSE);
1288 }
1289 }
1290 }
1292 void
1293 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1294 {
1295 sp_node_toolbox_sel_changed (selection, tbl);
1296 }
1300 //################################
1301 //## Node Editing Toolbox ##
1302 //################################
1304 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1305 {
1306 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1307 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1308 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1309 g_object_set_data( holder, "tracker", tracker );
1311 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
1313 {
1314 InkAction* inky = ink_action_new( "NodeInsertAction",
1315 _("Insert node"),
1316 _("Insert new nodes into selected segments"),
1317 INKSCAPE_ICON_NODE_ADD,
1318 secondarySize );
1319 g_object_set( inky, "short_label", _("Insert"), NULL );
1320 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1321 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1322 }
1324 {
1325 InkAction* inky = ink_action_new( "NodeDeleteAction",
1326 _("Delete node"),
1327 _("Delete selected nodes"),
1328 INKSCAPE_ICON_NODE_DELETE,
1329 secondarySize );
1330 g_object_set( inky, "short_label", _("Delete"), NULL );
1331 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1332 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1333 }
1335 {
1336 InkAction* inky = ink_action_new( "NodeJoinAction",
1337 _("Join endnodes"),
1338 _("Join selected endnodes"),
1339 INKSCAPE_ICON_NODE_JOIN,
1340 secondarySize );
1341 g_object_set( inky, "short_label", _("Join"), NULL );
1342 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1343 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1344 }
1346 {
1347 InkAction* inky = ink_action_new( "NodeBreakAction",
1348 _("Break nodes"),
1349 _("Break path at selected nodes"),
1350 INKSCAPE_ICON_NODE_BREAK,
1351 secondarySize );
1352 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1353 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1354 }
1357 {
1358 InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1359 _("Join with segment"),
1360 _("Join selected endnodes with a new segment"),
1361 INKSCAPE_ICON_NODE_JOIN_SEGMENT,
1362 secondarySize );
1363 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1364 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1365 }
1367 {
1368 InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1369 _("Delete segment"),
1370 _("Delete segment between two non-endpoint nodes"),
1371 INKSCAPE_ICON_NODE_DELETE_SEGMENT,
1372 secondarySize );
1373 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1374 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1375 }
1377 {
1378 InkAction* inky = ink_action_new( "NodeCuspAction",
1379 _("Node Cusp"),
1380 _("Make selected nodes corner"),
1381 INKSCAPE_ICON_NODE_TYPE_CUSP,
1382 secondarySize );
1383 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1384 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1385 }
1387 {
1388 InkAction* inky = ink_action_new( "NodeSmoothAction",
1389 _("Node Smooth"),
1390 _("Make selected nodes smooth"),
1391 INKSCAPE_ICON_NODE_TYPE_SMOOTH,
1392 secondarySize );
1393 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1394 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1395 }
1397 {
1398 InkAction* inky = ink_action_new( "NodeSymmetricAction",
1399 _("Node Symmetric"),
1400 _("Make selected nodes symmetric"),
1401 INKSCAPE_ICON_NODE_TYPE_SYMMETRIC,
1402 secondarySize );
1403 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1404 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1405 }
1407 {
1408 InkAction* inky = ink_action_new( "NodeAutoAction",
1409 _("Node Auto"),
1410 _("Make selected nodes auto-smooth"),
1411 INKSCAPE_ICON_NODE_TYPE_AUTO_SMOOTH,
1412 secondarySize );
1413 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_auto), 0 );
1414 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1415 }
1417 {
1418 InkAction* inky = ink_action_new( "NodeLineAction",
1419 _("Node Line"),
1420 _("Make selected segments lines"),
1421 INKSCAPE_ICON_NODE_SEGMENT_LINE,
1422 secondarySize );
1423 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1424 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1425 }
1427 {
1428 InkAction* inky = ink_action_new( "NodeCurveAction",
1429 _("Node Curve"),
1430 _("Make selected segments curves"),
1431 INKSCAPE_ICON_NODE_SEGMENT_CURVE,
1432 secondarySize );
1433 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1434 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1435 }
1437 {
1438 InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1439 _("Show Handles"),
1440 _("Show the Bezier handles of selected nodes"),
1441 INKSCAPE_ICON_SHOW_NODE_HANDLES,
1442 Inkscape::ICON_SIZE_DECORATION );
1443 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1444 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1445 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_handles", true) );
1446 }
1448 {
1449 InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1450 _("Show Outline"),
1451 _("Show the outline of the path"),
1452 INKSCAPE_ICON_SHOW_PATH_OUTLINE,
1453 Inkscape::ICON_SIZE_DECORATION );
1454 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1455 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1456 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_helperpath", false) );
1457 }
1459 {
1460 InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1461 _("Next path effect parameter"),
1462 _("Show next path effect parameter for editing"),
1463 INKSCAPE_ICON_PATH_EFFECT_PARAMETER_NEXT,
1464 Inkscape::ICON_SIZE_DECORATION );
1465 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1466 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1467 g_object_set_data( holder, "nodes_lpeedit", inky);
1468 }
1470 {
1471 InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1472 _("Edit clipping path"),
1473 _("Edit the clipping path of the object"),
1474 INKSCAPE_ICON_PATH_CLIP_EDIT,
1475 Inkscape::ICON_SIZE_DECORATION );
1476 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1477 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1478 g_object_set_data( holder, "nodes_clippathedit", inky);
1479 }
1481 {
1482 InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1483 _("Edit mask path"),
1484 _("Edit the mask of the object"),
1485 INKSCAPE_ICON_PATH_MASK_EDIT,
1486 Inkscape::ICON_SIZE_DECORATION );
1487 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1488 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1489 g_object_set_data( holder, "nodes_maskedit", inky);
1490 }
1492 /* X coord of selected node(s) */
1493 {
1494 EgeAdjustmentAction* eact = 0;
1495 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1496 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1497 eact = create_adjustment_action( "NodeXAction",
1498 _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1499 "/tools/nodes/Xcoord", 0,
1500 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1501 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1502 labels, values, G_N_ELEMENTS(labels),
1503 sp_node_path_x_value_changed );
1504 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1505 g_object_set_data( holder, "nodes_x_action", eact );
1506 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1507 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1508 }
1510 /* Y coord of selected node(s) */
1511 {
1512 EgeAdjustmentAction* eact = 0;
1513 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1514 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1515 eact = create_adjustment_action( "NodeYAction",
1516 _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1517 "/tools/nodes/Ycoord", 0,
1518 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1519 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1520 labels, values, G_N_ELEMENTS(labels),
1521 sp_node_path_y_value_changed );
1522 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1523 g_object_set_data( holder, "nodes_y_action", eact );
1524 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1525 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1526 }
1528 // add the units menu
1529 {
1530 GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1531 gtk_action_group_add_action( mainActions, act );
1532 }
1535 sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1537 //watch selection
1538 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1540 sigc::connection *c_selection_changed =
1541 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1542 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1543 pool->add_connection ("selection-changed", c_selection_changed);
1545 sigc::connection *c_selection_modified =
1546 new sigc::connection (sp_desktop_selection (desktop)->connectModified
1547 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1548 pool->add_connection ("selection-modified", c_selection_modified);
1550 sigc::connection *c_subselection_changed =
1551 new sigc::connection (desktop->connectToolSubselectionChanged
1552 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1553 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1555 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1557 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1558 } // end of sp_node_toolbox_prep()
1561 //########################
1562 //## Zoom Toolbox ##
1563 //########################
1565 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1566 {
1567 // no custom GtkAction setup needed
1568 } // end of sp_zoom_toolbox_prep()
1570 void
1571 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1572 {
1573 toolbox_set_desktop(toolbox,
1574 desktop,
1575 setup_tool_toolbox,
1576 update_tool_toolbox,
1577 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1578 "event_context_connection")));
1579 }
1582 void
1583 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1584 {
1585 toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1586 desktop,
1587 setup_aux_toolbox,
1588 update_aux_toolbox,
1589 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1590 "event_context_connection")));
1591 }
1593 void
1594 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1595 {
1596 toolbox_set_desktop(toolbox,
1597 desktop,
1598 setup_commands_toolbox,
1599 update_commands_toolbox,
1600 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1601 "event_context_connection")));
1602 }
1604 void
1605 sp_snap_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1606 {
1607 toolbox_set_desktop(toolbox,
1608 desktop,
1609 setup_snap_toolbox,
1610 update_snap_toolbox,
1611 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1612 "event_context_connection")));
1613 }
1616 static void
1617 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1618 {
1619 gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1620 SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1622 if (old_desktop) {
1623 GList *children, *iter;
1625 children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1626 for ( iter = children ; iter ; iter = iter->next ) {
1627 gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1628 }
1629 g_list_free(children);
1630 }
1632 g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1634 if (desktop) {
1635 gtk_widget_set_sensitive(toolbox, TRUE);
1636 setup_func(toolbox, desktop);
1637 update_func(desktop, desktop->event_context, toolbox);
1638 *conn = desktop->connectEventContextChanged
1639 (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1640 } else {
1641 gtk_widget_set_sensitive(toolbox, FALSE);
1642 }
1644 } // end of toolbox_set_desktop()
1647 static void
1648 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1649 {
1650 gchar const * descr =
1651 "<ui>"
1652 " <toolbar name='ToolToolbar'>"
1653 " <toolitem action='ToolSelector' />"
1654 " <toolitem action='ToolNode' />"
1655 " <toolitem action='ToolTweak' />"
1656 " <toolitem action='ToolSpray' />"
1657 " <toolitem action='ToolZoom' />"
1658 " <toolitem action='ToolRect' />"
1659 " <toolitem action='Tool3DBox' />"
1660 " <toolitem action='ToolArc' />"
1661 " <toolitem action='ToolStar' />"
1662 " <toolitem action='ToolSpiral' />"
1663 " <toolitem action='ToolPencil' />"
1664 " <toolitem action='ToolPen' />"
1665 " <toolitem action='ToolCalligraphic' />"
1666 " <toolitem action='ToolEraser' />"
1667 // " <toolitem action='ToolLPETool' />"
1668 " <toolitem action='ToolPaintBucket' />"
1669 " <toolitem action='ToolText' />"
1670 " <toolitem action='ToolConnector' />"
1671 " <toolitem action='ToolGradient' />"
1672 " <toolitem action='ToolDropper' />"
1673 " </toolbar>"
1674 "</ui>";
1675 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1676 GtkUIManager* mgr = gtk_ui_manager_new();
1677 GError* errVal = 0;
1678 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1680 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1681 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1683 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" );
1684 if ( prefs->getBool("/toolbox/icononly", true) ) {
1685 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1686 }
1687 Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
1688 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1690 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
1691 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1693 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1695 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1696 if ( child ) {
1697 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1698 }
1700 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1701 // Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
1702 }
1705 static void
1706 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1707 {
1708 gchar const *const tname = ( eventcontext
1709 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1710 : NULL );
1711 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1713 for (int i = 0 ; tools[i].type_name ; i++ ) {
1714 Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1715 if ( act ) {
1716 bool setActive = tname && !strcmp(tname, tools[i].type_name);
1717 Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1718 if ( verbAct ) {
1719 verbAct->set_active(setActive);
1720 }
1721 }
1722 }
1723 }
1725 static void
1726 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1727 {
1728 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1729 GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1730 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1731 GtkUIManager* mgr = gtk_ui_manager_new();
1732 GError* errVal = 0;
1733 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1734 gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1736 std::map<std::string, GtkWidget*> dataHolders;
1738 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1739 if ( aux_toolboxes[i].prep_func ) {
1740 // converted to GtkActions and UIManager
1742 GtkWidget* kludge = gtk_toolbar_new();
1743 g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1744 g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1745 dataHolders[aux_toolboxes[i].type_name] = kludge;
1746 aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1747 } else {
1749 GtkWidget *sub_toolbox = 0;
1750 if (aux_toolboxes[i].create_func == NULL)
1751 sub_toolbox = sp_empty_toolbox_new(desktop);
1752 else {
1753 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1754 }
1756 gtk_size_group_add_widget( grouper, sub_toolbox );
1758 gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1759 g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1761 }
1762 }
1764 // Second pass to create toolbars *after* all GtkActions are created
1765 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1766 if ( aux_toolboxes[i].prep_func ) {
1767 // converted to GtkActions and UIManager
1769 GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1771 GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1772 gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1774 gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1775 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1776 g_free( tmp );
1777 tmp = 0;
1779 if ( prefs->getBool( "/toolbox/icononly", true) ) {
1780 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1781 }
1783 Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
1784 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1786 gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1788 if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1789 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1790 swatch->setDesktop( desktop );
1791 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1792 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1793 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1794 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 );
1795 }
1797 gtk_widget_show_all( holder );
1798 sp_set_font_size_smaller( holder );
1800 gtk_size_group_add_widget( grouper, holder );
1802 gtk_container_add( GTK_CONTAINER(toolbox), holder );
1803 g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1804 }
1805 }
1807 g_object_unref( G_OBJECT(grouper) );
1808 }
1810 static void
1811 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1812 {
1813 gchar const *tname = ( eventcontext
1814 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1815 : NULL );
1816 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1817 GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1818 if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1819 gtk_widget_show_all(sub_toolbox);
1820 g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1821 } else {
1822 gtk_widget_hide(sub_toolbox);
1823 }
1824 }
1825 }
1827 static void
1828 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1829 {
1830 gchar const * descr =
1831 "<ui>"
1832 " <toolbar name='CommandsToolbar'>"
1833 " <toolitem action='FileNew' />"
1834 " <toolitem action='FileOpen' />"
1835 " <toolitem action='FileSave' />"
1836 " <toolitem action='FilePrint' />"
1837 " <separator />"
1838 " <toolitem action='FileImport' />"
1839 " <toolitem action='FileExport' />"
1840 " <separator />"
1841 " <toolitem action='EditUndo' />"
1842 " <toolitem action='EditRedo' />"
1843 " <separator />"
1844 " <toolitem action='EditCopy' />"
1845 " <toolitem action='EditCut' />"
1846 " <toolitem action='EditPaste' />"
1847 " <separator />"
1848 " <toolitem action='ZoomSelection' />"
1849 " <toolitem action='ZoomDrawing' />"
1850 " <toolitem action='ZoomPage' />"
1851 " <separator />"
1852 " <toolitem action='EditDuplicate' />"
1853 " <toolitem action='EditClone' />"
1854 " <toolitem action='EditUnlinkClone' />"
1855 " <separator />"
1856 " <toolitem action='SelectionGroup' />"
1857 " <toolitem action='SelectionUnGroup' />"
1858 " <separator />"
1859 " <toolitem action='DialogFillStroke' />"
1860 " <toolitem action='DialogText' />"
1861 " <toolitem action='DialogLayers' />"
1862 " <toolitem action='DialogXMLEditor' />"
1863 " <toolitem action='DialogAlignDistribute' />"
1864 " <separator />"
1865 " <toolitem action='DialogPreferences' />"
1866 " <toolitem action='DialogDocumentProperties' />"
1867 " </toolbar>"
1868 "</ui>";
1869 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1870 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1872 GtkUIManager* mgr = gtk_ui_manager_new();
1873 GError* errVal = 0;
1875 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1876 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1878 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1879 if ( prefs->getBool("/toolbox/icononly", true) ) {
1880 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1881 }
1883 Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
1884 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1886 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1887 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1890 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1892 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1893 if ( child ) {
1894 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1895 }
1897 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1898 }
1900 static void
1901 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1902 {
1903 }
1905 void toggle_snap_callback (GtkToggleAction *act, gpointer data) { //data points to the toolbox
1907 if (g_object_get_data(G_OBJECT(data), "freeze" )) {
1908 return;
1909 }
1911 gpointer ptr = g_object_get_data(G_OBJECT(data), "desktop");
1912 g_assert(ptr != NULL);
1914 SPDesktop *dt = reinterpret_cast<SPDesktop*>(ptr);
1915 SPNamedView *nv = sp_desktop_namedview(dt);
1916 SPDocument *doc = SP_OBJECT_DOCUMENT(nv);
1918 if (dt == NULL || nv == NULL) {
1919 g_warning("No desktop or namedview specified (in toggle_snap_callback)!");
1920 return;
1921 }
1923 Inkscape::XML::Node *repr = SP_OBJECT_REPR(nv);
1925 if (repr == NULL) {
1926 g_warning("This namedview doesn't have a xml representation attached!");
1927 return;
1928 }
1930 bool saved = sp_document_get_undo_sensitive(doc);
1931 sp_document_set_undo_sensitive(doc, false);
1933 bool v = false;
1934 SPAttributeEnum attr = (SPAttributeEnum) GPOINTER_TO_INT(g_object_get_data(G_OBJECT(act), "SP_ATTR_INKSCAPE"));
1936 switch (attr) {
1937 case SP_ATTR_INKSCAPE_SNAP_GLOBAL:
1938 dt->toggleSnapGlobal();
1939 break;
1940 case SP_ATTR_INKSCAPE_SNAP_BBOX:
1941 v = nv->snap_manager.snapprefs.getSnapModeBBox();
1942 sp_repr_set_boolean(repr, "inkscape:snap-bbox", !v);
1943 break;
1944 case SP_ATTR_INKSCAPE_BBOX_PATHS:
1945 v = nv->snap_manager.snapprefs.getSnapToBBoxPath();
1946 sp_repr_set_boolean(repr, "inkscape:bbox-paths", !v);
1947 break;
1948 case SP_ATTR_INKSCAPE_BBOX_NODES:
1949 v = nv->snap_manager.snapprefs.getSnapToBBoxNode();
1950 sp_repr_set_boolean(repr, "inkscape:bbox-nodes", !v);
1951 break;
1952 case SP_ATTR_INKSCAPE_SNAP_NODES:
1953 v = nv->snap_manager.snapprefs.getSnapModeNode();
1954 sp_repr_set_boolean(repr, "inkscape:snap-nodes", !v);
1955 break;
1956 case SP_ATTR_INKSCAPE_OBJECT_PATHS:
1957 v = nv->snap_manager.snapprefs.getSnapToItemPath();
1958 sp_repr_set_boolean(repr, "inkscape:object-paths", !v);
1959 break;
1960 case SP_ATTR_INKSCAPE_OBJECT_NODES:
1961 v = nv->snap_manager.snapprefs.getSnapToItemNode();
1962 sp_repr_set_boolean(repr, "inkscape:object-nodes", !v);
1963 break;
1964 case SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES:
1965 v = nv->snap_manager.snapprefs.getSnapSmoothNodes();
1966 sp_repr_set_boolean(repr, "inkscape:snap-smooth-nodes", !v);
1967 break;
1968 case SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS:
1969 v = nv->snap_manager.snapprefs.getSnapIntersectionCS();
1970 sp_repr_set_boolean(repr, "inkscape:snap-intersection-paths", !v);
1971 break;
1972 case SP_ATTR_INKSCAPE_SNAP_CENTER:
1973 v = nv->snap_manager.snapprefs.getIncludeItemCenter();
1974 sp_repr_set_boolean(repr, "inkscape:snap-center", !v);
1975 break;
1976 case SP_ATTR_INKSCAPE_SNAP_GRIDS:
1977 v = nv->snap_manager.snapprefs.getSnapToGrids();
1978 sp_repr_set_boolean(repr, "inkscape:snap-grids", !v);
1979 break;
1980 case SP_ATTR_INKSCAPE_SNAP_TO_GUIDES:
1981 v = nv->snap_manager.snapprefs.getSnapToGuides();
1982 sp_repr_set_boolean(repr, "inkscape:snap-to-guides", !v);
1983 break;
1984 case SP_ATTR_INKSCAPE_SNAP_PAGE:
1985 v = nv->snap_manager.snapprefs.getSnapToPageBorder();
1986 sp_repr_set_boolean(repr, "inkscape:snap-page", !v);
1987 break;
1988 /*case SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE:
1989 v = nv->snap_manager.snapprefs.getSnapIntersectionGG();
1990 sp_repr_set_boolean(repr, "inkscape:snap-intersection-grid-guide", !v);
1991 break;*/
1992 case SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS:
1993 v = nv->snap_manager.snapprefs.getSnapLineMidpoints();
1994 sp_repr_set_boolean(repr, "inkscape:snap-midpoints", !v);
1995 break;
1996 case SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS:
1997 v = nv->snap_manager.snapprefs.getSnapObjectMidpoints();
1998 sp_repr_set_boolean(repr, "inkscape:snap-object-midpoints", !v);
1999 break;
2000 case SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS:
2001 v = nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints();
2002 sp_repr_set_boolean(repr, "inkscape:snap-bbox-edge-midpoints", !v);
2003 break;
2004 case SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS:
2005 v = nv->snap_manager.snapprefs.getSnapBBoxMidpoints();
2006 sp_repr_set_boolean(repr, "inkscape:snap-bbox-midpoints", !v);
2007 break;
2008 default:
2009 g_warning("toggle_snap_callback has been called with an ID for which no action has been defined");
2010 break;
2011 }
2013 // The snapping preferences are stored in the document, and therefore toggling makes the document dirty
2014 doc->setModifiedSinceSave();
2016 sp_document_set_undo_sensitive(doc, saved);
2017 }
2019 void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
2020 {
2021 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2022 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2024 gchar const * descr =
2025 "<ui>"
2026 " <toolbar name='SnapToolbar'>"
2027 " <toolitem action='ToggleSnapGlobal' />"
2028 " <separator />"
2029 " <toolitem action='ToggleSnapFromBBoxCorner' />"
2030 " <toolitem action='ToggleSnapToBBoxPath' />"
2031 " <toolitem action='ToggleSnapToBBoxNode' />"
2032 " <toolitem action='ToggleSnapToFromBBoxEdgeMidpoints' />"
2033 " <toolitem action='ToggleSnapToFromBBoxCenters' />"
2034 " <separator />"
2035 " <toolitem action='ToggleSnapFromNode' />"
2036 " <toolitem action='ToggleSnapToItemPath' />"
2037 " <toolitem action='ToggleSnapToPathIntersections' />"
2038 " <toolitem action='ToggleSnapToItemNode' />"
2039 " <toolitem action='ToggleSnapToSmoothNodes' />"
2040 " <toolitem action='ToggleSnapToFromLineMidpoints' />"
2041 " <toolitem action='ToggleSnapToFromObjectCenters' />"
2042 " <toolitem action='ToggleSnapToFromRotationCenter' />"
2043 " <separator />"
2044 " <toolitem action='ToggleSnapToPageBorder' />"
2045 " <toolitem action='ToggleSnapToGrids' />"
2046 " <toolitem action='ToggleSnapToGuides' />"
2047 //" <toolitem action='ToggleSnapToGridGuideIntersections' />"
2048 " </toolbar>"
2049 "</ui>";
2051 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
2053 {
2054 InkToggleAction* act = ink_toggle_action_new("ToggleSnapGlobal",
2055 _("Snap"), _("Enable snapping"), INKSCAPE_ICON_SNAP, secondarySize,
2056 SP_ATTR_INKSCAPE_SNAP_GLOBAL);
2058 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2059 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2060 }
2062 {
2063 InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromBBoxCorner",
2064 _("Bounding box"), _("Snap bounding box corners"), INKSCAPE_ICON_SNAP_BOUNDING_BOX,
2065 secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX);
2067 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2068 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2069 }
2071 {
2072 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxPath",
2073 _("Bounding box edges"), _("Snap to edges of a bounding box"),
2074 INKSCAPE_ICON_SNAP_BOUNDING_BOX_EDGES, secondarySize, SP_ATTR_INKSCAPE_BBOX_PATHS);
2076 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2077 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2078 }
2080 {
2081 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxNode",
2082 _("Bounding box corners"), _("Snap to bounding box corners"),
2083 INKSCAPE_ICON_SNAP_BOUNDING_BOX_CORNERS, secondarySize, SP_ATTR_INKSCAPE_BBOX_NODES);
2085 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2086 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2087 }
2089 {
2090 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxEdgeMidpoints",
2091 _("BBox Edge Midpoints"), _("Snap from and to midpoints of bounding box edges"),
2092 INKSCAPE_ICON_SNAP_BOUNDING_BOX_MIDPOINTS, secondarySize,
2093 SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS);
2095 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2096 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2097 }
2099 {
2100 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxCenters",
2101 _("BBox Centers"), _("Snapping from and to centers of bounding boxes"),
2102 INKSCAPE_ICON_SNAP_BOUNDING_BOX_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS);
2104 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2105 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2106 }
2108 {
2109 InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromNode",
2110 _("Nodes"), _("Snap nodes or handles"), INKSCAPE_ICON_SNAP_NODES, secondarySize, SP_ATTR_INKSCAPE_SNAP_NODES);
2112 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2113 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2114 }
2116 {
2117 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemPath",
2118 _("Paths"), _("Snap to paths"), INKSCAPE_ICON_SNAP_NODES_PATH, secondarySize,
2119 SP_ATTR_INKSCAPE_OBJECT_PATHS);
2121 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2122 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2123 }
2125 {
2126 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPathIntersections",
2127 _("Path intersections"), _("Snap to path intersections"),
2128 INKSCAPE_ICON_SNAP_NODES_INTERSECTION, secondarySize, SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS);
2130 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2131 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2132 }
2134 {
2135 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemNode",
2136 _("To nodes"), _("Snap to cusp nodes"), INKSCAPE_ICON_SNAP_NODES_CUSP, secondarySize,
2137 SP_ATTR_INKSCAPE_OBJECT_NODES);
2139 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2140 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2141 }
2143 {
2144 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToSmoothNodes",
2145 _("Smooth nodes"), _("Snap to smooth nodes"), INKSCAPE_ICON_SNAP_NODES_SMOOTH,
2146 secondarySize, SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES);
2148 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2149 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2150 }
2152 {
2153 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromLineMidpoints",
2154 _("Line Midpoints"), _("Snap from and to midpoints of line segments"),
2155 INKSCAPE_ICON_SNAP_NODES_MIDPOINT, secondarySize, SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS);
2157 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2158 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2159 }
2161 {
2162 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromObjectCenters",
2163 _("Object Centers"), _("Snap from and to centers of objects"),
2164 INKSCAPE_ICON_SNAP_NODES_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS);
2166 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2167 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2168 }
2170 {
2171 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromRotationCenter",
2172 _("Rotation Centers"), _("Snap from and to an item's rotation center"),
2173 INKSCAPE_ICON_SNAP_NODES_ROTATION_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_CENTER);
2175 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2176 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2177 }
2179 {
2180 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPageBorder",
2181 _("Page border"), _("Snap to the page border"), INKSCAPE_ICON_SNAP_PAGE,
2182 secondarySize, SP_ATTR_INKSCAPE_SNAP_PAGE);
2184 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2185 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2186 }
2188 {
2189 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGrids",
2190 _("Grids"), _("Snap to grids"), INKSCAPE_ICON_GRID_RECTANGULAR, secondarySize,
2191 SP_ATTR_INKSCAPE_SNAP_GRIDS);
2193 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2194 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2195 }
2197 {
2198 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGuides",
2199 _("Guides"), _("Snap to guides"), INKSCAPE_ICON_GUIDES, secondarySize,
2200 SP_ATTR_INKSCAPE_SNAP_TO_GUIDES);
2202 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2203 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2204 }
2206 /*{
2207 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGridGuideIntersections",
2208 _("Grid/guide intersections"), _("Snap to intersections of a grid with a guide"),
2209 INKSCAPE_ICON_SNAP_GRID_GUIDE_INTERSECTIONS, secondarySize,
2210 SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE);
2212 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2213 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2214 }*/
2216 GtkUIManager* mgr = gtk_ui_manager_new();
2217 GError* errVal = 0;
2219 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
2220 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
2222 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/SnapToolbar" );
2223 if ( prefs->getBool("/toolbox/icononly", true) ) {
2224 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
2225 }
2227 Inkscape::IconSize toolboxSize = prefToSize("/toolbox/secondary");
2228 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
2230 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
2231 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
2233 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
2235 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
2236 if ( child ) {
2237 gtk_container_remove( GTK_CONTAINER(toolbox), child );
2238 }
2240 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
2242 }
2244 void update_snap_toolbox(SPDesktop *desktop, SPEventContext */*eventcontext*/, GtkWidget *toolbox)
2245 {
2246 g_assert(desktop != NULL);
2247 g_assert(toolbox != NULL);
2249 SPNamedView *nv = sp_desktop_namedview(desktop);
2250 if (nv == NULL) {
2251 g_warning("Namedview cannot be retrieved (in update_snap_toolbox)!");
2252 return;
2253 }
2255 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2257 Glib::RefPtr<Gtk::Action> act1 = mainActions->get_action("ToggleSnapGlobal");
2258 Glib::RefPtr<Gtk::Action> act2 = mainActions->get_action("ToggleSnapFromBBoxCorner");
2259 Glib::RefPtr<Gtk::Action> act3 = mainActions->get_action("ToggleSnapToBBoxPath");
2260 Glib::RefPtr<Gtk::Action> act4 = mainActions->get_action("ToggleSnapToBBoxNode");
2261 Glib::RefPtr<Gtk::Action> act4b = mainActions->get_action("ToggleSnapToFromBBoxEdgeMidpoints");
2262 Glib::RefPtr<Gtk::Action> act4c = mainActions->get_action("ToggleSnapToFromBBoxCenters");
2263 Glib::RefPtr<Gtk::Action> act5 = mainActions->get_action("ToggleSnapFromNode");
2264 Glib::RefPtr<Gtk::Action> act6 = mainActions->get_action("ToggleSnapToItemPath");
2265 Glib::RefPtr<Gtk::Action> act6b = mainActions->get_action("ToggleSnapToPathIntersections");
2266 Glib::RefPtr<Gtk::Action> act7 = mainActions->get_action("ToggleSnapToItemNode");
2267 Glib::RefPtr<Gtk::Action> act8 = mainActions->get_action("ToggleSnapToSmoothNodes");
2268 Glib::RefPtr<Gtk::Action> act9 = mainActions->get_action("ToggleSnapToFromLineMidpoints");
2269 Glib::RefPtr<Gtk::Action> act10 = mainActions->get_action("ToggleSnapToFromObjectCenters");
2270 Glib::RefPtr<Gtk::Action> act11 = mainActions->get_action("ToggleSnapToFromRotationCenter");
2271 Glib::RefPtr<Gtk::Action> act12 = mainActions->get_action("ToggleSnapToPageBorder");
2272 //Glib::RefPtr<Gtk::Action> act13 = mainActions->get_action("ToggleSnapToGridGuideIntersections");
2273 Glib::RefPtr<Gtk::Action> act14 = mainActions->get_action("ToggleSnapToGrids");
2274 Glib::RefPtr<Gtk::Action> act15 = mainActions->get_action("ToggleSnapToGuides");
2277 if (!act1) {
2278 return; // The snap actions haven't been defined yet (might be the case during startup)
2279 }
2281 // The ..._set_active calls below will toggle the buttons, but this shouldn't lead to
2282 // changes in our document because we're only updating the UI;
2283 // Setting the "freeze" parameter to true will block the code in toggle_snap_callback()
2284 g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(TRUE));
2286 bool const c1 = nv->snap_manager.snapprefs.getSnapEnabledGlobally();
2287 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act1->gobj()), c1);
2289 bool const c2 = nv->snap_manager.snapprefs.getSnapModeBBox();
2290 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act2->gobj()), c2);
2291 gtk_action_set_sensitive(GTK_ACTION(act2->gobj()), c1);
2293 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act3->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxPath());
2294 gtk_action_set_sensitive(GTK_ACTION(act3->gobj()), c1 && c2);
2295 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxNode());
2296 gtk_action_set_sensitive(GTK_ACTION(act4->gobj()), c1 && c2);
2297 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4b->gobj()), nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints());
2298 gtk_action_set_sensitive(GTK_ACTION(act4b->gobj()), c1 && c2);
2299 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4c->gobj()), nv->snap_manager.snapprefs.getSnapBBoxMidpoints());
2300 gtk_action_set_sensitive(GTK_ACTION(act4c->gobj()), c1 && c2);
2302 bool const c3 = nv->snap_manager.snapprefs.getSnapModeNode();
2303 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act5->gobj()), c3);
2304 gtk_action_set_sensitive(GTK_ACTION(act5->gobj()), c1);
2306 bool const c4 = nv->snap_manager.snapprefs.getSnapToItemPath();
2307 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6->gobj()), c4);
2308 gtk_action_set_sensitive(GTK_ACTION(act6->gobj()), c1 && c3);
2309 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6b->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionCS());
2310 gtk_action_set_sensitive(GTK_ACTION(act6b->gobj()), c1 && c3 && c4);
2311 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act7->gobj()), nv->snap_manager.snapprefs.getSnapToItemNode());
2312 gtk_action_set_sensitive(GTK_ACTION(act7->gobj()), c1 && c3);
2313 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act8->gobj()), nv->snap_manager.snapprefs.getSnapSmoothNodes());
2314 gtk_action_set_sensitive(GTK_ACTION(act8->gobj()), c1 && c3);
2315 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act9->gobj()), nv->snap_manager.snapprefs.getSnapLineMidpoints());
2316 gtk_action_set_sensitive(GTK_ACTION(act9->gobj()), c1 && c3);
2317 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act10->gobj()), nv->snap_manager.snapprefs.getSnapObjectMidpoints());
2318 gtk_action_set_sensitive(GTK_ACTION(act10->gobj()), c1 && c3);
2319 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act11->gobj()), nv->snap_manager.snapprefs.getIncludeItemCenter());
2320 gtk_action_set_sensitive(GTK_ACTION(act11->gobj()), c1 && c3);
2322 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act12->gobj()), nv->snap_manager.snapprefs.getSnapToPageBorder());
2323 gtk_action_set_sensitive(GTK_ACTION(act12->gobj()), c1);
2324 //gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act13->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionGG());
2325 //gtk_action_set_sensitive(GTK_ACTION(act13->gobj()), c1);
2327 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act14->gobj()), nv->snap_manager.snapprefs.getSnapToGrids());
2328 gtk_action_set_sensitive(GTK_ACTION(act14->gobj()), c1);
2329 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act15->gobj()), nv->snap_manager.snapprefs.getSnapToGuides());
2330 gtk_action_set_sensitive(GTK_ACTION(act15->gobj()), c1);
2333 g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(FALSE)); // unfreeze (see above)
2334 }
2336 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
2337 {
2338 gtk_widget_show(toolbox_toplevel);
2339 GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
2341 GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
2342 if (!shown_toolbox) {
2343 return;
2344 }
2345 gtk_widget_show(toolbox);
2347 gtk_widget_show_all(shown_toolbox);
2348 }
2350 static GtkWidget *
2351 sp_empty_toolbox_new(SPDesktop *desktop)
2352 {
2353 GtkWidget *tbl = gtk_toolbar_new();
2354 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
2355 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
2357 gtk_widget_show_all(tbl);
2358 sp_set_font_size_smaller (tbl);
2360 return tbl;
2361 }
2363 #define MODE_LABEL_WIDTH 70
2365 //########################
2366 //## Star ##
2367 //########################
2369 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2370 {
2371 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2373 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2374 // do not remember prefs if this call is initiated by an undo change, because undoing object
2375 // creation sets bogus values to its attributes before it is deleted
2376 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2377 prefs->setInt("/tools/shapes/star/magnitude", (gint)adj->value);
2378 }
2380 // quit if run by the attr_changed listener
2381 if (g_object_get_data( dataKludge, "freeze" )) {
2382 return;
2383 }
2385 // in turn, prevent listener from responding
2386 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2388 bool modmade = false;
2390 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2391 GSList const *items = selection->itemList();
2392 for (; items != NULL; items = items->next) {
2393 if (SP_IS_STAR((SPItem *) items->data)) {
2394 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2395 sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
2396 sp_repr_set_svg_double(repr, "sodipodi:arg2",
2397 (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
2398 + M_PI / (gint)adj->value));
2399 SP_OBJECT((SPItem *) items->data)->updateRepr();
2400 modmade = true;
2401 }
2402 }
2403 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2404 _("Star: Change number of corners"));
2406 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2407 }
2409 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2410 {
2411 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2413 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2414 if (!IS_NAN(adj->value)) {
2415 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2416 prefs->setDouble("/tools/shapes/star/proportion", adj->value);
2417 }
2418 }
2420 // quit if run by the attr_changed listener
2421 if (g_object_get_data( dataKludge, "freeze" )) {
2422 return;
2423 }
2425 // in turn, prevent listener from responding
2426 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2428 bool modmade = false;
2429 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2430 GSList const *items = selection->itemList();
2431 for (; items != NULL; items = items->next) {
2432 if (SP_IS_STAR((SPItem *) items->data)) {
2433 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2435 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2436 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2437 if (r2 < r1) {
2438 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
2439 } else {
2440 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
2441 }
2443 SP_OBJECT((SPItem *) items->data)->updateRepr();
2444 modmade = true;
2445 }
2446 }
2448 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2449 _("Star: Change spoke ratio"));
2451 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2452 }
2454 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
2455 {
2456 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2457 bool flat = ege_select_one_action_get_active( act ) == 0;
2459 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2460 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2461 prefs->setBool( "/tools/shapes/star/isflatsided", flat);
2462 }
2464 // quit if run by the attr_changed listener
2465 if (g_object_get_data( dataKludge, "freeze" )) {
2466 return;
2467 }
2469 // in turn, prevent listener from responding
2470 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2472 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2473 GSList const *items = selection->itemList();
2474 GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2475 bool modmade = false;
2477 if ( prop_action ) {
2478 gtk_action_set_sensitive( prop_action, !flat );
2479 }
2481 for (; items != NULL; items = items->next) {
2482 if (SP_IS_STAR((SPItem *) items->data)) {
2483 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2484 repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
2485 SP_OBJECT((SPItem *) items->data)->updateRepr();
2486 modmade = true;
2487 }
2488 }
2490 if (modmade) {
2491 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2492 flat ? _("Make polygon") : _("Make star"));
2493 }
2495 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2496 }
2498 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2499 {
2500 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2502 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2503 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2504 prefs->setDouble("/tools/shapes/star/rounded", (gdouble) adj->value);
2505 }
2507 // quit if run by the attr_changed listener
2508 if (g_object_get_data( dataKludge, "freeze" )) {
2509 return;
2510 }
2512 // in turn, prevent listener from responding
2513 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2515 bool modmade = false;
2517 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2518 GSList const *items = selection->itemList();
2519 for (; items != NULL; items = items->next) {
2520 if (SP_IS_STAR((SPItem *) items->data)) {
2521 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2522 sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2523 SP_OBJECT(items->data)->updateRepr();
2524 modmade = true;
2525 }
2526 }
2527 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2528 _("Star: Change rounding"));
2530 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2531 }
2533 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2534 {
2535 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2537 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2538 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2539 prefs->setDouble("/tools/shapes/star/randomized", (gdouble) adj->value);
2540 }
2542 // quit if run by the attr_changed listener
2543 if (g_object_get_data( dataKludge, "freeze" )) {
2544 return;
2545 }
2547 // in turn, prevent listener from responding
2548 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2550 bool modmade = false;
2552 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2553 GSList const *items = selection->itemList();
2554 for (; items != NULL; items = items->next) {
2555 if (SP_IS_STAR((SPItem *) items->data)) {
2556 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2557 sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2558 SP_OBJECT(items->data)->updateRepr();
2559 modmade = true;
2560 }
2561 }
2562 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2563 _("Star: Change randomization"));
2565 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2566 }
2569 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2570 gchar const */*old_value*/, gchar const */*new_value*/,
2571 bool /*is_interactive*/, gpointer data)
2572 {
2573 GtkWidget *tbl = GTK_WIDGET(data);
2575 // quit if run by the _changed callbacks
2576 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2577 return;
2578 }
2580 // in turn, prevent callbacks from responding
2581 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2583 GtkAdjustment *adj = 0;
2585 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2586 bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2588 if (!strcmp(name, "inkscape:randomized")) {
2589 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2590 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2591 } else if (!strcmp(name, "inkscape:rounded")) {
2592 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2593 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2594 } else if (!strcmp(name, "inkscape:flatsided")) {
2595 GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2596 char const *flatsides = repr->attribute("inkscape:flatsided");
2597 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2598 if ( flatsides && !strcmp(flatsides,"false") ) {
2599 ege_select_one_action_set_active( flat_action, 1 );
2600 gtk_action_set_sensitive( prop_action, TRUE );
2601 } else {
2602 ege_select_one_action_set_active( flat_action, 0 );
2603 gtk_action_set_sensitive( prop_action, FALSE );
2604 }
2605 } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2606 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2607 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2608 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2609 if (r2 < r1) {
2610 gtk_adjustment_set_value(adj, r2/r1);
2611 } else {
2612 gtk_adjustment_set_value(adj, r1/r2);
2613 }
2614 } else if (!strcmp(name, "sodipodi:sides")) {
2615 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2616 gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2617 }
2619 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2620 }
2623 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2624 {
2625 NULL, /* child_added */
2626 NULL, /* child_removed */
2627 star_tb_event_attr_changed,
2628 NULL, /* content_changed */
2629 NULL /* order_changed */
2630 };
2633 /**
2634 * \param selection Should not be NULL.
2635 */
2636 static void
2637 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2638 {
2639 int n_selected = 0;
2640 Inkscape::XML::Node *repr = NULL;
2642 purge_repr_listener( tbl, tbl );
2644 for (GSList const *items = selection->itemList();
2645 items != NULL;
2646 items = items->next)
2647 {
2648 if (SP_IS_STAR((SPItem *) items->data)) {
2649 n_selected++;
2650 repr = SP_OBJECT_REPR((SPItem *) items->data);
2651 }
2652 }
2654 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2656 if (n_selected == 0) {
2657 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2658 } else if (n_selected == 1) {
2659 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2661 if (repr) {
2662 g_object_set_data( tbl, "repr", repr );
2663 Inkscape::GC::anchor(repr);
2664 sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2665 sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2666 }
2667 } else {
2668 // FIXME: implement averaging of all parameters for multiple selected stars
2669 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2670 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2671 }
2672 }
2675 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2676 {
2677 // FIXME: in this and all other _default functions, set some flag telling the value_changed
2678 // callbacks to lump all the changes for all selected objects in one undo step
2680 GtkAdjustment *adj = 0;
2682 // fixme: make settable in prefs!
2683 gint mag = 5;
2684 gdouble prop = 0.5;
2685 gboolean flat = FALSE;
2686 gdouble randomized = 0;
2687 gdouble rounded = 0;
2689 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2690 ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2692 GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2693 gtk_action_set_sensitive( sb2, !flat );
2695 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2696 gtk_adjustment_set_value(adj, mag);
2697 gtk_adjustment_value_changed(adj);
2699 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2700 gtk_adjustment_set_value(adj, prop);
2701 gtk_adjustment_value_changed(adj);
2703 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2704 gtk_adjustment_set_value(adj, rounded);
2705 gtk_adjustment_value_changed(adj);
2707 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2708 gtk_adjustment_set_value(adj, randomized);
2709 gtk_adjustment_value_changed(adj);
2710 }
2713 void
2714 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2715 {
2716 GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2717 if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2718 GtkWidget *l = gtk_label_new(NULL);
2719 gtk_label_set_markup(GTK_LABEL(l), title);
2720 gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2721 if ( GTK_IS_TOOLBAR(tbl) ) {
2722 gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2723 } else {
2724 gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2725 }
2726 gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2727 }
2730 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2731 {
2732 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
2734 {
2735 EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2736 ege_output_action_set_use_markup( act, TRUE );
2737 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2738 g_object_set_data( holder, "mode_action", act );
2739 }
2741 {
2742 EgeAdjustmentAction* eact = 0;
2743 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2744 bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2746 /* Flatsided checkbox */
2747 {
2748 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2750 GtkTreeIter iter;
2751 gtk_list_store_append( model, &iter );
2752 gtk_list_store_set( model, &iter,
2753 0, _("Polygon"),
2754 1, _("Regular polygon (with one handle) instead of a star"),
2755 2, INKSCAPE_ICON_DRAW_POLYGON,
2756 -1 );
2758 gtk_list_store_append( model, &iter );
2759 gtk_list_store_set( model, &iter,
2760 0, _("Star"),
2761 1, _("Star instead of a regular polygon (with one handle)"),
2762 2, INKSCAPE_ICON_DRAW_STAR,
2763 -1 );
2765 EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2766 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2767 g_object_set_data( holder, "flat_action", act );
2769 ege_select_one_action_set_appearance( act, "full" );
2770 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2771 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2772 ege_select_one_action_set_icon_column( act, 2 );
2773 ege_select_one_action_set_icon_size( act, secondarySize );
2774 ege_select_one_action_set_tooltip_column( act, 1 );
2776 ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2777 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2778 }
2780 /* Magnitude */
2781 {
2782 gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2783 gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2784 eact = create_adjustment_action( "MagnitudeAction",
2785 _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2786 "/tools/shapes/star/magnitude", 3,
2787 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2788 3, 1024, 1, 5,
2789 labels, values, G_N_ELEMENTS(labels),
2790 sp_stb_magnitude_value_changed,
2791 1.0, 0 );
2792 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2793 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2794 }
2796 /* Spoke ratio */
2797 {
2798 gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2799 gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2800 eact = create_adjustment_action( "SpokeAction",
2801 _("Spoke ratio"), _("Spoke ratio:"),
2802 // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2803 // Base radius is the same for the closest handle.
2804 _("Base radius to tip radius ratio"),
2805 "/tools/shapes/star/proportion", 0.5,
2806 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2807 0.01, 1.0, 0.01, 0.1,
2808 labels, values, G_N_ELEMENTS(labels),
2809 sp_stb_proportion_value_changed );
2810 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2811 g_object_set_data( holder, "prop_action", eact );
2812 }
2814 if ( !isFlatSided ) {
2815 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2816 } else {
2817 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2818 }
2820 /* Roundedness */
2821 {
2822 gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2823 gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2824 eact = create_adjustment_action( "RoundednessAction",
2825 _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2826 "/tools/shapes/star/rounded", 0.0,
2827 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2828 -10.0, 10.0, 0.01, 0.1,
2829 labels, values, G_N_ELEMENTS(labels),
2830 sp_stb_rounded_value_changed );
2831 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2832 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2833 }
2835 /* Randomization */
2836 {
2837 gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2838 gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2839 eact = create_adjustment_action( "RandomizationAction",
2840 _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2841 "/tools/shapes/star/randomized", 0.0,
2842 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2843 -10.0, 10.0, 0.001, 0.01,
2844 labels, values, G_N_ELEMENTS(labels),
2845 sp_stb_randomized_value_changed, 0.1, 3 );
2846 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2847 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2848 }
2849 }
2851 {
2852 /* Reset */
2853 {
2854 GtkAction* act = gtk_action_new( "StarResetAction",
2855 _("Defaults"),
2856 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2857 GTK_STOCK_CLEAR );
2858 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2859 gtk_action_group_add_action( mainActions, act );
2860 gtk_action_set_sensitive( act, TRUE );
2861 }
2862 }
2864 sigc::connection *connection = new sigc::connection(
2865 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2866 );
2867 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2868 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2869 }
2872 //########################
2873 //## Rect ##
2874 //########################
2876 static void sp_rtb_sensitivize( GObject *tbl )
2877 {
2878 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2879 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2880 GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2882 if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2883 gtk_action_set_sensitive( not_rounded, FALSE );
2884 } else {
2885 gtk_action_set_sensitive( not_rounded, TRUE );
2886 }
2887 }
2890 static void
2891 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2892 void (*setter)(SPRect *, gdouble))
2893 {
2894 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2896 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2897 SPUnit const *unit = tracker->getActiveUnit();
2899 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2900 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2901 prefs->setDouble(Glib::ustring("/tools/shapes/rect/") + value_name, sp_units_get_pixels(adj->value, *unit));
2902 }
2904 // quit if run by the attr_changed listener
2905 if (g_object_get_data( tbl, "freeze" )) {
2906 return;
2907 }
2909 // in turn, prevent listener from responding
2910 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2912 bool modmade = false;
2913 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2914 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2915 if (SP_IS_RECT(items->data)) {
2916 if (adj->value != 0) {
2917 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2918 } else {
2919 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2920 }
2921 modmade = true;
2922 }
2923 }
2925 sp_rtb_sensitivize( tbl );
2927 if (modmade) {
2928 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2929 _("Change rectangle"));
2930 }
2932 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2933 }
2935 static void
2936 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2937 {
2938 sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2939 }
2941 static void
2942 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2943 {
2944 sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2945 }
2947 static void
2948 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2949 {
2950 sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2951 }
2953 static void
2954 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2955 {
2956 sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2957 }
2961 static void
2962 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2963 {
2964 GtkAdjustment *adj = 0;
2966 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2967 gtk_adjustment_set_value(adj, 0.0);
2968 // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2969 gtk_adjustment_value_changed(adj);
2971 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2972 gtk_adjustment_set_value(adj, 0.0);
2973 gtk_adjustment_value_changed(adj);
2975 sp_rtb_sensitivize( obj );
2976 }
2978 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2979 gchar const */*old_value*/, gchar const */*new_value*/,
2980 bool /*is_interactive*/, gpointer data)
2981 {
2982 GObject *tbl = G_OBJECT(data);
2984 // quit if run by the _changed callbacks
2985 if (g_object_get_data( tbl, "freeze" )) {
2986 return;
2987 }
2989 // in turn, prevent callbacks from responding
2990 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2992 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2993 SPUnit const *unit = tracker->getActiveUnit();
2995 gpointer item = g_object_get_data( tbl, "item" );
2996 if (item && SP_IS_RECT(item)) {
2997 {
2998 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2999 gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
3000 gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
3001 }
3003 {
3004 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
3005 gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
3006 gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
3007 }
3009 {
3010 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
3011 gdouble width = sp_rect_get_visible_width (SP_RECT(item));
3012 gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
3013 }
3015 {
3016 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
3017 gdouble height = sp_rect_get_visible_height (SP_RECT(item));
3018 gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
3019 }
3020 }
3022 sp_rtb_sensitivize( tbl );
3024 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3025 }
3028 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
3029 NULL, /* child_added */
3030 NULL, /* child_removed */
3031 rect_tb_event_attr_changed,
3032 NULL, /* content_changed */
3033 NULL /* order_changed */
3034 };
3036 /**
3037 * \param selection should not be NULL.
3038 */
3039 static void
3040 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3041 {
3042 int n_selected = 0;
3043 Inkscape::XML::Node *repr = NULL;
3044 SPItem *item = NULL;
3046 if ( g_object_get_data( tbl, "repr" ) ) {
3047 g_object_set_data( tbl, "item", NULL );
3048 }
3049 purge_repr_listener( tbl, tbl );
3051 for (GSList const *items = selection->itemList();
3052 items != NULL;
3053 items = items->next) {
3054 if (SP_IS_RECT((SPItem *) items->data)) {
3055 n_selected++;
3056 item = (SPItem *) items->data;
3057 repr = SP_OBJECT_REPR(item);
3058 }
3059 }
3061 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3063 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3065 if (n_selected == 0) {
3066 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3068 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3069 gtk_action_set_sensitive(w, FALSE);
3070 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3071 gtk_action_set_sensitive(h, FALSE);
3073 } else if (n_selected == 1) {
3074 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3075 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3077 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3078 gtk_action_set_sensitive(w, TRUE);
3079 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3080 gtk_action_set_sensitive(h, TRUE);
3082 if (repr) {
3083 g_object_set_data( tbl, "repr", repr );
3084 g_object_set_data( tbl, "item", item );
3085 Inkscape::GC::anchor(repr);
3086 sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
3087 sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
3088 }
3089 } else {
3090 // FIXME: implement averaging of all parameters for multiple selected
3091 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3092 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3093 sp_rtb_sensitivize( tbl );
3094 }
3095 }
3098 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3099 {
3100 EgeAdjustmentAction* eact = 0;
3101 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3103 {
3104 EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
3105 ege_output_action_set_use_markup( act, TRUE );
3106 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3107 g_object_set_data( holder, "mode_action", act );
3108 }
3110 // rx/ry units menu: create
3111 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
3112 //tracker->addUnit( SP_UNIT_PERCENT, 0 );
3113 // fixme: add % meaning per cent of the width/height
3114 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
3115 g_object_set_data( holder, "tracker", tracker );
3117 /* W */
3118 {
3119 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3120 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3121 eact = create_adjustment_action( "RectWidthAction",
3122 _("Width"), _("W:"), _("Width of rectangle"),
3123 "/tools/shapes/rect/width", 0,
3124 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
3125 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3126 labels, values, G_N_ELEMENTS(labels),
3127 sp_rtb_width_value_changed );
3128 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3129 g_object_set_data( holder, "width_action", eact );
3130 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3131 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3132 }
3134 /* H */
3135 {
3136 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3137 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3138 eact = create_adjustment_action( "RectHeightAction",
3139 _("Height"), _("H:"), _("Height of rectangle"),
3140 "/tools/shapes/rect/height", 0,
3141 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3142 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3143 labels, values, G_N_ELEMENTS(labels),
3144 sp_rtb_height_value_changed );
3145 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3146 g_object_set_data( holder, "height_action", eact );
3147 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3148 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3149 }
3151 /* rx */
3152 {
3153 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3154 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3155 eact = create_adjustment_action( "RadiusXAction",
3156 _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
3157 "/tools/shapes/rect/rx", 0,
3158 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3159 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3160 labels, values, G_N_ELEMENTS(labels),
3161 sp_rtb_rx_value_changed);
3162 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3163 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3164 }
3166 /* ry */
3167 {
3168 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3169 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3170 eact = create_adjustment_action( "RadiusYAction",
3171 _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
3172 "/tools/shapes/rect/ry", 0,
3173 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3174 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3175 labels, values, G_N_ELEMENTS(labels),
3176 sp_rtb_ry_value_changed);
3177 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3178 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3179 }
3181 // add the units menu
3182 {
3183 GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
3184 gtk_action_group_add_action( mainActions, act );
3185 }
3187 /* Reset */
3188 {
3189 InkAction* inky = ink_action_new( "RectResetAction",
3190 _("Not rounded"),
3191 _("Make corners sharp"),
3192 INKSCAPE_ICON_RECTANGLE_MAKE_CORNERS_SHARP,
3193 secondarySize );
3194 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
3195 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3196 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3197 g_object_set_data( holder, "not_rounded", inky );
3198 }
3200 g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
3201 sp_rtb_sensitivize( holder );
3203 sigc::connection *connection = new sigc::connection(
3204 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
3205 );
3206 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3207 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3208 }
3210 //########################
3211 //## 3D Box ##
3212 //########################
3214 // normalize angle so that it lies in the interval [0,360]
3215 static double box3d_normalize_angle (double a) {
3216 double angle = a + ((int) (a/360.0))*360;
3217 if (angle < 0) {
3218 angle += 360.0;
3219 }
3220 return angle;
3221 }
3223 static void
3224 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
3225 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
3226 // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
3227 // have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
3228 // are reset).
3229 bool is_infinite = !persp3d_VP_is_finite(persp, axis);
3231 if (is_infinite) {
3232 gtk_toggle_action_set_active(tact, TRUE);
3233 gtk_action_set_sensitive(act, TRUE);
3235 double angle = persp3d_get_infinite_angle(persp, axis);
3236 if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
3237 gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
3238 }
3239 } else {
3240 gtk_toggle_action_set_active(tact, FALSE);
3241 gtk_action_set_sensitive(act, FALSE);
3242 }
3243 }
3245 static void
3246 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
3247 if (!persp_repr) {
3248 g_print ("No perspective given to box3d_resync_toolbar().\n");
3249 return;
3250 }
3252 GtkWidget *tbl = GTK_WIDGET(data);
3253 GtkAdjustment *adj = 0;
3254 GtkAction *act = 0;
3255 GtkToggleAction *tact = 0;
3256 Persp3D *persp = persp3d_get_from_repr(persp_repr);
3257 {
3258 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
3259 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
3260 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
3262 box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
3263 }
3264 {
3265 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
3266 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
3267 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
3269 box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
3270 }
3271 {
3272 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
3273 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
3274 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
3276 box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
3277 }
3278 }
3280 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3281 gchar const */*old_value*/, gchar const */*new_value*/,
3282 bool /*is_interactive*/, gpointer data)
3283 {
3284 GtkWidget *tbl = GTK_WIDGET(data);
3286 // quit if run by the attr_changed or selection changed listener
3287 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3288 return;
3289 }
3291 // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
3292 // sp_document_maybe_done() when the document is undo insensitive)
3293 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3295 // TODO: Only update the appropriate part of the toolbar
3296 // if (!strcmp(name, "inkscape:vp_z")) {
3297 box3d_resync_toolbar(repr, G_OBJECT(tbl));
3298 // }
3300 Persp3D *persp = persp3d_get_from_repr(repr);
3301 persp3d_update_box_reprs(persp);
3303 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3304 }
3306 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
3307 {
3308 NULL, /* child_added */
3309 NULL, /* child_removed */
3310 box3d_persp_tb_event_attr_changed,
3311 NULL, /* content_changed */
3312 NULL /* order_changed */
3313 };
3315 /**
3316 * \param selection Should not be NULL.
3317 */
3318 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
3319 // Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
3320 static void
3321 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3322 {
3323 // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
3324 // disable the angle entry fields for this direction (otherwise entering a value in them should only
3325 // update the perspectives with infinite VPs and leave the other ones untouched).
3327 Inkscape::XML::Node *persp_repr = NULL;
3328 purge_repr_listener(tbl, tbl);
3330 SPItem *item = selection->singleItem();
3331 if (item && SP_IS_BOX3D(item)) {
3332 // FIXME: Also deal with multiple selected boxes
3333 SPBox3D *box = SP_BOX3D(item);
3334 Persp3D *persp = box3d_get_perspective(box);
3335 persp_repr = SP_OBJECT_REPR(persp);
3336 if (persp_repr) {
3337 g_object_set_data(tbl, "repr", persp_repr);
3338 Inkscape::GC::anchor(persp_repr);
3339 sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
3340 sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
3341 }
3343 inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
3344 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3345 prefs->setString("/tools/shapes/3dbox/persp", persp_repr->attribute("id"));
3347 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
3348 box3d_resync_toolbar(persp_repr, tbl);
3349 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
3350 }
3351 }
3353 static void
3354 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
3355 {
3356 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3357 SPDocument *document = sp_desktop_document(desktop);
3359 // quit if run by the attr_changed or selection changed listener
3360 if (g_object_get_data( dataKludge, "freeze" )) {
3361 return;
3362 }
3364 // in turn, prevent listener from responding
3365 g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
3367 //Persp3D *persp = document->current_persp3d;
3368 std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
3369 if (sel_persps.empty()) {
3370 // this can happen when the document is created; we silently ignore it
3371 return;
3372 }
3373 Persp3D *persp = sel_persps.front();
3375 persp->tmat.set_infinite_direction (axis, adj->value);
3376 SP_OBJECT(persp)->updateRepr();
3378 // TODO: use the correct axis here, too
3379 sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
3381 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
3382 }
3385 static void
3386 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3387 {
3388 box3d_angle_value_changed(adj, dataKludge, Proj::X);
3389 }
3391 static void
3392 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3393 {
3394 box3d_angle_value_changed(adj, dataKludge, Proj::Y);
3395 }
3397 static void
3398 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3399 {
3400 box3d_angle_value_changed(adj, dataKludge, Proj::Z);
3401 }
3404 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
3405 {
3406 // TODO: Take all selected perspectives into account
3407 std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
3408 if (sel_persps.empty()) {
3409 // this can happen when the document is created; we silently ignore it
3410 return;
3411 }
3412 Persp3D *persp = sel_persps.front();
3414 bool set_infinite = gtk_toggle_action_get_active(act);
3415 persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
3416 }
3418 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3419 {
3420 box3d_vp_state_changed(act, box3d_angle, Proj::X);
3421 }
3423 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3424 {
3425 box3d_vp_state_changed(act, box3d_angle, Proj::Y);
3426 }
3428 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3429 {
3430 box3d_vp_state_changed(act, box3d_angle, Proj::Z);
3431 }
3433 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3434 {
3435 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3436 EgeAdjustmentAction* eact = 0;
3437 SPDocument *document = sp_desktop_document (desktop);
3438 Persp3D *persp = document->current_persp3d;
3440 EgeAdjustmentAction* box3d_angle_x = 0;
3441 EgeAdjustmentAction* box3d_angle_y = 0;
3442 EgeAdjustmentAction* box3d_angle_z = 0;
3444 /* Angle X */
3445 {
3446 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3447 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3448 eact = create_adjustment_action( "3DBoxAngleXAction",
3449 _("Angle in X direction"), _("Angle X:"),
3450 // Translators: PL is short for 'perspective line'
3451 _("Angle of PLs in X direction"),
3452 "/tools/shapes/3dbox/box3d_angle_x", 30,
3453 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
3454 -360.0, 360.0, 1.0, 10.0,
3455 labels, values, G_N_ELEMENTS(labels),
3456 box3d_angle_x_value_changed );
3457 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3458 g_object_set_data( holder, "box3d_angle_x_action", eact );
3459 box3d_angle_x = eact;
3460 }
3462 if (!persp3d_VP_is_finite(persp, Proj::X)) {
3463 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3464 } else {
3465 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3466 }
3469 /* VP X state */
3470 {
3471 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
3472 // Translators: VP is short for 'vanishing point'
3473 _("State of VP in X direction"),
3474 _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
3475 INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3476 Inkscape::ICON_SIZE_DECORATION );
3477 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3478 g_object_set_data( holder, "box3d_vp_x_state_action", act );
3479 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
3480 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3481 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3482 }
3484 /* Angle Y */
3485 {
3486 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3487 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3488 eact = create_adjustment_action( "3DBoxAngleYAction",
3489 _("Angle in Y direction"), _("Angle Y:"),
3490 // Translators: PL is short for 'perspective line'
3491 _("Angle of PLs in Y direction"),
3492 "/tools/shapes/3dbox/box3d_angle_y", 30,
3493 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3494 -360.0, 360.0, 1.0, 10.0,
3495 labels, values, G_N_ELEMENTS(labels),
3496 box3d_angle_y_value_changed );
3497 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3498 g_object_set_data( holder, "box3d_angle_y_action", eact );
3499 box3d_angle_y = eact;
3500 }
3502 if (!persp3d_VP_is_finite(persp, Proj::Y)) {
3503 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3504 } else {
3505 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3506 }
3508 /* VP Y state */
3509 {
3510 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
3511 // Translators: VP is short for 'vanishing point'
3512 _("State of VP in Y direction"),
3513 _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
3514 INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3515 Inkscape::ICON_SIZE_DECORATION );
3516 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3517 g_object_set_data( holder, "box3d_vp_y_state_action", act );
3518 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
3519 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3520 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3521 }
3523 /* Angle Z */
3524 {
3525 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3526 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3527 eact = create_adjustment_action( "3DBoxAngleZAction",
3528 _("Angle in Z direction"), _("Angle Z:"),
3529 // Translators: PL is short for 'perspective line'
3530 _("Angle of PLs in Z direction"),
3531 "/tools/shapes/3dbox/box3d_angle_z", 30,
3532 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3533 -360.0, 360.0, 1.0, 10.0,
3534 labels, values, G_N_ELEMENTS(labels),
3535 box3d_angle_z_value_changed );
3536 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3537 g_object_set_data( holder, "box3d_angle_z_action", eact );
3538 box3d_angle_z = eact;
3539 }
3541 if (!persp3d_VP_is_finite(persp, Proj::Z)) {
3542 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3543 } else {
3544 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3545 }
3547 /* VP Z state */
3548 {
3549 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3550 // Translators: VP is short for 'vanishing point'
3551 _("State of VP in Z direction"),
3552 _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3553 INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3554 Inkscape::ICON_SIZE_DECORATION );
3555 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3556 g_object_set_data( holder, "box3d_vp_z_state_action", act );
3557 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3558 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3559 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3560 }
3562 sigc::connection *connection = new sigc::connection(
3563 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3564 );
3565 g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3566 g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3567 }
3569 //########################
3570 //## Spiral ##
3571 //########################
3573 static void
3574 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, Glib::ustring const &value_name)
3575 {
3576 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3578 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3579 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3580 prefs->setDouble("/tools/shapes/spiral/" + value_name, adj->value);
3581 }
3583 // quit if run by the attr_changed listener
3584 if (g_object_get_data( tbl, "freeze" )) {
3585 return;
3586 }
3588 // in turn, prevent listener from responding
3589 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3591 gchar* namespaced_name = g_strconcat("sodipodi:", value_name.data(), NULL);
3593 bool modmade = false;
3594 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3595 items != NULL;
3596 items = items->next)
3597 {
3598 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3599 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3600 sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3601 SP_OBJECT((SPItem *) items->data)->updateRepr();
3602 modmade = true;
3603 }
3604 }
3606 g_free(namespaced_name);
3608 if (modmade) {
3609 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3610 _("Change spiral"));
3611 }
3613 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3614 }
3616 static void
3617 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3618 {
3619 sp_spl_tb_value_changed(adj, tbl, "revolution");
3620 }
3622 static void
3623 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3624 {
3625 sp_spl_tb_value_changed(adj, tbl, "expansion");
3626 }
3628 static void
3629 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3630 {
3631 sp_spl_tb_value_changed(adj, tbl, "t0");
3632 }
3634 static void
3635 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3636 {
3637 GtkWidget *tbl = GTK_WIDGET(obj);
3639 GtkAdjustment *adj;
3641 // fixme: make settable
3642 gdouble rev = 5;
3643 gdouble exp = 1.0;
3644 gdouble t0 = 0.0;
3646 adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3647 gtk_adjustment_set_value(adj, rev);
3648 gtk_adjustment_value_changed(adj);
3650 adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3651 gtk_adjustment_set_value(adj, exp);
3652 gtk_adjustment_value_changed(adj);
3654 adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3655 gtk_adjustment_set_value(adj, t0);
3656 gtk_adjustment_value_changed(adj);
3658 spinbutton_defocus(GTK_OBJECT(tbl));
3659 }
3662 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3663 gchar const */*old_value*/, gchar const */*new_value*/,
3664 bool /*is_interactive*/, gpointer data)
3665 {
3666 GtkWidget *tbl = GTK_WIDGET(data);
3668 // quit if run by the _changed callbacks
3669 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3670 return;
3671 }
3673 // in turn, prevent callbacks from responding
3674 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3676 GtkAdjustment *adj;
3677 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3678 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3680 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3681 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3683 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3684 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3686 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3687 }
3690 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3691 NULL, /* child_added */
3692 NULL, /* child_removed */
3693 spiral_tb_event_attr_changed,
3694 NULL, /* content_changed */
3695 NULL /* order_changed */
3696 };
3698 static void
3699 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3700 {
3701 int n_selected = 0;
3702 Inkscape::XML::Node *repr = NULL;
3704 purge_repr_listener( tbl, tbl );
3706 for (GSList const *items = selection->itemList();
3707 items != NULL;
3708 items = items->next)
3709 {
3710 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3711 n_selected++;
3712 repr = SP_OBJECT_REPR((SPItem *) items->data);
3713 }
3714 }
3716 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3718 if (n_selected == 0) {
3719 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3720 } else if (n_selected == 1) {
3721 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3723 if (repr) {
3724 g_object_set_data( tbl, "repr", repr );
3725 Inkscape::GC::anchor(repr);
3726 sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3727 sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3728 }
3729 } else {
3730 // FIXME: implement averaging of all parameters for multiple selected
3731 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3732 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3733 }
3734 }
3737 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3738 {
3739 EgeAdjustmentAction* eact = 0;
3740 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3742 {
3743 EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3744 ege_output_action_set_use_markup( act, TRUE );
3745 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3746 g_object_set_data( holder, "mode_action", act );
3747 }
3749 /* Revolution */
3750 {
3751 gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3752 gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3753 eact = create_adjustment_action( "SpiralRevolutionAction",
3754 _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3755 "/tools/shapes/spiral/revolution", 3.0,
3756 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3757 0.01, 1024.0, 0.1, 1.0,
3758 labels, values, G_N_ELEMENTS(labels),
3759 sp_spl_tb_revolution_value_changed, 1, 2);
3760 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3761 }
3763 /* Expansion */
3764 {
3765 gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3766 gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3767 eact = create_adjustment_action( "SpiralExpansionAction",
3768 _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3769 "/tools/shapes/spiral/expansion", 1.0,
3770 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3771 0.0, 1000.0, 0.01, 1.0,
3772 labels, values, G_N_ELEMENTS(labels),
3773 sp_spl_tb_expansion_value_changed);
3774 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3775 }
3777 /* T0 */
3778 {
3779 gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3780 gdouble values[] = {0, 0.5, 0.9};
3781 eact = create_adjustment_action( "SpiralT0Action",
3782 _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3783 "/tools/shapes/spiral/t0", 0.0,
3784 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3785 0.0, 0.999, 0.01, 1.0,
3786 labels, values, G_N_ELEMENTS(labels),
3787 sp_spl_tb_t0_value_changed);
3788 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3789 }
3791 /* Reset */
3792 {
3793 InkAction* inky = ink_action_new( "SpiralResetAction",
3794 _("Defaults"),
3795 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3796 GTK_STOCK_CLEAR,
3797 secondarySize );
3798 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3799 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3800 }
3803 sigc::connection *connection = new sigc::connection(
3804 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3805 );
3806 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3807 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3808 }
3810 //########################
3811 //## Pen/Pencil ##
3812 //########################
3814 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3815 static Glib::ustring const
3816 freehand_tool_name(GObject *dataKludge)
3817 {
3818 SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3819 return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3820 ? "/tools/freehand/pen"
3821 : "/tools/freehand/pencil" );
3822 }
3824 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3825 {
3826 gint mode = ege_select_one_action_get_active(act);
3828 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3829 prefs->setInt(freehand_tool_name(tbl) + "/freehand-mode", mode);
3831 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3833 // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3834 // preparatory work here
3835 if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3836 SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3837 sp_pen_context_set_polyline_mode(pc);
3838 }
3839 }
3841 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3842 {
3843 /* Freehand mode toggle buttons */
3844 {
3845 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3846 guint freehandMode = prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/freehand-mode" : "/tools/freehand/pen/freehand-mode" ), 0);
3847 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3849 {
3850 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3852 GtkTreeIter iter;
3853 gtk_list_store_append( model, &iter );
3854 gtk_list_store_set( model, &iter,
3855 0, _("Bezier"),
3856 1, _("Create regular Bezier path"),
3857 2, INKSCAPE_ICON_PATH_MODE_BEZIER,
3858 -1 );
3860 gtk_list_store_append( model, &iter );
3861 gtk_list_store_set( model, &iter,
3862 0, _("Spiro"),
3863 1, _("Create Spiro path"),
3864 2, INKSCAPE_ICON_PATH_MODE_SPIRO,
3865 -1 );
3867 if (!tool_is_pencil) {
3868 gtk_list_store_append( model, &iter );
3869 gtk_list_store_set( model, &iter,
3870 0, _("Zigzag"),
3871 1, _("Create a sequence of straight line segments"),
3872 2, INKSCAPE_ICON_PATH_MODE_POLYLINE,
3873 -1 );
3875 gtk_list_store_append( model, &iter );
3876 gtk_list_store_set( model, &iter,
3877 0, _("Paraxial"),
3878 1, _("Create a sequence of paraxial line segments"),
3879 2, INKSCAPE_ICON_PATH_MODE_POLYLINE_PARAXIAL,
3880 -1 );
3881 }
3883 EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3884 "FreehandModeActionPencil" :
3885 "FreehandModeActionPen",
3886 (_("Mode:")), (_("Mode of new lines drawn by this tool")), NULL, GTK_TREE_MODEL(model) );
3887 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3889 ege_select_one_action_set_appearance( act, "full" );
3890 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3891 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3892 ege_select_one_action_set_icon_column( act, 2 );
3893 ege_select_one_action_set_icon_size( act, secondarySize );
3894 ege_select_one_action_set_tooltip_column( act, 1 );
3896 ege_select_one_action_set_active( act, freehandMode);
3897 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3898 }
3899 }
3900 }
3902 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3903 gint shape = ege_select_one_action_get_active( act );
3904 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3905 prefs->setInt(freehand_tool_name(dataKludge) + "/shape", shape);
3906 }
3908 /**
3909 * \brief Generate the list of freehand advanced shape option entries.
3910 */
3911 GList * freehand_shape_dropdown_items_list() {
3912 GList *glist = NULL;
3914 glist = g_list_append (glist, _("None"));
3915 glist = g_list_append (glist, _("Triangle in"));
3916 glist = g_list_append (glist, _("Triangle out"));
3917 glist = g_list_append (glist, _("Ellipse"));
3918 glist = g_list_append (glist, _("From clipboard"));
3920 return glist;
3921 }
3923 static void
3924 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3925 /*advanced shape options */
3926 {
3927 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3928 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3930 GList* items = 0;
3931 gint count = 0;
3932 for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3933 {
3934 GtkTreeIter iter;
3935 gtk_list_store_append( model, &iter );
3936 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3937 count++;
3938 }
3939 g_list_free( items );
3940 items = 0;
3941 EgeSelectOneAction* act1 = ege_select_one_action_new(
3942 tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3943 _("Shape:"), (_("Shape of new paths drawn by this tool")), NULL, GTK_TREE_MODEL(model));
3944 g_object_set( act1, "short_label", _("Shape:"), NULL );
3945 ege_select_one_action_set_appearance( act1, "compact" );
3946 ege_select_one_action_set_active( act1, prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/shape" : "/tools/freehand/pen/shape" ), 0) );
3947 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3948 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3949 g_object_set_data( holder, "shape_action", act1 );
3950 }
3951 }
3953 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3954 {
3955 sp_add_freehand_mode_toggle(mainActions, holder, false);
3956 freehand_add_advanced_shape_options(mainActions, holder, false);
3957 }
3960 static void
3961 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3962 {
3963 GtkWidget *tbl = GTK_WIDGET(obj);
3965 GtkAdjustment *adj;
3967 // fixme: make settable
3968 gdouble tolerance = 4;
3970 adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3971 gtk_adjustment_set_value(adj, tolerance);
3972 gtk_adjustment_value_changed(adj);
3974 spinbutton_defocus(GTK_OBJECT(tbl));
3975 }
3977 static void
3978 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3979 {
3980 // quit if run by the attr_changed listener
3981 if (g_object_get_data( tbl, "freeze" )) {
3982 return;
3983 }
3984 // in turn, prevent listener from responding
3985 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3986 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3987 prefs->setDouble("/tools/freehand/pencil/tolerance", adj->value);
3988 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3989 }
3991 class PencilToleranceObserver : public Inkscape::Preferences::Observer {
3992 public:
3993 PencilToleranceObserver(Glib::ustring const &path, GObject *x) : Observer(path), _obj(x)
3994 {
3995 g_object_set_data(_obj, "prefobserver", this);
3996 }
3997 virtual ~PencilToleranceObserver() {
3998 if (g_object_get_data(_obj, "prefobserver") == this) {
3999 g_object_set_data(_obj, "prefobserver", NULL);
4000 }
4001 }
4002 virtual void notify(Inkscape::Preferences::Entry const &val) {
4003 GObject* tbl = _obj;
4004 if (g_object_get_data( tbl, "freeze" )) {
4005 return;
4006 }
4007 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4009 GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl, "tolerance");
4011 double v = val.getDouble(adj->value);
4012 gtk_adjustment_set_value(adj, v);
4013 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4014 }
4015 private:
4016 GObject *_obj;
4017 };
4020 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4021 {
4022 sp_add_freehand_mode_toggle(mainActions, holder, true);
4024 EgeAdjustmentAction* eact = 0;
4026 /* Tolerance */
4027 {
4028 gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
4029 gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
4030 eact = create_adjustment_action( "PencilToleranceAction",
4031 _("Smoothing:"), _("Smoothing: "),
4032 _("How much smoothing (simplifying) is applied to the line"),
4033 "/tools/freehand/pencil/tolerance",
4034 3.0,
4035 GTK_WIDGET(desktop->canvas), NULL,
4036 holder, TRUE, "altx-pencil",
4037 1, 100.0, 0.5, 1.0,
4038 labels, values, G_N_ELEMENTS(labels),
4039 sp_pencil_tb_tolerance_value_changed,
4040 1, 2);
4041 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4042 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4044 PencilToleranceObserver *obs =
4045 new PencilToleranceObserver("/tools/freehand/pencil/tolerance", G_OBJECT(holder));
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_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4412 {
4413 //Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4414 //prefs->setDouble( "/tools/spray/force", adj->value * 0.01 );
4415 }
4417 static void sp_spray_mean_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4418 {
4419 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4420 prefs->setDouble( "/tools/spray/mean", adj->value );
4421 }
4423 static void sp_spray_standard_deviation_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4424 {
4425 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4426 prefs->setDouble( "/tools/spray/standard_deviation", adj->value );
4427 }
4429 static void sp_spray_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4430 {
4431 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4432 prefs->setBool("/tools/spray/usepressure", gtk_toggle_action_get_active(act));
4433 }
4435 static void sp_spray_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4436 {
4437 int mode = ege_select_one_action_get_active( act );
4438 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4439 prefs->setInt("/tools/spray/mode", mode);
4440 }
4442 static void sp_spray_population_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4443 {
4444 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4445 prefs->setDouble( "/tools/spray/population", adj->value );
4446 }
4448 /*static void spray_toggle_doh (GtkToggleAction *act, gpointer ) {
4449 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4450 prefs->setBool("/tools/spray/doh", gtk_toggle_action_get_active(act));
4451 }
4452 static void spray_toggle_dos (GtkToggleAction *act, gpointer ) {
4453 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4454 prefs->setBool("/tools/spray/dos", gtk_toggle_action_get_active(act));
4455 }
4456 static void spray_toggle_dol (GtkToggleAction *act, gpointer ) {
4457 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4458 prefs->setBool("/tools/spray/dol", gtk_toggle_action_get_active(act));
4459 }
4460 static void spray_toggle_doo (GtkToggleAction *act, gpointer ) {
4461 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4462 prefs->setBool("/tools/spray/doo", gtk_toggle_action_get_active(act));
4463 }
4464 */
4465 static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4466 {
4467 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
4468 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4470 {
4471 /* Width */
4472 gchar const* labels[] = {_("(narrow spray)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad spray)")};
4473 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4474 EgeAdjustmentAction *eact = create_adjustment_action( "SprayWidthAction",
4475 _("Width"), _("Width:"), _("The width of the spray area (relative to the visible canvas area)"),
4476 "/tools/spray/width", 15,
4477 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spray",
4478 1, 100, 1.0, 10.0,
4479 labels, values, G_N_ELEMENTS(labels),
4480 sp_spray_width_value_changed, 1, 0 );
4481 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4482 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4483 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4484 }
4486 {
4487 /* Mean */
4488 gchar const* labels[] = {_("(minimum mean)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum mean)")};
4489 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4490 EgeAdjustmentAction *eact = create_adjustment_action( "SprayMeanAction",
4491 _("Mean"), _("Mean:"), _("The mean of the spray action"),
4492 "/tools/spray/mean", 20,
4493 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-mean",
4494 1, 100, 1.0, 10.0,
4495 labels, values, G_N_ELEMENTS(labels),
4496 sp_spray_mean_value_changed, 1, 0 );
4497 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4498 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4499 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4500 }
4502 {
4503 /* Standard_deviation */
4504 gchar const* labels[] = {_("(minimum standard_deviation)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum standard_deviation)")};
4505 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4506 EgeAdjustmentAction *eact = create_adjustment_action( "SprayStandard_deviationAction",
4507 _("SD"), _("SD:"), _("The standard deviation of the spray action"),
4508 "/tools/spray/standard_deviation", 20,
4509 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-standard_deviation",
4510 1, 100, 1.0, 10.0,
4511 labels, values, G_N_ELEMENTS(labels),
4512 sp_spray_standard_deviation_value_changed, 1, 0 );
4513 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4514 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4515 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4516 }
4518 /* Mode */
4519 {
4520 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4522 GtkTreeIter iter;
4523 gtk_list_store_append( model, &iter );
4524 gtk_list_store_set( model, &iter,
4525 0, _("Spray with copies"),
4526 1, _("Spray copies of the initial selection"),
4527 2, INKSCAPE_ICON_SPRAY_COPY_MODE,
4528 -1 );
4530 gtk_list_store_append( model, &iter );
4531 gtk_list_store_set( model, &iter,
4532 0, _("Spray with clones"),
4533 1, _("Spray clones of the initial selection"),
4534 2, INKSCAPE_ICON_SPRAY_CLONE_MODE,
4535 -1 );
4537 gtk_list_store_append( model, &iter );
4538 gtk_list_store_set( model, &iter,
4539 0, _("Spray single path"),
4540 1, _("Spray objects in a single path"),
4541 2, INKSCAPE_ICON_SPRAY_UNION_MODE,
4542 -1 );
4544 EgeSelectOneAction* act = ege_select_one_action_new( "SprayModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4545 g_object_set( act, "short_label", _("Mode:"), NULL );
4546 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4547 g_object_set_data( holder, "mode_action", act );
4549 ege_select_one_action_set_appearance( act, "full" );
4550 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4551 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4552 ege_select_one_action_set_icon_column( act, 2 );
4553 ege_select_one_action_set_icon_size( act, secondarySize );
4554 ege_select_one_action_set_tooltip_column( act, 1 );
4556 gint mode = prefs->getInt("/tools/spray/mode", 0);
4557 ege_select_one_action_set_active( act, mode );
4558 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_spray_mode_changed), holder );
4560 g_object_set_data( G_OBJECT(holder), "spray_tool_mode", act);
4561 }
4563 { /* Population */
4564 gchar const* labels[] = {_("(low population)"), 0, 0, _("(default)"), 0, 0, _("(high population)")};
4565 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4566 EgeAdjustmentAction *eact = create_adjustment_action( "SprayPopulationAction",
4567 _("Population"), _("Population:"),
4568 _("This setting adjusts the number of items sprayed"),
4569 "/tools/spray/population", 50,
4570 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-population",
4571 1, 100, 1.0, 10.0,
4572 labels, values, G_N_ELEMENTS(labels),
4573 sp_spray_population_value_changed, 1, 0 );
4574 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4575 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4576 g_object_set_data( holder, "spray_population", eact );
4577 }
4579 /* Use Pressure button */
4580 {
4581 InkToggleAction* act = ink_toggle_action_new( "SprayPressureAction",
4582 _("Pressure"),
4583 _("Use the pressure of the input device to alter the force of spray action"),
4584 "use_pressure",
4585 Inkscape::ICON_SIZE_DECORATION );
4586 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4587 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_spray_pressure_state_changed), NULL);
4588 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/spray/usepressure", true) );
4589 }
4590 }
4593 //########################
4594 //## Calligraphy ##
4595 //########################
4596 static void update_presets_list (GObject *tbl)
4597 {
4598 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4599 if (g_object_get_data(tbl, "presets_blocked"))
4600 return;
4602 EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4603 if (!sel) {
4604 // WTF!? This will cause a segfault if ever reached
4605 //ege_select_one_action_set_active(sel, 0);
4606 return;
4607 }
4609 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4611 int ege_index = 1;
4612 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++ege_index) {
4613 bool match = true;
4615 std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(*i);
4616 for (std::vector<Inkscape::Preferences::Entry>::iterator j = preset.begin(); j != preset.end(); ++j) {
4617 Glib::ustring entry_name = j->getEntryName();
4618 if (entry_name == "id" || entry_name == "name") continue;
4620 void *widget = g_object_get_data(tbl, entry_name.data());
4621 if (widget) {
4622 if (GTK_IS_ADJUSTMENT(widget)) {
4623 double v = j->getDouble();
4624 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4625 //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
4626 if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
4627 match = false;
4628 break;
4629 }
4630 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4631 bool v = j->getBool();
4632 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4633 //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
4634 if ( static_cast<bool>(gtk_toggle_action_get_active(toggle)) != v ) {
4635 match = false;
4636 break;
4637 }
4638 }
4639 }
4640 }
4642 if (match) {
4643 // newly added item is at the same index as the
4644 // save command, so we need to change twice for it to take effect
4645 ege_select_one_action_set_active(sel, 0);
4646 ege_select_one_action_set_active(sel, ege_index); // one-based index
4647 return;
4648 }
4649 }
4651 // no match found
4652 ege_select_one_action_set_active(sel, 0);
4653 }
4655 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
4656 {
4657 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4658 prefs->setDouble( "/tools/calligraphic/mass", adj->value );
4659 update_presets_list(tbl);
4660 }
4662 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
4663 {
4664 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4665 prefs->setDouble( "/tools/calligraphic/wiggle", adj->value );
4666 update_presets_list(tbl);
4667 }
4669 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
4670 {
4671 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4672 prefs->setDouble( "/tools/calligraphic/angle", adj->value );
4673 update_presets_list(tbl);
4674 }
4676 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
4677 {
4678 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4679 prefs->setDouble( "/tools/calligraphic/width", adj->value );
4680 update_presets_list(tbl);
4681 }
4683 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
4684 {
4685 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4686 prefs->setDouble("/tools/calligraphic/thinning", adj->value );
4687 update_presets_list(tbl);
4688 }
4690 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
4691 {
4692 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4693 prefs->setDouble( "/tools/calligraphic/flatness", adj->value );
4694 update_presets_list(tbl);
4695 }
4697 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
4698 {
4699 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4700 prefs->setDouble( "/tools/calligraphic/tremor", adj->value );
4701 update_presets_list(tbl);
4702 }
4704 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
4705 {
4706 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4707 prefs->setDouble( "/tools/calligraphic/cap_rounding", adj->value );
4708 update_presets_list(tbl);
4709 }
4711 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject* tbl )
4712 {
4713 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4714 prefs->setBool("/tools/calligraphic/usepressure", gtk_toggle_action_get_active( act ));
4715 update_presets_list(tbl);
4716 }
4718 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject* tbl )
4719 {
4720 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4721 prefs->setBool("/tools/calligraphic/tracebackground", gtk_toggle_action_get_active( act ));
4722 update_presets_list(tbl);
4723 }
4725 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject* tbl )
4726 {
4727 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4728 GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
4729 prefs->setBool("/tools/calligraphic/usetilt", gtk_toggle_action_get_active( act ));
4730 update_presets_list(tbl);
4731 if (calligraphy_angle )
4732 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
4733 }
4736 static gchar const *const widget_names[] = {
4737 "width",
4738 "mass",
4739 "wiggle",
4740 "angle",
4741 "thinning",
4742 "tremor",
4743 "flatness",
4744 "cap_rounding",
4745 "usepressure",
4746 "tracebackground",
4747 "usetilt"
4748 };
4751 static void sp_dcc_build_presets_list(GObject *tbl)
4752 {
4753 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4755 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4756 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4757 gtk_list_store_clear (model);
4759 {
4760 GtkTreeIter iter;
4761 gtk_list_store_append( model, &iter );
4762 gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4763 }
4765 // iterate over all presets to populate the list
4766 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4767 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4768 int ii=1;
4770 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i) {
4771 GtkTreeIter iter;
4772 Glib::ustring preset_name = prefs->getString(*i + "/name");
4773 gtk_list_store_append( model, &iter );
4774 gtk_list_store_set( model, &iter, 0, _(preset_name.data()), 1, ii++, -1 );
4775 }
4777 {
4778 GtkTreeIter iter;
4779 gtk_list_store_append( model, &iter );
4780 gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4781 g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4782 }
4784 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4786 update_presets_list (tbl);
4787 }
4789 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4790 {
4791 using Inkscape::UI::Dialog::CalligraphicProfileRename;
4792 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4793 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4794 if (! desktop) return;
4796 if (g_object_get_data(tbl, "presets_blocked"))
4797 return;
4799 CalligraphicProfileRename::show(desktop);
4800 if ( !CalligraphicProfileRename::applied()) {
4801 // dialog cancelled
4802 update_presets_list (tbl);
4803 return;
4804 }
4805 Glib::ustring profile_name = CalligraphicProfileRename::getProfileName();
4807 if (profile_name.empty()) {
4808 // empty name entered
4809 update_presets_list (tbl);
4810 return;
4811 }
4813 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4815 // If there's a preset with the given name, find it and set save_path appropriately
4816 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4817 int total_presets = presets.size();
4818 int new_index = -1;
4819 Glib::ustring save_path; // profile pref path without a trailing slash
4821 int temp_index = 0;
4822 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++temp_index) {
4823 Glib::ustring name = prefs->getString(*i + "/name");
4824 if (!name.empty() && profile_name == name) {
4825 new_index = temp_index;
4826 save_path = *i;
4827 break;
4828 }
4829 }
4831 if (new_index == -1) {
4832 // no preset with this name, create
4833 new_index = total_presets + 1;
4834 gchar *profile_id = g_strdup_printf("/dcc%d", new_index);
4835 save_path = Glib::ustring("/tools/calligraphic/preset") + profile_id;
4836 g_free(profile_id);
4837 }
4839 for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4840 gchar const *const widget_name = widget_names[i];
4841 void *widget = g_object_get_data(tbl, widget_name);
4842 if (widget) {
4843 if (GTK_IS_ADJUSTMENT(widget)) {
4844 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4845 prefs->setDouble(save_path + "/" + widget_name, gtk_adjustment_get_value(adj));
4846 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4847 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4848 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4849 prefs->setBool(save_path + "/" + widget_name, gtk_toggle_action_get_active(toggle));
4850 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4851 } else {
4852 g_warning("Unknown widget type for preset: %s\n", widget_name);
4853 }
4854 } else {
4855 g_warning("Bad key when writing preset: %s\n", widget_name);
4856 }
4857 }
4858 prefs->setString(save_path + "/name", profile_name);
4860 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4861 sp_dcc_build_presets_list (tbl);
4862 }
4865 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4867 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4869 gint preset_index = ege_select_one_action_get_active( act );
4870 // This is necessary because EgeSelectOneAction spams us with GObject "changed" signal calls
4871 // even when the preset is not changed. It would be good to replace it with something more
4872 // modern. Index 0 means "No preset", so we don't do anything.
4873 if (preset_index == 0) return;
4875 gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4877 if (preset_index == save_presets_index) {
4878 // this is the Save command
4879 sp_dcc_save_profile(NULL, tbl);
4880 return;
4881 }
4883 if (g_object_get_data(tbl, "presets_blocked"))
4884 return;
4886 // preset_index is one-based so we subtract 1
4887 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4888 Glib::ustring preset_path = presets.at(preset_index - 1);
4890 if (!preset_path.empty()) {
4891 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
4893 std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(preset_path);
4895 // Shouldn't this be std::map?
4896 for (std::vector<Inkscape::Preferences::Entry>::iterator i = preset.begin(); i != preset.end(); ++i) {
4897 Glib::ustring entry_name = i->getEntryName();
4898 if (entry_name == "id" || entry_name == "name") continue;
4899 void *widget = g_object_get_data(tbl, entry_name.data());
4900 if (widget) {
4901 if (GTK_IS_ADJUSTMENT(widget)) {
4902 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4903 gtk_adjustment_set_value(adj, i->getDouble());
4904 //std::cout << "set adj " << attr_name << " to " << v << "\n";
4905 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4906 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4907 gtk_toggle_action_set_active(toggle, i->getBool());
4908 //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4909 } else {
4910 g_warning("Unknown widget type for preset: %s\n", entry_name.data());
4911 }
4912 } else {
4913 g_warning("Bad key found in a preset record: %s\n", entry_name.data());
4914 }
4915 }
4916 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4917 }
4918 }
4921 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4922 {
4923 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4924 {
4925 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4927 EgeAdjustmentAction* calligraphy_angle = 0;
4929 {
4930 /* Width */
4931 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4932 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4933 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4934 _("Pen Width"), _("Width:"),
4935 _("The width of the calligraphic pen (relative to the visible canvas area)"),
4936 "/tools/calligraphic/width", 15,
4937 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4938 1, 100, 1.0, 10.0,
4939 labels, values, G_N_ELEMENTS(labels),
4940 sp_ddc_width_value_changed, 1, 0 );
4941 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4942 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4943 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4944 }
4946 {
4947 /* Thinning */
4948 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4949 gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4950 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4951 _("Stroke Thinning"), _("Thinning:"),
4952 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4953 "/tools/calligraphic/thinning", 10,
4954 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4955 -100, 100, 1, 10.0,
4956 labels, values, G_N_ELEMENTS(labels),
4957 sp_ddc_velthin_value_changed, 1, 0);
4958 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4959 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4960 }
4962 {
4963 /* Angle */
4964 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4965 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4966 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4967 _("Pen Angle"), _("Angle:"),
4968 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4969 "/tools/calligraphic/angle", 30,
4970 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4971 -90.0, 90.0, 1.0, 10.0,
4972 labels, values, G_N_ELEMENTS(labels),
4973 sp_ddc_angle_value_changed, 1, 0 );
4974 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4975 g_object_set_data( holder, "angle_action", eact );
4976 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4977 calligraphy_angle = eact;
4978 }
4980 {
4981 /* Fixation */
4982 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4983 gdouble values[] = {0, 20, 40, 60, 90, 100};
4984 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4985 _("Fixation"), _("Fixation:"),
4986 _("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"),
4987 "/tools/calligraphic/flatness", 90,
4988 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4989 0.0, 100, 1.0, 10.0,
4990 labels, values, G_N_ELEMENTS(labels),
4991 sp_ddc_flatness_value_changed, 1, 0);
4992 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4993 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4994 }
4996 {
4997 /* Cap Rounding */
4998 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4999 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
5000 // TRANSLATORS: "cap" means "end" (both start and finish) here
5001 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
5002 _("Cap rounding"), _("Caps:"),
5003 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
5004 "/tools/calligraphic/cap_rounding", 0.0,
5005 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5006 0.0, 5.0, 0.01, 0.1,
5007 labels, values, G_N_ELEMENTS(labels),
5008 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
5009 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5010 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5011 }
5013 {
5014 /* Tremor */
5015 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
5016 gdouble values[] = {0, 10, 20, 40, 60, 100};
5017 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
5018 _("Stroke Tremor"), _("Tremor:"),
5019 _("Increase to make strokes rugged and trembling"),
5020 "/tools/calligraphic/tremor", 0.0,
5021 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5022 0.0, 100, 1, 10.0,
5023 labels, values, G_N_ELEMENTS(labels),
5024 sp_ddc_tremor_value_changed, 1, 0);
5026 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5027 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5028 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5029 }
5031 {
5032 /* Wiggle */
5033 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
5034 gdouble values[] = {0, 20, 40, 60, 100};
5035 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
5036 _("Pen Wiggle"), _("Wiggle:"),
5037 _("Increase to make the pen waver and wiggle"),
5038 "/tools/calligraphic/wiggle", 0.0,
5039 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5040 0.0, 100, 1, 10.0,
5041 labels, values, G_N_ELEMENTS(labels),
5042 sp_ddc_wiggle_value_changed, 1, 0);
5043 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5044 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5045 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5046 }
5048 {
5049 /* Mass */
5050 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
5051 gdouble values[] = {0.0, 2, 10, 20, 50, 100};
5052 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
5053 _("Pen Mass"), _("Mass:"),
5054 _("Increase to make the pen drag behind, as if slowed by inertia"),
5055 "/tools/calligraphic/mass", 2.0,
5056 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5057 0.0, 100, 1, 10.0,
5058 labels, values, G_N_ELEMENTS(labels),
5059 sp_ddc_mass_value_changed, 1, 0);
5060 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5061 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5062 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5063 }
5066 /* Trace Background button */
5067 {
5068 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
5069 _("Trace Background"),
5070 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
5071 INKSCAPE_ICON_DRAW_TRACE_BACKGROUND,
5072 Inkscape::ICON_SIZE_DECORATION );
5073 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5074 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
5075 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/tracebackground", false) );
5076 g_object_set_data( holder, "tracebackground", act );
5077 }
5079 /* Use Pressure button */
5080 {
5081 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
5082 _("Pressure"),
5083 _("Use the pressure of the input device to alter the width of the pen"),
5084 INKSCAPE_ICON_DRAW_USE_PRESSURE,
5085 Inkscape::ICON_SIZE_DECORATION );
5086 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5087 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
5088 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usepressure", true) );
5089 g_object_set_data( holder, "usepressure", act );
5090 }
5092 /* Use Tilt button */
5093 {
5094 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
5095 _("Tilt"),
5096 _("Use the tilt of the input device to alter the angle of the pen's nib"),
5097 INKSCAPE_ICON_DRAW_USE_TILT,
5098 Inkscape::ICON_SIZE_DECORATION );
5099 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5100 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
5101 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs->getBool("/tools/calligraphic/usetilt", true) );
5102 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usetilt", true) );
5103 g_object_set_data( holder, "usetilt", act );
5104 }
5106 /*calligraphic profile */
5107 {
5108 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5109 EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
5110 ege_select_one_action_set_appearance (act1, "compact");
5111 g_object_set_data (holder, "profile_selector", act1 );
5113 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
5115 sp_dcc_build_presets_list (holder);
5117 g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
5118 gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
5119 }
5120 }
5121 }
5124 //########################
5125 //## Circle / Arc ##
5126 //########################
5128 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
5129 {
5130 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
5131 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
5133 if (v1 == 0 && v2 == 0) {
5134 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
5135 gtk_action_set_sensitive( ocb, FALSE );
5136 gtk_action_set_sensitive( make_whole, FALSE );
5137 }
5138 } else {
5139 gtk_action_set_sensitive( ocb, TRUE );
5140 gtk_action_set_sensitive( make_whole, TRUE );
5141 }
5142 }
5144 static void
5145 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
5146 {
5147 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5149 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5150 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5151 prefs->setDouble(Glib::ustring("/tools/shapes/arc/") + value_name, adj->value);
5152 }
5154 // quit if run by the attr_changed listener
5155 if (g_object_get_data( tbl, "freeze" )) {
5156 return;
5157 }
5159 // in turn, prevent listener from responding
5160 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5162 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
5164 bool modmade = false;
5165 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5166 items != NULL;
5167 items = items->next)
5168 {
5169 SPItem *item = SP_ITEM(items->data);
5171 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
5173 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
5174 SPArc *arc = SP_ARC(item);
5176 if (!strcmp(value_name, "start"))
5177 ge->start = (adj->value * M_PI)/ 180;
5178 else
5179 ge->end = (adj->value * M_PI)/ 180;
5181 sp_genericellipse_normalize(ge);
5182 ((SPObject *)arc)->updateRepr();
5183 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
5185 modmade = true;
5186 }
5187 }
5189 g_free(namespaced_name);
5191 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
5193 sp_arctb_sensitivize( tbl, adj->value, other->value );
5195 if (modmade) {
5196 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
5197 _("Arc: Change start/end"));
5198 }
5200 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5201 }
5204 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
5205 {
5206 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
5207 }
5209 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
5210 {
5211 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
5212 }
5215 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
5216 {
5217 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5218 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5219 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5220 prefs->setBool("/tools/shapes/arc/open", ege_select_one_action_get_active(act) != 0);
5221 }
5223 // quit if run by the attr_changed listener
5224 if (g_object_get_data( tbl, "freeze" )) {
5225 return;
5226 }
5228 // in turn, prevent listener from responding
5229 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5231 bool modmade = false;
5233 if ( ege_select_one_action_get_active(act) != 0 ) {
5234 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5235 items != NULL;
5236 items = items->next)
5237 {
5238 if (SP_IS_ARC((SPItem *) items->data)) {
5239 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5240 repr->setAttribute("sodipodi:open", "true");
5241 SP_OBJECT((SPItem *) items->data)->updateRepr();
5242 modmade = true;
5243 }
5244 }
5245 } else {
5246 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5247 items != NULL;
5248 items = items->next)
5249 {
5250 if (SP_IS_ARC((SPItem *) items->data)) {
5251 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5252 repr->setAttribute("sodipodi:open", NULL);
5253 SP_OBJECT((SPItem *) items->data)->updateRepr();
5254 modmade = true;
5255 }
5256 }
5257 }
5259 if (modmade) {
5260 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
5261 _("Arc: Change open/closed"));
5262 }
5264 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5265 }
5267 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
5268 {
5269 GtkAdjustment *adj;
5270 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
5271 gtk_adjustment_set_value(adj, 0.0);
5272 gtk_adjustment_value_changed(adj);
5274 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
5275 gtk_adjustment_set_value(adj, 0.0);
5276 gtk_adjustment_value_changed(adj);
5278 spinbutton_defocus( GTK_OBJECT(obj) );
5279 }
5281 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
5282 gchar const */*old_value*/, gchar const */*new_value*/,
5283 bool /*is_interactive*/, gpointer data)
5284 {
5285 GObject *tbl = G_OBJECT(data);
5287 // quit if run by the _changed callbacks
5288 if (g_object_get_data( tbl, "freeze" )) {
5289 return;
5290 }
5292 // in turn, prevent callbacks from responding
5293 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5295 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
5296 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
5298 GtkAdjustment *adj1,*adj2;
5299 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
5300 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
5301 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
5302 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
5304 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
5306 char const *openstr = NULL;
5307 openstr = repr->attribute("sodipodi:open");
5308 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
5310 if (openstr) {
5311 ege_select_one_action_set_active( ocb, 1 );
5312 } else {
5313 ege_select_one_action_set_active( ocb, 0 );
5314 }
5316 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5317 }
5319 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
5320 NULL, /* child_added */
5321 NULL, /* child_removed */
5322 arc_tb_event_attr_changed,
5323 NULL, /* content_changed */
5324 NULL /* order_changed */
5325 };
5328 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
5329 {
5330 int n_selected = 0;
5331 Inkscape::XML::Node *repr = NULL;
5333 purge_repr_listener( tbl, tbl );
5335 for (GSList const *items = selection->itemList();
5336 items != NULL;
5337 items = items->next)
5338 {
5339 if (SP_IS_ARC((SPItem *) items->data)) {
5340 n_selected++;
5341 repr = SP_OBJECT_REPR((SPItem *) items->data);
5342 }
5343 }
5345 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
5347 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
5348 if (n_selected == 0) {
5349 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
5350 } else if (n_selected == 1) {
5351 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
5352 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5354 if (repr) {
5355 g_object_set_data( tbl, "repr", repr );
5356 Inkscape::GC::anchor(repr);
5357 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
5358 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
5359 }
5360 } else {
5361 // FIXME: implement averaging of all parameters for multiple selected
5362 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
5363 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5364 sp_arctb_sensitivize( tbl, 1, 0 );
5365 }
5366 }
5369 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5370 {
5371 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5373 EgeAdjustmentAction* eact = 0;
5374 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
5377 {
5378 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
5379 ege_output_action_set_use_markup( act, TRUE );
5380 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5381 g_object_set_data( holder, "mode_action", act );
5382 }
5384 /* Start */
5385 {
5386 eact = create_adjustment_action( "ArcStartAction",
5387 _("Start"), _("Start:"),
5388 _("The angle (in degrees) from the horizontal to the arc's start point"),
5389 "/tools/shapes/arc/start", 0.0,
5390 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
5391 -360.0, 360.0, 1.0, 10.0,
5392 0, 0, 0,
5393 sp_arctb_start_value_changed);
5394 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5395 }
5397 /* End */
5398 {
5399 eact = create_adjustment_action( "ArcEndAction",
5400 _("End"), _("End:"),
5401 _("The angle (in degrees) from the horizontal to the arc's end point"),
5402 "/tools/shapes/arc/end", 0.0,
5403 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
5404 -360.0, 360.0, 1.0, 10.0,
5405 0, 0, 0,
5406 sp_arctb_end_value_changed);
5407 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5408 }
5410 /* Segments / Pie checkbox */
5411 {
5412 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5414 GtkTreeIter iter;
5415 gtk_list_store_append( model, &iter );
5416 gtk_list_store_set( model, &iter,
5417 0, _("Closed arc"),
5418 1, _("Switch to segment (closed shape with two radii)"),
5419 2, INKSCAPE_ICON_DRAW_ELLIPSE_SEGMENT,
5420 -1 );
5422 gtk_list_store_append( model, &iter );
5423 gtk_list_store_set( model, &iter,
5424 0, _("Open Arc"),
5425 1, _("Switch to arc (unclosed shape)"),
5426 2, INKSCAPE_ICON_DRAW_ELLIPSE_ARC,
5427 -1 );
5429 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5430 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5431 g_object_set_data( holder, "open_action", act );
5433 ege_select_one_action_set_appearance( act, "full" );
5434 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5435 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5436 ege_select_one_action_set_icon_column( act, 2 );
5437 ege_select_one_action_set_icon_size( act, secondarySize );
5438 ege_select_one_action_set_tooltip_column( act, 1 );
5440 bool isClosed = !prefs->getBool("/tools/shapes/arc/open", false);
5441 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
5442 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
5443 }
5445 /* Make Whole */
5446 {
5447 InkAction* inky = ink_action_new( "ArcResetAction",
5448 _("Make whole"),
5449 _("Make the shape a whole ellipse, not arc or segment"),
5450 INKSCAPE_ICON_DRAW_ELLIPSE_WHOLE,
5451 secondarySize );
5452 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
5453 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5454 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
5455 g_object_set_data( holder, "make_whole", inky );
5456 }
5458 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
5459 // sensitivize make whole and open checkbox
5460 {
5461 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
5462 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
5463 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
5464 }
5467 sigc::connection *connection = new sigc::connection(
5468 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
5469 );
5470 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
5471 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
5472 }
5477 // toggle button callbacks and updaters
5479 //########################
5480 //## Dropper ##
5481 //########################
5483 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
5484 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5485 prefs->setInt( "/tools/dropper/pick", gtk_toggle_action_get_active( act ) );
5486 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
5487 if ( set_action ) {
5488 if ( gtk_toggle_action_get_active( act ) ) {
5489 gtk_action_set_sensitive( set_action, TRUE );
5490 } else {
5491 gtk_action_set_sensitive( set_action, FALSE );
5492 }
5493 }
5495 spinbutton_defocus(GTK_OBJECT(tbl));
5496 }
5498 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
5499 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5500 prefs->setBool( "/tools/dropper/setalpha", gtk_toggle_action_get_active( act ) );
5501 spinbutton_defocus(GTK_OBJECT(tbl));
5502 }
5505 /**
5506 * Dropper auxiliary toolbar construction and setup.
5507 *
5508 * TODO: Would like to add swatch of current color.
5509 * TODO: Add queue of last 5 or so colors selected with new swatches so that
5510 * can drag and drop places. Will provide a nice mixing palette.
5511 */
5512 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
5513 {
5514 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5515 gint pickAlpha = prefs->getInt( "/tools/dropper/pick", 1 );
5517 {
5518 EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
5519 ege_output_action_set_use_markup( act, TRUE );
5520 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5521 }
5523 {
5524 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
5525 _("Pick opacity"),
5526 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
5527 NULL,
5528 Inkscape::ICON_SIZE_DECORATION );
5529 g_object_set( act, "short_label", _("Pick"), NULL );
5530 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5531 g_object_set_data( holder, "pick_action", act );
5532 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
5533 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
5534 }
5536 {
5537 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
5538 _("Assign opacity"),
5539 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
5540 NULL,
5541 Inkscape::ICON_SIZE_DECORATION );
5542 g_object_set( act, "short_label", _("Assign"), NULL );
5543 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5544 g_object_set_data( holder, "set_action", act );
5545 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/dropper/setalpha", true) );
5546 // make sure it's disabled if we're not picking alpha
5547 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
5548 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
5549 }
5550 }
5553 //########################
5554 //## LPETool ##
5555 //########################
5557 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
5559 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
5560 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
5561 {
5562 using namespace Inkscape::LivePathEffect;
5564 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
5565 SPEventContext *ec = desktop->event_context;
5566 if (!SP_IS_LPETOOL_CONTEXT(ec)) {
5567 return;
5568 }
5570 // only take action if run by the attr_changed listener
5571 if (!g_object_get_data(tbl, "freeze")) {
5572 // in turn, prevent listener from responding
5573 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5575 gint mode = ege_select_one_action_get_active(act);
5576 EffectType type = lpesubtools[mode].type;
5578 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5579 bool success = lpetool_try_construction(lc, type);
5580 if (success) {
5581 // since the construction was already performed, we set the state back to inactive
5582 ege_select_one_action_set_active(act, 0);
5583 mode = 0;
5584 } else {
5585 // switch to the chosen subtool
5586 SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
5587 }
5589 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5590 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5591 prefs->setInt( "/tools/lpetool/mode", mode );
5592 }
5594 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
5595 }
5596 }
5598 void sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject */*tbl*/)
5599 {
5600 SPEventContext *ec = selection->desktop()->event_context;
5601 if (!SP_IS_LPETOOL_CONTEXT(ec))
5602 return;
5604 lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
5605 }
5607 void
5608 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
5609 {
5610 using namespace Inkscape::LivePathEffect;
5611 SPEventContext *ec = selection->desktop()->event_context;
5612 if (!SP_IS_LPETOOL_CONTEXT(ec))
5613 return;
5614 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
5616 lpetool_delete_measuring_items(lc);
5617 lpetool_create_measuring_items(lc, selection);
5619 // activate line segment combo box if a single item with LPELineSegment is selected
5620 GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
5621 SPItem *item = selection->singleItem();
5622 if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
5623 SPLPEItem *lpeitem = SP_LPE_ITEM(item);
5624 Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
5625 if (lpe && lpe->effectType() == LINE_SEGMENT) {
5626 LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
5627 g_object_set_data(tbl, "currentlpe", lpe);
5628 g_object_set_data(tbl, "currentlpeitem", lpeitem);
5629 gtk_action_set_sensitive(w, TRUE);
5630 ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
5631 } else {
5632 g_object_set_data(tbl, "currentlpe", NULL);
5633 g_object_set_data(tbl, "currentlpeitem", NULL);
5634 gtk_action_set_sensitive(w, FALSE);
5635 }
5636 } else {
5637 g_object_set_data(tbl, "currentlpe", NULL);
5638 g_object_set_data(tbl, "currentlpeitem", NULL);
5639 gtk_action_set_sensitive(w, FALSE);
5640 }
5641 }
5643 static void
5644 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
5645 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5646 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5648 bool show = gtk_toggle_action_get_active( act );
5649 prefs->setBool("/tools/lpetool/show_bbox", show);
5651 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5652 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5653 lpetool_context_reset_limiting_bbox(lc);
5654 }
5655 }
5657 static void
5658 lpetool_toggle_show_measuring_info (GtkToggleAction *act, GObject *tbl) {
5659 SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
5660 if (!tools_isactive(desktop, TOOLS_LPETOOL))
5661 return;
5663 GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
5664 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5665 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5666 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5667 bool show = gtk_toggle_action_get_active( act );
5668 prefs->setBool("/tools/lpetool/show_measuring_info", show);
5669 lpetool_show_measuring_info(lc, show);
5670 gtk_action_set_sensitive(GTK_ACTION(unitact), show);
5671 }
5672 }
5674 static void lpetool_unit_changed(GtkAction* /*act*/, GObject* tbl) {
5675 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
5676 SPUnit const *unit = tracker->getActiveUnit();
5677 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5678 prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5680 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5681 if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
5682 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5683 lpetool_delete_measuring_items(lc);
5684 lpetool_create_measuring_items(lc);
5685 }
5686 }
5688 static void
5689 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
5690 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5691 Inkscape::Selection *selection = desktop->selection;
5693 Geom::OptRect bbox = selection->bounds();
5695 if (bbox) {
5696 Geom::Point A(bbox->min());
5697 Geom::Point B(bbox->max());
5699 A *= desktop->doc2dt();
5700 B *= desktop->doc2dt();
5702 // TODO: should we provide a way to store points in prefs?
5703 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5704 prefs->setDouble("/tools/lpetool/bbox_upperleftx", A[Geom::X]);
5705 prefs->setDouble("/tools/lpetool/bbox_upperlefty", A[Geom::Y]);
5706 prefs->setDouble("/tools/lpetool/bbox_lowerrightx", B[Geom::X]);
5707 prefs->setDouble("/tools/lpetool/bbox_lowerrighty", B[Geom::Y]);
5709 lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
5710 }
5712 gtk_toggle_action_set_active(act, false);
5713 }
5715 static void
5716 sp_line_segment_build_list(GObject *tbl)
5717 {
5718 g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
5720 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
5721 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
5722 gtk_list_store_clear (model);
5724 // TODO: we add the entries of rht combo box manually; later this should be done automatically
5725 {
5726 GtkTreeIter iter;
5727 gtk_list_store_append( model, &iter );
5728 gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
5729 gtk_list_store_append( model, &iter );
5730 gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
5731 gtk_list_store_append( model, &iter );
5732 gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
5733 gtk_list_store_append( model, &iter );
5734 gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
5735 }
5737 g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5738 }
5740 static void
5741 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
5742 using namespace Inkscape::LivePathEffect;
5744 // quit if run by the attr_changed listener
5745 if (g_object_get_data(tbl, "freeze")) {
5746 return;
5747 }
5749 // in turn, prevent listener from responding
5750 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5752 LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
5753 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5754 if (lpeitem) {
5755 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5756 lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
5757 sp_lpe_item_update_patheffect(lpeitem, true, true);
5758 }
5760 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5761 }
5763 static void
5764 lpetool_open_lpe_dialog (GtkToggleAction *act, gpointer data) {
5765 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5767 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5768 sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5769 }
5770 gtk_toggle_action_set_active(act, false);
5771 }
5773 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5774 {
5775 UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5776 tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5777 g_object_set_data(holder, "tracker", tracker);
5778 SPUnit const *unit = tracker->getActiveUnit();
5780 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5781 prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5783 /** Automatically create a list of LPEs that get added to the toolbar **/
5784 {
5785 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5787 GtkTreeIter iter;
5789 // the first toggle button represents the state that no subtool is active (remove this when
5790 // this can be modeled by EgeSelectOneAction or some other action)
5791 gtk_list_store_append( model, &iter );
5792 gtk_list_store_set( model, &iter,
5793 0, _("All inactive"),
5794 1, _("No geometric tool is active"),
5795 2, "draw-geometry-inactive",
5796 -1 );
5798 Inkscape::LivePathEffect::EffectType type;
5799 for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5800 type = lpesubtools[i].type;
5801 gtk_list_store_append( model, &iter );
5802 gtk_list_store_set( model, &iter,
5803 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5804 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5805 2, lpesubtools[i].icon_name,
5806 -1 );
5807 }
5809 EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5810 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5811 g_object_set_data( holder, "lpetool_mode_action", act );
5813 ege_select_one_action_set_appearance( act, "full" );
5814 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5815 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5816 ege_select_one_action_set_icon_column( act, 2 );
5817 ege_select_one_action_set_tooltip_column( act, 1 );
5819 gint lpeToolMode = prefs->getInt("/tools/lpetool/mode", 0);
5820 ege_select_one_action_set_active( act, lpeToolMode );
5821 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5822 }
5824 /* Show limiting bounding box */
5825 {
5826 InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5827 _("Show limiting bounding box"),
5828 _("Show bounding box (used to cut infinite lines)"),
5829 "show-bounding-box",
5830 Inkscape::ICON_SIZE_DECORATION );
5831 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5832 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5833 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_bbox", true ) );
5834 }
5836 /* Set limiting bounding box to bbox of current selection */
5837 {
5838 InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5839 _("Get limiting bounding box from selection"),
5840 _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5841 "draw-geometry-set-bounding-box",
5842 Inkscape::ICON_SIZE_DECORATION );
5843 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5844 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5845 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5846 }
5849 /* Combo box to choose line segment type */
5850 {
5851 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5852 EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5853 ege_select_one_action_set_appearance (act, "compact");
5854 g_object_set_data (holder, "lpetool_line_segment_action", act );
5856 g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5858 sp_line_segment_build_list (holder);
5860 g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5861 gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5862 gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5863 }
5865 /* Display measuring info for selected items */
5866 {
5867 InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5868 _("Display measuring info"),
5869 _("Display measuring info for selected items"),
5870 "draw-geometry-show-measuring-info",
5871 Inkscape::ICON_SIZE_DECORATION );
5872 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5873 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
5874 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_measuring_info", true ) );
5875 }
5877 // add the units menu
5878 {
5879 GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5880 gtk_action_group_add_action( mainActions, act );
5881 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5882 g_object_set_data(holder, "lpetool_units_action", act);
5883 gtk_action_set_sensitive(act, prefs->getBool("/tools/lpetool/show_measuring_info", true));
5884 }
5886 /* Open LPE dialog (to adapt parameters numerically) */
5887 {
5888 InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
5889 _("Open LPE dialog"),
5890 _("Open LPE dialog (to adapt parameters numerically)"),
5891 "dialog-geometry",
5892 Inkscape::ICON_SIZE_DECORATION );
5893 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5894 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
5895 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5896 }
5898 //watch selection
5899 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5901 sigc::connection *c_selection_modified =
5902 new sigc::connection (sp_desktop_selection (desktop)->connectModified
5903 (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
5904 pool->add_connection ("selection-modified", c_selection_modified);
5906 sigc::connection *c_selection_changed =
5907 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5908 (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5909 pool->add_connection ("selection-changed", c_selection_changed);
5910 }
5912 //########################
5913 //## Eraser ##
5914 //########################
5916 static void sp_erc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
5917 {
5918 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5919 prefs->setDouble( "/tools/eraser/width", adj->value );
5920 update_presets_list(tbl);
5921 }
5923 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5924 {
5925 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5926 bool eraserMode = ege_select_one_action_get_active( act ) != 0;
5927 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5928 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5929 prefs->setBool( "/tools/eraser/mode", eraserMode );
5930 }
5932 // only take action if run by the attr_changed listener
5933 if (!g_object_get_data( tbl, "freeze" )) {
5934 // in turn, prevent listener from responding
5935 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5937 if ( eraserMode != 0 ) {
5938 } else {
5939 }
5940 // TODO finish implementation
5942 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5943 }
5944 }
5946 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5947 {
5948 {
5949 /* Width */
5950 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5951 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5952 EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5953 _("Pen Width"), _("Width:"),
5954 _("The width of the eraser pen (relative to the visible canvas area)"),
5955 "/tools/eraser/width", 15,
5956 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5957 1, 100, 1.0, 10.0,
5958 labels, values, G_N_ELEMENTS(labels),
5959 sp_erc_width_value_changed, 1, 0);
5960 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5961 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5962 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5963 }
5965 {
5966 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5968 GtkTreeIter iter;
5969 gtk_list_store_append( model, &iter );
5970 gtk_list_store_set( model, &iter,
5971 0, _("Delete"),
5972 1, _("Delete objects touched by the eraser"),
5973 2, INKSCAPE_ICON_DRAW_ERASER_DELETE_OBJECTS,
5974 -1 );
5976 gtk_list_store_append( model, &iter );
5977 gtk_list_store_set( model, &iter,
5978 0, _("Cut"),
5979 1, _("Cut out from objects"),
5980 2, INKSCAPE_ICON_PATH_DIFFERENCE,
5981 -1 );
5983 EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5984 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5985 g_object_set_data( holder, "eraser_mode_action", act );
5987 ege_select_one_action_set_appearance( act, "full" );
5988 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5989 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5990 ege_select_one_action_set_icon_column( act, 2 );
5991 ege_select_one_action_set_tooltip_column( act, 1 );
5993 /// @todo Convert to boolean?
5994 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5995 gint eraserMode = prefs->getBool("/tools/eraser/mode") ? 1 : 0;
5996 ege_select_one_action_set_active( act, eraserMode );
5997 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
5998 }
6000 }
6002 //########################
6003 //## Text Toolbox ##
6004 //########################
6005 /*
6006 static void
6007 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
6008 {
6009 //Call back for letter sizing spinbutton
6010 }
6012 static void
6013 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
6014 {
6015 //Call back for line height spinbutton
6016 }
6018 static void
6019 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
6020 {
6021 //Call back for horizontal kerning spinbutton
6022 }
6024 static void
6025 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
6026 {
6027 //Call back for vertical kerning spinbutton
6028 }
6030 static void
6031 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
6032 {
6033 //Call back for letter rotation spinbutton
6034 }*/
6036 namespace {
6038 void
6039 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
6040 {
6041 // quit if run by the _changed callbacks
6042 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6043 return;
6044 }
6046 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6048 SPStyle *query =
6049 sp_style_new (SP_ACTIVE_DOCUMENT);
6051 int result_family =
6052 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6054 int result_style =
6055 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6057 int result_numbers =
6058 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6060 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6062 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6063 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
6064 // there are no texts in selection, read from prefs
6066 sp_style_read_from_prefs(query, "/tools/text");
6068 if (g_object_get_data(tbl, "text_style_from_prefs")) {
6069 // do not reset the toolbar style from prefs if we already did it last time
6070 sp_style_unref(query);
6071 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6072 return;
6073 }
6074 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
6075 } else {
6076 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
6077 }
6079 if (query->text)
6080 {
6081 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
6082 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6083 gtk_entry_set_text (GTK_ENTRY (entry), "");
6085 } else if (query->text->font_specification.value || query->text->font_family.value) {
6087 Gtk::ComboBoxEntry *combo = (Gtk::ComboBoxEntry *) (g_object_get_data (G_OBJECT (tbl), "family-entry-combo"));
6088 GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6090 // Get the font that corresponds
6091 Glib::ustring familyName;
6093 font_instance * font = font_factory::Default()->FaceFromStyle(query);
6094 if (font) {
6095 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
6096 font->Unref();
6097 font = NULL;
6098 }
6100 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
6102 Gtk::TreeIter iter;
6103 try {
6104 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
6105 Glib::RefPtr<Gtk::TreeModel> model = combo->get_model();
6106 iter = model->get_iter(path);
6107 } catch (...) {
6108 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
6109 sp_style_unref(query);
6110 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6111 return;
6112 }
6114 combo->set_active (iter);
6115 }
6117 //Size
6118 {
6119 GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
6120 gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
6121 gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
6122 g_free(str);
6123 }
6125 //Anchor
6126 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
6127 {
6128 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
6129 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6130 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6131 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6132 }
6133 else
6134 {
6135 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
6136 {
6137 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
6138 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6139 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6140 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6141 }
6142 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
6143 {
6144 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
6145 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6146 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6147 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6148 }
6149 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
6150 {
6151 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
6152 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6153 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6154 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6155 }
6156 }
6158 //Style
6159 {
6160 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
6162 gboolean active = gtk_toggle_button_get_active (button);
6163 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));
6165 if (active != check)
6166 {
6167 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6168 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6169 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6170 }
6171 }
6173 {
6174 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
6176 gboolean active = gtk_toggle_button_get_active (button);
6177 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
6179 if (active != check)
6180 {
6181 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6182 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6183 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6184 }
6185 }
6187 //Orientation
6188 //locking both buttons, changing one affect all group (both)
6189 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
6190 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6192 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
6193 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
6195 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
6196 {
6197 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6198 }
6199 else
6200 {
6201 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
6202 }
6203 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6204 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
6205 }
6207 sp_style_unref(query);
6209 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6210 }
6212 void
6213 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
6214 {
6215 sp_text_toolbox_selection_changed (selection, tbl);
6216 }
6218 void
6219 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
6220 {
6221 sp_text_toolbox_selection_changed (NULL, tbl);
6222 }
6224 void
6225 sp_text_toolbox_family_changed (GtkComboBoxEntry *,
6226 GObject *tbl)
6227 {
6228 // quit if run by the _changed callbacks
6229 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6230 return;
6231 }
6233 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6235 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6236 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6237 const gchar* family = gtk_entry_get_text (GTK_ENTRY (entry));
6239 //g_print ("family changed to: %s\n", family);
6241 SPStyle *query =
6242 sp_style_new (SP_ACTIVE_DOCUMENT);
6244 int result_fontspec =
6245 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6247 SPCSSAttr *css = sp_repr_css_attr_new ();
6249 // First try to get the font spec from the stored value
6250 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
6252 if (fontSpec.empty()) {
6253 // Construct a new font specification if it does not yet exist
6254 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6255 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6256 fontFromStyle->Unref();
6257 }
6259 if (!fontSpec.empty()) {
6261 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
6263 if (!newFontSpec.empty()) {
6265 if (fontSpec != newFontSpec) {
6267 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
6269 if (font) {
6270 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6272 // Set all the these just in case they were altered when finding the best
6273 // match for the new family and old style...
6275 gchar c[256];
6277 font->Family(c, 256);
6279 sp_repr_css_set_property (css, "font-family", c);
6281 font->Attribute( "weight", c, 256);
6282 sp_repr_css_set_property (css, "font-weight", c);
6284 font->Attribute("style", c, 256);
6285 sp_repr_css_set_property (css, "font-style", c);
6287 font->Attribute("stretch", c, 256);
6288 sp_repr_css_set_property (css, "font-stretch", c);
6290 font->Attribute("variant", c, 256);
6291 sp_repr_css_set_property (css, "font-variant", c);
6293 font->Unref();
6294 }
6295 }
6297 } else {
6298 // If the old font on selection (or default) was not existing on the system,
6299 // ReplaceFontSpecificationFamily does not work. In that case we fall back to blindly
6300 // setting the family reported by the family chooser.
6302 //g_print ("fallback setting family: %s\n", family);
6303 sp_repr_css_set_property (css, "-inkscape-font-specification", family);
6304 sp_repr_css_set_property (css, "font-family", family);
6305 }
6306 }
6308 // If querying returned nothing, set the default style of the tool (for new texts)
6309 if (result_fontspec == QUERY_STYLE_NOTHING)
6310 {
6311 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6312 prefs->mergeStyle("/tools/text/style", css);
6313 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
6314 }
6315 else
6316 {
6317 sp_desktop_set_style (desktop, css, true, true);
6318 }
6320 sp_style_unref(query);
6322 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6323 _("Text: Change font family"));
6324 sp_repr_css_attr_unref (css);
6326 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6328 // unfreeze
6329 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6331 // focus to canvas
6332 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6333 }
6336 void
6337 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
6338 gpointer data)
6339 {
6340 if (g_object_get_data (G_OBJECT (button), "block")) return;
6341 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
6342 int prop = GPOINTER_TO_INT(data);
6344 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6345 SPCSSAttr *css = sp_repr_css_attr_new ();
6347 switch (prop)
6348 {
6349 case 0:
6350 {
6351 sp_repr_css_set_property (css, "text-anchor", "start");
6352 sp_repr_css_set_property (css, "text-align", "start");
6353 break;
6354 }
6355 case 1:
6356 {
6357 sp_repr_css_set_property (css, "text-anchor", "middle");
6358 sp_repr_css_set_property (css, "text-align", "center");
6359 break;
6360 }
6362 case 2:
6363 {
6364 sp_repr_css_set_property (css, "text-anchor", "end");
6365 sp_repr_css_set_property (css, "text-align", "end");
6366 break;
6367 }
6369 case 3:
6370 {
6371 sp_repr_css_set_property (css, "text-anchor", "start");
6372 sp_repr_css_set_property (css, "text-align", "justify");
6373 break;
6374 }
6375 }
6377 SPStyle *query =
6378 sp_style_new (SP_ACTIVE_DOCUMENT);
6379 int result_numbers =
6380 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6382 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6383 if (result_numbers == QUERY_STYLE_NOTHING)
6384 {
6385 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6386 prefs->mergeStyle("/tools/text/style", css);
6387 }
6389 sp_style_unref(query);
6391 sp_desktop_set_style (desktop, css, true, true);
6392 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6393 _("Text: Change alignment"));
6394 sp_repr_css_attr_unref (css);
6396 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6397 }
6399 void
6400 sp_text_toolbox_style_toggled (GtkToggleButton *button,
6401 gpointer data)
6402 {
6403 if (g_object_get_data (G_OBJECT (button), "block")) return;
6405 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6406 SPCSSAttr *css = sp_repr_css_attr_new ();
6407 int prop = GPOINTER_TO_INT(data);
6408 bool active = gtk_toggle_button_get_active (button);
6410 SPStyle *query =
6411 sp_style_new (SP_ACTIVE_DOCUMENT);
6413 int result_fontspec =
6414 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6416 //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6417 //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6418 //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6420 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
6421 Glib::ustring newFontSpec = "";
6423 if (fontSpec.empty()) {
6424 // Construct a new font specification if it does not yet exist
6425 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6426 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6427 fontFromStyle->Unref();
6428 }
6430 bool nochange = true;
6431 switch (prop)
6432 {
6433 case 0:
6434 {
6435 if (!fontSpec.empty()) {
6436 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
6437 if (!newFontSpec.empty()) {
6438 // Don't even set the bold if the font didn't exist on the system
6439 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
6440 nochange = false;
6441 }
6442 }
6443 // set or reset the button according
6444 if(nochange) {
6445 gboolean check = gtk_toggle_button_get_active (button);
6447 if (active != check)
6448 {
6449 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6450 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6451 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6452 }
6453 }
6455 break;
6456 }
6458 case 1:
6459 {
6460 if (!fontSpec.empty()) {
6461 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
6462 if (!newFontSpec.empty()) {
6463 // Don't even set the italic if the font didn't exist on the system
6464 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
6465 nochange = false;
6466 }
6467 }
6468 if(nochange) {
6469 gboolean check = gtk_toggle_button_get_active (button);
6471 if (active != check)
6472 {
6473 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6474 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6475 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6476 }
6477 }
6478 break;
6479 }
6480 }
6482 if (!newFontSpec.empty()) {
6483 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6484 }
6486 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6487 if (result_fontspec == QUERY_STYLE_NOTHING)
6488 {
6489 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6490 prefs->mergeStyle("/tools/text/style", css);
6491 }
6493 sp_style_unref(query);
6495 sp_desktop_set_style (desktop, css, true, true);
6496 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6497 _("Text: Change font style"));
6498 sp_repr_css_attr_unref (css);
6500 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6501 }
6503 void
6504 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
6505 gpointer data)
6506 {
6507 if (g_object_get_data (G_OBJECT (button), "block")) {
6508 return;
6509 }
6511 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6512 SPCSSAttr *css = sp_repr_css_attr_new ();
6513 int prop = GPOINTER_TO_INT(data);
6515 switch (prop)
6516 {
6517 case 0:
6518 {
6519 sp_repr_css_set_property (css, "writing-mode", "lr");
6520 break;
6521 }
6523 case 1:
6524 {
6525 sp_repr_css_set_property (css, "writing-mode", "tb");
6526 break;
6527 }
6528 }
6530 SPStyle *query =
6531 sp_style_new (SP_ACTIVE_DOCUMENT);
6532 int result_numbers =
6533 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6535 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6536 if (result_numbers == QUERY_STYLE_NOTHING)
6537 {
6538 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6539 prefs->mergeStyle("/tools/text/style", css);
6540 }
6542 sp_desktop_set_style (desktop, css, true, true);
6543 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6544 _("Text: Change orientation"));
6545 sp_repr_css_attr_unref (css);
6547 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6548 }
6550 gboolean
6551 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6552 {
6553 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6554 if (!desktop) return FALSE;
6556 switch (get_group0_keyval (event)) {
6557 case GDK_KP_Enter: // chosen
6558 case GDK_Return:
6559 // unfreeze and update, which will defocus
6560 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6561 sp_text_toolbox_family_changed (NULL, tbl);
6562 return TRUE; // I consumed the event
6563 break;
6564 case GDK_Escape:
6565 // defocus
6566 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6567 return TRUE; // I consumed the event
6568 break;
6569 }
6570 return FALSE;
6571 }
6573 gboolean
6574 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
6575 {
6576 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6577 if (!desktop) return FALSE;
6579 switch (get_group0_keyval (event)) {
6580 case GDK_KP_Enter:
6581 case GDK_Return:
6582 case GDK_Escape: // defocus
6583 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6584 return TRUE; // I consumed the event
6585 break;
6586 case GDK_w:
6587 case GDK_W:
6588 if (event->state & GDK_CONTROL_MASK) {
6589 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6590 return TRUE; // I consumed the event
6591 }
6592 break;
6593 }
6594 return FALSE;
6595 }
6598 void
6599 sp_text_toolbox_size_changed (GtkComboBox *cbox,
6600 GObject *tbl)
6601 {
6602 // quit if run by the _changed callbacks
6603 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6604 return;
6605 }
6607 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6609 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6611 // If this is not from selecting a size in the list (in which case get_active will give the
6612 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
6613 // process this event. This fixes GTK's stupid insistence on sending an activate change every
6614 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
6615 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed")) {
6616 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6617 return;
6618 }
6620 gdouble value = -1;
6621 {
6622 gchar *endptr;
6623 gchar *const text = gtk_combo_box_get_active_text(cbox);
6624 if (text) {
6625 value = g_strtod(text, &endptr);
6626 if (endptr == text) { // Conversion failed, non-numeric input.
6627 value = -1;
6628 }
6629 g_free(text);
6630 }
6631 }
6632 if (value <= 0) {
6633 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6634 return; // could not parse value
6635 }
6637 SPCSSAttr *css = sp_repr_css_attr_new ();
6638 Inkscape::CSSOStringStream osfs;
6639 osfs << value;
6640 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
6642 SPStyle *query =
6643 sp_style_new (SP_ACTIVE_DOCUMENT);
6644 int result_numbers =
6645 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6647 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6648 if (result_numbers == QUERY_STYLE_NOTHING)
6649 {
6650 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6651 prefs->mergeStyle("/tools/text/style", css);
6652 }
6654 sp_style_unref(query);
6656 sp_desktop_set_style (desktop, css, true, true);
6657 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
6658 _("Text: Change font size"));
6659 sp_repr_css_attr_unref (css);
6661 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6663 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6664 }
6666 gboolean
6667 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
6668 {
6669 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6670 if (!desktop) return FALSE;
6672 if (!g_object_get_data (tbl, "esc-pressed")) {
6673 g_object_set_data (tbl, "enter-pressed", gpointer(1));
6674 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6675 sp_text_toolbox_size_changed (cbox, tbl);
6676 g_object_set_data (tbl, "enter-pressed", gpointer(0));
6677 }
6678 return FALSE; // I consumed the event
6679 }
6682 gboolean
6683 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6684 {
6685 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6686 if (!desktop) return FALSE;
6688 switch (get_group0_keyval (event)) {
6689 case GDK_Escape: // defocus
6690 g_object_set_data (tbl, "esc-pressed", gpointer(1));
6691 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6692 g_object_set_data (tbl, "esc-pressed", gpointer(0));
6693 return TRUE; // I consumed the event
6694 break;
6695 case GDK_Return: // defocus
6696 case GDK_KP_Enter:
6697 g_object_set_data (tbl, "enter-pressed", gpointer(1));
6698 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6699 sp_text_toolbox_size_changed (cbox, tbl);
6700 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6701 g_object_set_data (tbl, "enter-pressed", gpointer(0));
6702 return TRUE; // I consumed the event
6703 break;
6704 }
6705 return FALSE;
6706 }
6708 // While editing font name in the entry, disable family_changed by freezing, otherwise completion
6709 // does not work!
6710 gboolean
6711 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
6712 GdkEventFocus */*event*/,
6713 GObject *tbl)
6714 {
6715 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6716 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1); // select all
6717 return FALSE;
6718 }
6720 gboolean
6721 sp_text_toolbox_entry_focus_out (GtkWidget *entry,
6722 GdkEventFocus */*event*/,
6723 GObject *tbl)
6724 {
6725 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6726 gtk_entry_select_region (GTK_ENTRY (entry), 0, 0); // deselect
6727 return FALSE;
6728 }
6730 void
6731 cell_data_func (GtkCellLayout */*cell_layout*/,
6732 GtkCellRenderer *cell,
6733 GtkTreeModel *tree_model,
6734 GtkTreeIter *iter,
6735 gpointer /*data*/)
6736 {
6737 gchar *family;
6738 gtk_tree_model_get(tree_model, iter, 0, &family, -1);
6739 gchar *const family_escaped = g_markup_escape_text(family, -1);
6741 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6742 int show_sample = prefs->getInt("/tools/text/show_sample_in_list", 1);
6743 if (show_sample) {
6745 Glib::ustring sample = prefs->getString("/tools/text/font_sample");
6746 gchar *const sample_escaped = g_markup_escape_text(sample.data(), -1);
6748 std::stringstream markup;
6749 markup << family_escaped << " <span foreground='darkgray' font_family='"
6750 << family_escaped << "'>" << sample_escaped << "</span>";
6751 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6753 g_free(sample_escaped);
6754 } else {
6755 g_object_set (G_OBJECT (cell), "markup", family_escaped, NULL);
6756 }
6758 g_free(family);
6759 g_free(family_escaped);
6760 }
6762 gboolean text_toolbox_completion_match_selected (GtkEntryCompletion *widget,
6763 GtkTreeModel *model,
6764 GtkTreeIter *iter,
6765 GObject *tbl)
6766 {
6767 // We intercept this signal so as to fire family_changed at once (without it, you'd have to
6768 // press Enter again after choosing a completion)
6769 gchar *family;
6770 gtk_tree_model_get(model, iter, 0, &family, -1);
6772 GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6773 gtk_entry_set_text (GTK_ENTRY (entry), family);
6775 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6776 sp_text_toolbox_family_changed (NULL, tbl);
6777 return TRUE;
6778 }
6781 static void
6782 cbe_add_completion (GtkComboBoxEntry *cbe, GObject *tbl){
6783 GtkEntry *entry;
6784 GtkEntryCompletion *completion;
6785 GtkTreeModel *model;
6787 entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(cbe)));
6788 completion = gtk_entry_completion_new();
6789 model = gtk_combo_box_get_model(GTK_COMBO_BOX(cbe));
6790 gtk_entry_completion_set_model(completion, model);
6791 gtk_entry_completion_set_text_column(completion, 0);
6792 gtk_entry_completion_set_inline_completion(completion, FALSE);
6793 gtk_entry_completion_set_inline_selection(completion, FALSE);
6794 gtk_entry_completion_set_popup_completion(completion, TRUE);
6795 gtk_entry_set_completion(entry, completion);
6797 g_signal_connect (G_OBJECT (completion), "match-selected", G_CALLBACK (text_toolbox_completion_match_selected), tbl);
6799 g_object_unref(completion);
6800 }
6802 void sp_text_toolbox_family_popnotify (GtkComboBox *widget,
6803 void *property,
6804 GObject *tbl)
6805 {
6806 // while the drop-down is open, we disable font family changing, reenabling it only when it closes
6808 gboolean shown;
6809 g_object_get (G_OBJECT(widget), "popup-shown", &shown, NULL);
6810 if (shown) {
6811 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6812 //g_print("POP: notify: SHOWN\n");
6813 } else {
6814 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6816 // stupid GTK doesn't let us attach to events in the drop-down window, so we peek here to
6817 // find out if the drop down was closed by Enter and if so, manually update (only
6818 // necessary on Windows, on Linux it updates itself - what a mess, but we'll manage)
6819 GdkEvent *ev = gtk_get_current_event();
6820 if (ev) {
6821 //g_print ("ev type: %d\n", ev->type);
6822 if (ev->type == GDK_KEY_PRESS) {
6823 switch (get_group0_keyval ((GdkEventKey *) ev)) {
6824 case GDK_KP_Enter: // chosen
6825 case GDK_Return:
6826 {
6827 // make sure the chosen one is inserted into the entry
6828 GtkComboBox *combo = GTK_COMBO_BOX (((Gtk::ComboBox *) (g_object_get_data (tbl, "family-entry-combo")))->gobj());
6829 GtkTreeModel *model = gtk_combo_box_get_model(combo);
6830 GtkTreeIter iter;
6831 gboolean has_active = gtk_combo_box_get_active_iter (combo, &iter);
6832 if (has_active) {
6833 gchar *family;
6834 gtk_tree_model_get(model, &iter, 0, &family, -1);
6835 GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6836 gtk_entry_set_text (GTK_ENTRY (entry), family);
6837 }
6839 // update
6840 sp_text_toolbox_family_changed (NULL, tbl);
6841 break;
6842 }
6843 }
6844 }
6845 }
6847 // regardless of whether we updated, defocus the widget
6848 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6849 if (desktop)
6850 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6851 //g_print("POP: notify: HIDDEN\n");
6852 }
6853 }
6855 GtkWidget*
6856 sp_text_toolbox_new (SPDesktop *desktop)
6857 {
6858 GtkToolbar *tbl = GTK_TOOLBAR(gtk_toolbar_new());
6859 GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("/toolbox/secondary", 1));
6861 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
6862 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
6864 GtkTooltips *tt = gtk_tooltips_new();
6866 ////////////Family
6867 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
6868 Gtk::ComboBoxEntry *font_sel = Gtk::manage(new Gtk::ComboBoxEntry(store));
6870 gtk_rc_parse_string (
6871 "style \"dropdown-as-list-style\"\n"
6872 "{\n"
6873 " GtkComboBox::appears-as-list = 1\n"
6874 "}\n"
6875 "widget \"*.toolbox-fontfamily-list\" style \"dropdown-as-list-style\"");
6876 gtk_widget_set_name(GTK_WIDGET (font_sel->gobj()), "toolbox-fontfamily-list");
6877 gtk_tooltips_set_tip (tt, GTK_WIDGET (font_sel->gobj()), _("Select font family (Alt+X to access)"), "");
6879 g_signal_connect (G_OBJECT (font_sel->gobj()), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
6881 cbe_add_completion(font_sel->gobj(), G_OBJECT(tbl));
6883 gtk_toolbar_append_widget( tbl, (GtkWidget*) font_sel->gobj(), "", "");
6884 g_object_set_data (G_OBJECT (tbl), "family-entry-combo", font_sel);
6886 // expand the field a bit so as to view more of the previews in the drop-down
6887 GtkRequisition req;
6888 gtk_widget_size_request (GTK_WIDGET (font_sel->gobj()), &req);
6889 gtk_widget_set_size_request (GTK_WIDGET (font_sel->gobj()), MIN(req.width + 50, 500), -1);
6891 GtkWidget* entry = (GtkWidget*) font_sel->get_entry()->gobj();
6892 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6894 g_signal_connect (G_OBJECT (font_sel->gobj()), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6895 g_signal_connect (G_OBJECT (font_sel->gobj()), "notify::popup-shown",
6896 G_CALLBACK (sp_text_toolbox_family_popnotify), tbl);
6897 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
6898 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
6899 g_signal_connect (G_OBJECT (entry), "focus-out-event", G_CALLBACK (sp_text_toolbox_entry_focus_out), tbl);
6901 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
6902 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
6904 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
6905 gtk_cell_layout_clear( GTK_CELL_LAYOUT(font_sel->gobj()) );
6906 gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(font_sel->gobj()) , cell , TRUE );
6907 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT(font_sel->gobj()), cell, GtkCellLayoutDataFunc (cell_data_func), NULL, NULL);
6909 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
6910 GtkWidget *box = gtk_event_box_new ();
6911 gtk_container_add (GTK_CONTAINER (box), image);
6912 gtk_toolbar_append_widget( tbl, box, "", "");
6913 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
6914 gtk_tooltips_set_tip (tt, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
6915 gtk_widget_hide (GTK_WIDGET (box));
6916 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
6918 ////////////Size
6919 gchar const *const sizes[] = {
6920 "4", "6", "8", "9", "10", "11", "12", "13", "14",
6921 "16", "18", "20", "22", "24", "28",
6922 "32", "36", "40", "48", "56", "64", "72", "144"
6923 };
6925 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
6926 for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
6927 gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
6928 }
6929 gtk_widget_set_size_request (cbox, 80, -1);
6930 gtk_toolbar_append_widget( tbl, cbox, "", "");
6931 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
6932 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
6933 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
6934 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
6936 ////////////Text anchor
6937 GtkWidget *group = gtk_radio_button_new (NULL);
6938 GtkWidget *row = gtk_hbox_new (FALSE, 4);
6939 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
6941 // left
6942 GtkWidget *rbutton = group;
6943 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6944 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
6945 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6947 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6948 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
6949 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
6950 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
6952 // center
6953 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6954 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6955 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
6956 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6958 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6959 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
6960 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
6961 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
6963 // right
6964 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6965 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6966 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
6967 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6969 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6970 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
6971 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
6972 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
6974 // fill
6975 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6976 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6977 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
6978 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6980 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6981 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
6982 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
6983 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
6985 gtk_toolbar_append_widget( tbl, row, "", "");
6987 //spacer
6988 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6990 ////////////Text style
6991 row = gtk_hbox_new (FALSE, 4);
6993 // bold
6994 rbutton = gtk_toggle_button_new ();
6995 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6996 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
6997 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6998 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
7000 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7001 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
7002 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
7004 // italic
7005 rbutton = gtk_toggle_button_new ();
7006 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7007 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
7008 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7009 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
7011 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7012 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
7013 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
7015 gtk_toolbar_append_widget( tbl, row, "", "");
7017 //spacer
7018 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
7020 // Text orientation
7021 group = gtk_radio_button_new (NULL);
7022 row = gtk_hbox_new (FALSE, 4);
7023 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
7025 // horizontal
7026 rbutton = group;
7027 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7028 gtk_container_add (GTK_CONTAINER (rbutton),
7029 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_HORIZONTAL));
7030 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7031 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
7033 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7034 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
7035 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
7037 // vertical
7038 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7039 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7040 gtk_container_add (GTK_CONTAINER (rbutton),
7041 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_VERTICAL));
7042 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7043 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
7045 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7046 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
7047 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
7048 gtk_toolbar_append_widget( tbl, row, "", "" );
7051 //watch selection
7052 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
7054 sigc::connection *c_selection_changed =
7055 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
7056 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
7057 pool->add_connection ("selection-changed", c_selection_changed);
7059 sigc::connection *c_selection_modified =
7060 new sigc::connection (sp_desktop_selection (desktop)->connectModified
7061 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
7062 pool->add_connection ("selection-modified", c_selection_modified);
7064 sigc::connection *c_subselection_changed =
7065 new sigc::connection (desktop->connectToolSubselectionChanged
7066 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
7067 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
7069 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
7072 gtk_widget_show_all( GTK_WIDGET(tbl) );
7074 return GTK_WIDGET(tbl);
7075 } // end of sp_text_toolbox_new()
7077 }//<unnamed> namespace
7080 //#########################
7081 //## Connector ##
7082 //#########################
7084 static void sp_connector_mode_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7085 {
7086 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7087 prefs->setBool("/tools/connector/mode",
7088 gtk_toggle_action_get_active( act ));
7089 }
7091 static void sp_connector_path_set_avoid(void)
7092 {
7093 cc_selection_set_avoid(true);
7094 }
7097 static void sp_connector_path_set_ignore(void)
7098 {
7099 cc_selection_set_avoid(false);
7100 }
7102 static void sp_connector_orthogonal_toggled( GtkToggleAction* act, GObject *tbl )
7103 {
7104 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7105 Inkscape::Selection * selection = sp_desktop_selection(desktop);
7106 SPDocument *doc = sp_desktop_document(desktop);
7108 if (!sp_document_get_undo_sensitive(doc))
7109 {
7110 return;
7111 }
7114 // quit if run by the _changed callbacks
7115 if (g_object_get_data( tbl, "freeze" )) {
7116 return;
7117 }
7119 // in turn, prevent callbacks from responding
7120 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7122 bool is_orthog = gtk_toggle_action_get_active( act );
7123 gchar orthog_str[] = "orthogonal";
7124 gchar polyline_str[] = "polyline";
7125 gchar *value = is_orthog ? orthog_str : polyline_str ;
7127 bool modmade = false;
7128 GSList *l = (GSList *) selection->itemList();
7129 while (l) {
7130 SPItem *item = (SPItem *) l->data;
7132 if (cc_item_is_connector(item)) {
7133 sp_object_setAttribute(item, "inkscape:connector-type",
7134 value, false);
7135 item->avoidRef->handleSettingChange();
7136 modmade = true;
7137 }
7138 l = l->next;
7139 }
7141 if (!modmade)
7142 {
7143 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7144 prefs->setBool("/tools/connector/orthogonal", is_orthog);
7145 }
7147 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7148 is_orthog ? _("Set connector type: orthogonal"): _("Set connector type: polyline"));
7150 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7151 }
7153 static void connector_curvature_changed(GtkAdjustment *adj, GObject* tbl)
7154 {
7155 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7156 Inkscape::Selection * selection = sp_desktop_selection(desktop);
7157 SPDocument *doc = sp_desktop_document(desktop);
7159 if (!sp_document_get_undo_sensitive(doc))
7160 {
7161 return;
7162 }
7165 // quit if run by the _changed callbacks
7166 if (g_object_get_data( tbl, "freeze" )) {
7167 return;
7168 }
7170 // in turn, prevent callbacks from responding
7171 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7173 gdouble newValue = gtk_adjustment_get_value(adj);
7174 gchar value[G_ASCII_DTOSTR_BUF_SIZE];
7175 g_ascii_dtostr(value, G_ASCII_DTOSTR_BUF_SIZE, newValue);
7177 bool modmade = false;
7178 GSList *l = (GSList *) selection->itemList();
7179 while (l) {
7180 SPItem *item = (SPItem *) l->data;
7182 if (cc_item_is_connector(item)) {
7183 sp_object_setAttribute(item, "inkscape:connector-curvature",
7184 value, false);
7185 item->avoidRef->handleSettingChange();
7186 modmade = true;
7187 }
7188 l = l->next;
7189 }
7191 if (!modmade)
7192 {
7193 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7194 prefs->setDouble(Glib::ustring("/tools/connector/curvature"), newValue);
7195 }
7197 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7198 _("Change connector curvature"));
7200 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7201 }
7204 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
7205 {
7206 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7207 SPDocument *doc = sp_desktop_document(desktop);
7209 if (!sp_document_get_undo_sensitive(doc))
7210 {
7211 return;
7212 }
7214 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7216 if ( !repr->attribute("inkscape:connector-spacing") &&
7217 ( adj->value == defaultConnSpacing )) {
7218 // Don't need to update the repr if the attribute doesn't
7219 // exist and it is being set to the default value -- as will
7220 // happen at startup.
7221 return;
7222 }
7224 // quit if run by the attr_changed listener
7225 if (g_object_get_data( tbl, "freeze" )) {
7226 return;
7227 }
7229 // in turn, prevent listener from responding
7230 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
7232 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
7233 SP_OBJECT(desktop->namedview)->updateRepr();
7235 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
7236 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
7237 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
7238 Geom::Matrix m = Geom::identity();
7239 avoid_item_move(&m, item);
7240 }
7242 if (items) {
7243 g_slist_free(items);
7244 }
7246 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7247 _("Change connector spacing"));
7249 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7250 }
7252 static void sp_connector_graph_layout(void)
7253 {
7254 if (!SP_ACTIVE_DESKTOP) return;
7255 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7257 // hack for clones, see comment in align-and-distribute.cpp
7258 int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7259 prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7261 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
7263 prefs->setInt("/options/clonecompensation/value", saved_compensation);
7265 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
7266 }
7268 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7269 {
7270 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7271 prefs->setBool("/tools/connector/directedlayout",
7272 gtk_toggle_action_get_active( act ));
7273 }
7275 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7276 {
7277 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7278 prefs->setBool("/tools/connector/avoidoverlaplayout",
7279 gtk_toggle_action_get_active( act ));
7280 }
7283 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
7284 {
7285 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7286 prefs->setDouble("/tools/connector/length", adj->value);
7287 }
7289 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
7290 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
7291 bool /*is_interactive*/, gpointer data)
7292 {
7293 GtkWidget *tbl = GTK_WIDGET(data);
7295 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
7296 return;
7297 }
7298 if (strcmp(name, "inkscape:connector-spacing") == 0)
7299 {
7300 GtkAdjustment *adj = (GtkAdjustment*)
7301 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
7302 gdouble spacing = defaultConnSpacing;
7303 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
7305 gtk_adjustment_set_value(adj, spacing);
7306 gtk_adjustment_value_changed(adj);
7307 }
7309 spinbutton_defocus(GTK_OBJECT(tbl));
7310 }
7312 static void sp_connector_new_connection_point(GtkWidget *, GObject *tbl)
7313 {
7314 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7315 SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7317 if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE)
7318 cc_create_connection_point(cc);
7319 }
7321 static void sp_connector_remove_connection_point(GtkWidget *, GObject *tbl)
7322 {
7323 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7324 SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7326 if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE)
7327 cc_remove_connection_point(cc);
7328 }
7330 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
7331 NULL, /* child_added */
7332 NULL, /* child_removed */
7333 connector_tb_event_attr_changed,
7334 NULL, /* content_changed */
7335 NULL /* order_changed */
7336 };
7338 static void sp_connector_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
7339 {
7340 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "curvature" ) );
7341 GtkToggleAction *act = GTK_TOGGLE_ACTION( g_object_get_data( tbl, "orthogonal" ) );
7342 SPItem *item = selection->singleItem();
7343 if (SP_IS_PATH(item))
7344 {
7345 gdouble curvature = SP_PATH(item)->connEndPair.getCurvature();
7346 bool is_orthog = SP_PATH(item)->connEndPair.isOrthogonal();
7347 gtk_toggle_action_set_active(act, is_orthog);
7348 gtk_adjustment_set_value(adj, curvature);
7349 }
7351 }
7353 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
7354 {
7355 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7356 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
7358 // Editing mode toggle button
7359 {
7360 InkToggleAction* act = ink_toggle_action_new( "ConnectorEditModeAction",
7361 _("EditMode"),
7362 _("Switch between connection point editing and connector drawing mode"),
7363 INKSCAPE_ICON_CONNECTOR_EDIT,
7364 Inkscape::ICON_SIZE_DECORATION );
7365 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7367 bool tbuttonstate = prefs->getBool("/tools/connector/mode");
7368 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7369 g_object_set_data( holder, "mode", act );
7370 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_mode_toggled), holder );
7371 }
7374 {
7375 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
7376 _("Avoid"),
7377 _("Make connectors avoid selected objects"),
7378 INKSCAPE_ICON_CONNECTOR_AVOID,
7379 secondarySize );
7380 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
7381 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7382 }
7384 {
7385 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
7386 _("Ignore"),
7387 _("Make connectors ignore selected objects"),
7388 INKSCAPE_ICON_CONNECTOR_IGNORE,
7389 secondarySize );
7390 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
7391 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7392 }
7394 // Orthogonal connectors toggle button
7395 {
7396 InkToggleAction* act = ink_toggle_action_new( "ConnectorOrthogonalAction",
7397 _("Orthogonal"),
7398 _("Make connector orthogonal or polyline"),
7399 INKSCAPE_ICON_CONNECTOR_ORTHOGONAL,
7400 Inkscape::ICON_SIZE_DECORATION );
7401 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7403 bool tbuttonstate = prefs->getBool("/tools/connector/orthogonal");
7404 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7405 g_object_set_data( holder, "orthogonal", act );
7406 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_orthogonal_toggled), holder );
7407 }
7409 EgeAdjustmentAction* eact = 0;
7410 // Curvature spinbox
7411 eact = create_adjustment_action( "ConnectorCurvatureAction",
7412 _("Connector Curvature"), _("Curvature:"),
7413 _("The amount of connectors curvature"),
7414 "/tools/connector/curvature", defaultConnCurvature,
7415 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-curvature",
7416 0, 100, 1.0, 10.0,
7417 0, 0, 0,
7418 connector_curvature_changed, 1, 0 );
7419 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7421 // Spacing spinbox
7422 eact = create_adjustment_action( "ConnectorSpacingAction",
7423 _("Connector Spacing"), _("Spacing:"),
7424 _("The amount of space left around objects by auto-routing connectors"),
7425 "/tools/connector/spacing", defaultConnSpacing,
7426 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
7427 0, 100, 1.0, 10.0,
7428 0, 0, 0,
7429 connector_spacing_changed, 1, 0 );
7430 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7432 // Graph (connector network) layout
7433 {
7434 InkAction* inky = ink_action_new( "ConnectorGraphAction",
7435 _("Graph"),
7436 _("Nicely arrange selected connector network"),
7437 INKSCAPE_ICON_DISTRIBUTE_GRAPH,
7438 secondarySize );
7439 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
7440 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7441 }
7443 // Default connector length spinbox
7444 eact = create_adjustment_action( "ConnectorLengthAction",
7445 _("Connector Length"), _("Length:"),
7446 _("Ideal length for connectors when layout is applied"),
7447 "/tools/connector/length", 100,
7448 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
7449 10, 1000, 10.0, 100.0,
7450 0, 0, 0,
7451 connector_length_changed, 1, 0 );
7452 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7455 // Directed edges toggle button
7456 {
7457 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
7458 _("Downwards"),
7459 _("Make connectors with end-markers (arrows) point downwards"),
7460 INKSCAPE_ICON_DISTRIBUTE_GRAPH_DIRECTED,
7461 Inkscape::ICON_SIZE_DECORATION );
7462 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7464 bool tbuttonstate = prefs->getBool("/tools/connector/directedlayout");
7465 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7467 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
7468 sigc::connection *connection = new sigc::connection(sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_connector_toolbox_selection_changed), (GObject *)holder))
7469 );
7470 }
7472 // Avoid overlaps toggle button
7473 {
7474 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
7475 _("Remove overlaps"),
7476 _("Do not allow overlapping shapes"),
7477 INKSCAPE_ICON_DISTRIBUTE_REMOVE_OVERLAPS,
7478 Inkscape::ICON_SIZE_DECORATION );
7479 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7481 bool tbuttonstate = prefs->getBool("/tools/connector/avoidoverlaplayout");
7482 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), (tbuttonstate ? TRUE : FALSE ));
7484 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
7485 }
7488 // New connection point button
7489 {
7490 InkAction* inky = ink_action_new( "ConnectorNewConnPointAction",
7491 _("New connection point"),
7492 _("Add a new connection point to the currently selected item"),
7493 INKSCAPE_ICON_CONNECTOR_NEW_CONNPOINT,
7494 secondarySize );
7495 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_new_connection_point), holder );
7496 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7497 }
7499 // Remove selected connection point button
7501 {
7502 InkAction* inky = ink_action_new( "ConnectorRemoveConnPointAction",
7503 _("Remove connection point"),
7504 _("Remove the currently selected connection point"),
7505 INKSCAPE_ICON_CONNECTOR_REMOVE_CONNPOINT,
7506 secondarySize );
7507 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_remove_connection_point), holder );
7508 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7509 }
7512 // Code to watch for changes to the connector-spacing attribute in
7513 // the XML.
7514 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7515 g_assert(repr != NULL);
7517 purge_repr_listener( holder, holder );
7519 if (repr) {
7520 g_object_set_data( holder, "repr", repr );
7521 Inkscape::GC::anchor(repr);
7522 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
7523 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
7524 }
7525 } // end of sp_connector_toolbox_prep()
7528 //#########################
7529 //## Paintbucket ##
7530 //#########################
7532 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
7533 {
7534 gint channels = ege_select_one_action_get_active( act );
7535 flood_channels_set_channels( channels );
7536 }
7538 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
7539 {
7540 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7541 prefs->setInt("/tools/paintbucket/threshold", (gint)adj->value);
7542 }
7544 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
7545 {
7546 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7547 prefs->setBool("/tools/paintbucket/autogap", ege_select_one_action_get_active( act ));
7548 }
7550 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
7551 {
7552 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
7553 SPUnit const *unit = tracker->getActiveUnit();
7554 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7556 prefs->setDouble("/tools/paintbucket/offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
7557 prefs->setString("/tools/paintbucket/offsetunits", sp_unit_get_abbreviation(unit));
7558 }
7560 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
7561 {
7562 // FIXME: make defaults settable via Inkscape Options
7563 struct KeyValue {
7564 char const *key;
7565 double value;
7566 } const key_values[] = {
7567 {"threshold", 15},
7568 {"offset", 0.0}
7569 };
7571 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
7572 KeyValue const &kv = key_values[i];
7573 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
7574 if ( adj ) {
7575 gtk_adjustment_set_value(adj, kv.value);
7576 }
7577 }
7579 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
7580 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
7581 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
7582 ege_select_one_action_set_active( autogap_action, 0 );
7583 }
7585 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
7586 {
7587 EgeAdjustmentAction* eact = 0;
7588 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7590 {
7591 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7593 GList* items = 0;
7594 gint count = 0;
7595 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
7596 {
7597 GtkTreeIter iter;
7598 gtk_list_store_append( model, &iter );
7599 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7600 count++;
7601 }
7602 g_list_free( items );
7603 items = 0;
7604 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
7605 g_object_set( act1, "short_label", _("Fill by:"), NULL );
7606 ege_select_one_action_set_appearance( act1, "compact" );
7607 ege_select_one_action_set_active( act1, prefs->getInt("/tools/paintbucket/channels", 0) );
7608 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
7609 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
7610 g_object_set_data( holder, "channels_action", act1 );
7611 }
7613 // Spacing spinbox
7614 {
7615 eact = create_adjustment_action(
7616 "ThresholdAction",
7617 _("Fill Threshold"), _("Threshold:"),
7618 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
7619 "/tools/paintbucket/threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
7620 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
7621 0, 0, 0,
7622 paintbucket_threshold_changed, 1, 0 );
7624 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
7625 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7626 }
7628 // Create the units menu.
7629 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
7630 Glib::ustring stored_unit = prefs->getString("/tools/paintbucket/offsetunits");
7631 if (!stored_unit.empty())
7632 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit.data()));
7633 g_object_set_data( holder, "tracker", tracker );
7634 {
7635 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
7636 gtk_action_group_add_action( mainActions, act );
7637 }
7639 // Offset spinbox
7640 {
7641 eact = create_adjustment_action(
7642 "OffsetAction",
7643 _("Grow/shrink by"), _("Grow/shrink by:"),
7644 _("The amount to grow (positive) or shrink (negative) the created fill path"),
7645 "/tools/paintbucket/offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
7646 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
7647 0, 0, 0,
7648 paintbucket_offset_changed, 1, 2);
7649 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
7651 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7652 }
7654 /* Auto Gap */
7655 {
7656 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7658 GList* items = 0;
7659 gint count = 0;
7660 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
7661 {
7662 GtkTreeIter iter;
7663 gtk_list_store_append( model, &iter );
7664 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7665 count++;
7666 }
7667 g_list_free( items );
7668 items = 0;
7669 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
7670 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
7671 ege_select_one_action_set_appearance( act2, "compact" );
7672 ege_select_one_action_set_active( act2, prefs->getBool("/tools/paintbucket/autogap") );
7673 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
7674 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
7675 g_object_set_data( holder, "autogap_action", act2 );
7676 }
7678 /* Reset */
7679 {
7680 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
7681 _("Defaults"),
7682 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
7683 GTK_STOCK_CLEAR );
7684 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
7685 gtk_action_group_add_action( mainActions, act );
7686 gtk_action_set_sensitive( act, TRUE );
7687 }
7689 }
7691 /*
7692 Local Variables:
7693 mode:c++
7694 c-file-style:"stroustrup"
7695 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
7696 indent-tabs-mode:nil
7697 fill-column:99
7698 End:
7699 */
7700 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :