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 /*
4412 static void sp_spray_force_value_changed( GtkAdjustment * / *adj* /, GObject * / *tbl* / )
4413 {
4414 //Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4415 //prefs->setDouble( "/tools/spray/force", adj->value * 0.01 );
4416 }
4417 */
4419 static void sp_spray_mean_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4420 {
4421 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4422 prefs->setDouble( "/tools/spray/mean", adj->value );
4423 }
4425 static void sp_spray_standard_deviation_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4426 {
4427 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4428 prefs->setDouble( "/tools/spray/standard_deviation", adj->value );
4429 }
4431 static void sp_spray_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4432 {
4433 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4434 prefs->setBool("/tools/spray/usepressure", gtk_toggle_action_get_active(act));
4435 }
4437 static void sp_spray_mode_changed( EgeSelectOneAction *act, GObject */*tbl*/ )
4438 {
4439 int mode = ege_select_one_action_get_active( act );
4440 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4441 prefs->setInt("/tools/spray/mode", mode);
4442 }
4444 static void sp_spray_population_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4445 {
4446 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4447 prefs->setDouble( "/tools/spray/population", adj->value );
4448 }
4450 /*static void spray_toggle_doh (GtkToggleAction *act, gpointer ) {
4451 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4452 prefs->setBool("/tools/spray/doh", gtk_toggle_action_get_active(act));
4453 }
4454 static void spray_toggle_dos (GtkToggleAction *act, gpointer ) {
4455 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4456 prefs->setBool("/tools/spray/dos", gtk_toggle_action_get_active(act));
4457 }
4458 static void spray_toggle_dol (GtkToggleAction *act, gpointer ) {
4459 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4460 prefs->setBool("/tools/spray/dol", gtk_toggle_action_get_active(act));
4461 }
4462 static void spray_toggle_doo (GtkToggleAction *act, gpointer ) {
4463 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4464 prefs->setBool("/tools/spray/doo", gtk_toggle_action_get_active(act));
4465 }
4466 */
4467 static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4468 {
4469 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
4470 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4472 {
4473 /* Width */
4474 gchar const* labels[] = {_("(narrow spray)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad spray)")};
4475 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4476 EgeAdjustmentAction *eact = create_adjustment_action( "SprayWidthAction",
4477 _("Width"), _("Width:"), _("The width of the spray area (relative to the visible canvas area)"),
4478 "/tools/spray/width", 15,
4479 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spray",
4480 1, 100, 1.0, 10.0,
4481 labels, values, G_N_ELEMENTS(labels),
4482 sp_spray_width_value_changed, 1, 0 );
4483 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4484 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4485 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4486 }
4488 {
4489 /* Mean */
4490 gchar const* labels[] = {_("(minimum mean)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum mean)")};
4491 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4492 EgeAdjustmentAction *eact = create_adjustment_action( "SprayMeanAction",
4493 _("Mean"), _("Mean:"), _("The mean of the spray action"),
4494 "/tools/spray/mean", 20,
4495 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-mean",
4496 1, 100, 1.0, 10.0,
4497 labels, values, G_N_ELEMENTS(labels),
4498 sp_spray_mean_value_changed, 1, 0 );
4499 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4500 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4501 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4502 }
4504 {
4505 /* Standard_deviation */
4506 gchar const* labels[] = {_("(minimum standard_deviation)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum standard_deviation)")};
4507 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4508 EgeAdjustmentAction *eact = create_adjustment_action( "SprayStandard_deviationAction",
4509 _("SD"), _("SD:"), _("The standard deviation of the spray action"),
4510 "/tools/spray/standard_deviation", 20,
4511 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-standard_deviation",
4512 1, 100, 1.0, 10.0,
4513 labels, values, G_N_ELEMENTS(labels),
4514 sp_spray_standard_deviation_value_changed, 1, 0 );
4515 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4516 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4517 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4518 }
4520 /* Mode */
4521 {
4522 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4524 GtkTreeIter iter;
4525 gtk_list_store_append( model, &iter );
4526 gtk_list_store_set( model, &iter,
4527 0, _("Spray with copies"),
4528 1, _("Spray copies of the initial selection"),
4529 2, INKSCAPE_ICON_SPRAY_COPY_MODE,
4530 -1 );
4532 gtk_list_store_append( model, &iter );
4533 gtk_list_store_set( model, &iter,
4534 0, _("Spray with clones"),
4535 1, _("Spray clones of the initial selection"),
4536 2, INKSCAPE_ICON_SPRAY_CLONE_MODE,
4537 -1 );
4539 gtk_list_store_append( model, &iter );
4540 gtk_list_store_set( model, &iter,
4541 0, _("Spray single path"),
4542 1, _("Spray objects in a single path"),
4543 2, INKSCAPE_ICON_SPRAY_UNION_MODE,
4544 -1 );
4546 EgeSelectOneAction* act = ege_select_one_action_new( "SprayModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4547 g_object_set( act, "short_label", _("Mode:"), NULL );
4548 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4549 g_object_set_data( holder, "mode_action", act );
4551 ege_select_one_action_set_appearance( act, "full" );
4552 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4553 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4554 ege_select_one_action_set_icon_column( act, 2 );
4555 ege_select_one_action_set_icon_size( act, secondarySize );
4556 ege_select_one_action_set_tooltip_column( act, 1 );
4558 gint mode = prefs->getInt("/tools/spray/mode", 0);
4559 ege_select_one_action_set_active( act, mode );
4560 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_spray_mode_changed), holder );
4562 g_object_set_data( G_OBJECT(holder), "spray_tool_mode", act);
4563 }
4565 { /* Population */
4566 gchar const* labels[] = {_("(low population)"), 0, 0, _("(default)"), 0, 0, _("(high population)")};
4567 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4568 EgeAdjustmentAction *eact = create_adjustment_action( "SprayPopulationAction",
4569 _("Population"), _("Population:"),
4570 _("This setting adjusts the number of items sprayed"),
4571 "/tools/spray/population", 50,
4572 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-population",
4573 1, 100, 1.0, 10.0,
4574 labels, values, G_N_ELEMENTS(labels),
4575 sp_spray_population_value_changed, 1, 0 );
4576 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4577 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4578 g_object_set_data( holder, "spray_population", eact );
4579 }
4581 /* Use Pressure button */
4582 {
4583 InkToggleAction* act = ink_toggle_action_new( "SprayPressureAction",
4584 _("Pressure"),
4585 _("Use the pressure of the input device to alter the force of spray action"),
4586 "use_pressure",
4587 Inkscape::ICON_SIZE_DECORATION );
4588 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4589 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_spray_pressure_state_changed), NULL);
4590 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/spray/usepressure", true) );
4591 }
4592 }
4595 //########################
4596 //## Calligraphy ##
4597 //########################
4598 static void update_presets_list (GObject *tbl)
4599 {
4600 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4601 if (g_object_get_data(tbl, "presets_blocked"))
4602 return;
4604 EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4605 if (!sel) {
4606 // WTF!? This will cause a segfault if ever reached
4607 //ege_select_one_action_set_active(sel, 0);
4608 return;
4609 }
4611 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4613 int ege_index = 1;
4614 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++ege_index) {
4615 bool match = true;
4617 std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(*i);
4618 for (std::vector<Inkscape::Preferences::Entry>::iterator j = preset.begin(); j != preset.end(); ++j) {
4619 Glib::ustring entry_name = j->getEntryName();
4620 if (entry_name == "id" || entry_name == "name") continue;
4622 void *widget = g_object_get_data(tbl, entry_name.data());
4623 if (widget) {
4624 if (GTK_IS_ADJUSTMENT(widget)) {
4625 double v = j->getDouble();
4626 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4627 //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
4628 if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
4629 match = false;
4630 break;
4631 }
4632 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4633 bool v = j->getBool();
4634 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4635 //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
4636 if ( static_cast<bool>(gtk_toggle_action_get_active(toggle)) != v ) {
4637 match = false;
4638 break;
4639 }
4640 }
4641 }
4642 }
4644 if (match) {
4645 // newly added item is at the same index as the
4646 // save command, so we need to change twice for it to take effect
4647 ege_select_one_action_set_active(sel, 0);
4648 ege_select_one_action_set_active(sel, ege_index); // one-based index
4649 return;
4650 }
4651 }
4653 // no match found
4654 ege_select_one_action_set_active(sel, 0);
4655 }
4657 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
4658 {
4659 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4660 prefs->setDouble( "/tools/calligraphic/mass", adj->value );
4661 update_presets_list(tbl);
4662 }
4664 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
4665 {
4666 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4667 prefs->setDouble( "/tools/calligraphic/wiggle", adj->value );
4668 update_presets_list(tbl);
4669 }
4671 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
4672 {
4673 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4674 prefs->setDouble( "/tools/calligraphic/angle", adj->value );
4675 update_presets_list(tbl);
4676 }
4678 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
4679 {
4680 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4681 prefs->setDouble( "/tools/calligraphic/width", adj->value );
4682 update_presets_list(tbl);
4683 }
4685 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
4686 {
4687 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4688 prefs->setDouble("/tools/calligraphic/thinning", adj->value );
4689 update_presets_list(tbl);
4690 }
4692 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
4693 {
4694 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4695 prefs->setDouble( "/tools/calligraphic/flatness", adj->value );
4696 update_presets_list(tbl);
4697 }
4699 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
4700 {
4701 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4702 prefs->setDouble( "/tools/calligraphic/tremor", adj->value );
4703 update_presets_list(tbl);
4704 }
4706 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
4707 {
4708 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4709 prefs->setDouble( "/tools/calligraphic/cap_rounding", adj->value );
4710 update_presets_list(tbl);
4711 }
4713 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject* tbl )
4714 {
4715 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4716 prefs->setBool("/tools/calligraphic/usepressure", gtk_toggle_action_get_active( act ));
4717 update_presets_list(tbl);
4718 }
4720 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject* tbl )
4721 {
4722 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4723 prefs->setBool("/tools/calligraphic/tracebackground", gtk_toggle_action_get_active( act ));
4724 update_presets_list(tbl);
4725 }
4727 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject* tbl )
4728 {
4729 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4730 GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
4731 prefs->setBool("/tools/calligraphic/usetilt", gtk_toggle_action_get_active( act ));
4732 update_presets_list(tbl);
4733 if (calligraphy_angle )
4734 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
4735 }
4738 static gchar const *const widget_names[] = {
4739 "width",
4740 "mass",
4741 "wiggle",
4742 "angle",
4743 "thinning",
4744 "tremor",
4745 "flatness",
4746 "cap_rounding",
4747 "usepressure",
4748 "tracebackground",
4749 "usetilt"
4750 };
4753 static void sp_dcc_build_presets_list(GObject *tbl)
4754 {
4755 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4757 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4758 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4759 gtk_list_store_clear (model);
4761 {
4762 GtkTreeIter iter;
4763 gtk_list_store_append( model, &iter );
4764 gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4765 }
4767 // iterate over all presets to populate the list
4768 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4769 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4770 int ii=1;
4772 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i) {
4773 GtkTreeIter iter;
4774 Glib::ustring preset_name = prefs->getString(*i + "/name");
4775 gtk_list_store_append( model, &iter );
4776 gtk_list_store_set( model, &iter, 0, _(preset_name.data()), 1, ii++, -1 );
4777 }
4779 {
4780 GtkTreeIter iter;
4781 gtk_list_store_append( model, &iter );
4782 gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4783 g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4784 }
4786 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4788 update_presets_list (tbl);
4789 }
4791 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4792 {
4793 using Inkscape::UI::Dialog::CalligraphicProfileRename;
4794 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4795 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4796 if (! desktop) return;
4798 if (g_object_get_data(tbl, "presets_blocked"))
4799 return;
4801 CalligraphicProfileRename::show(desktop);
4802 if ( !CalligraphicProfileRename::applied()) {
4803 // dialog cancelled
4804 update_presets_list (tbl);
4805 return;
4806 }
4807 Glib::ustring profile_name = CalligraphicProfileRename::getProfileName();
4809 if (profile_name.empty()) {
4810 // empty name entered
4811 update_presets_list (tbl);
4812 return;
4813 }
4815 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4817 // If there's a preset with the given name, find it and set save_path appropriately
4818 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4819 int total_presets = presets.size();
4820 int new_index = -1;
4821 Glib::ustring save_path; // profile pref path without a trailing slash
4823 int temp_index = 0;
4824 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++temp_index) {
4825 Glib::ustring name = prefs->getString(*i + "/name");
4826 if (!name.empty() && profile_name == name) {
4827 new_index = temp_index;
4828 save_path = *i;
4829 break;
4830 }
4831 }
4833 if (new_index == -1) {
4834 // no preset with this name, create
4835 new_index = total_presets + 1;
4836 gchar *profile_id = g_strdup_printf("/dcc%d", new_index);
4837 save_path = Glib::ustring("/tools/calligraphic/preset") + profile_id;
4838 g_free(profile_id);
4839 }
4841 for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4842 gchar const *const widget_name = widget_names[i];
4843 void *widget = g_object_get_data(tbl, widget_name);
4844 if (widget) {
4845 if (GTK_IS_ADJUSTMENT(widget)) {
4846 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4847 prefs->setDouble(save_path + "/" + widget_name, gtk_adjustment_get_value(adj));
4848 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4849 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4850 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4851 prefs->setBool(save_path + "/" + widget_name, gtk_toggle_action_get_active(toggle));
4852 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4853 } else {
4854 g_warning("Unknown widget type for preset: %s\n", widget_name);
4855 }
4856 } else {
4857 g_warning("Bad key when writing preset: %s\n", widget_name);
4858 }
4859 }
4860 prefs->setString(save_path + "/name", profile_name);
4862 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4863 sp_dcc_build_presets_list (tbl);
4864 }
4867 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4869 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4871 gint preset_index = ege_select_one_action_get_active( act );
4872 // This is necessary because EgeSelectOneAction spams us with GObject "changed" signal calls
4873 // even when the preset is not changed. It would be good to replace it with something more
4874 // modern. Index 0 means "No preset", so we don't do anything.
4875 if (preset_index == 0) return;
4877 gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4879 if (preset_index == save_presets_index) {
4880 // this is the Save command
4881 sp_dcc_save_profile(NULL, tbl);
4882 return;
4883 }
4885 if (g_object_get_data(tbl, "presets_blocked"))
4886 return;
4888 // preset_index is one-based so we subtract 1
4889 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4890 Glib::ustring preset_path = presets.at(preset_index - 1);
4892 if (!preset_path.empty()) {
4893 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
4895 std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(preset_path);
4897 // Shouldn't this be std::map?
4898 for (std::vector<Inkscape::Preferences::Entry>::iterator i = preset.begin(); i != preset.end(); ++i) {
4899 Glib::ustring entry_name = i->getEntryName();
4900 if (entry_name == "id" || entry_name == "name") continue;
4901 void *widget = g_object_get_data(tbl, entry_name.data());
4902 if (widget) {
4903 if (GTK_IS_ADJUSTMENT(widget)) {
4904 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4905 gtk_adjustment_set_value(adj, i->getDouble());
4906 //std::cout << "set adj " << attr_name << " to " << v << "\n";
4907 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4908 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4909 gtk_toggle_action_set_active(toggle, i->getBool());
4910 //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4911 } else {
4912 g_warning("Unknown widget type for preset: %s\n", entry_name.data());
4913 }
4914 } else {
4915 g_warning("Bad key found in a preset record: %s\n", entry_name.data());
4916 }
4917 }
4918 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4919 }
4920 }
4923 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4924 {
4925 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4926 {
4927 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4929 EgeAdjustmentAction* calligraphy_angle = 0;
4931 {
4932 /* Width */
4933 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4934 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4935 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4936 _("Pen Width"), _("Width:"),
4937 _("The width of the calligraphic pen (relative to the visible canvas area)"),
4938 "/tools/calligraphic/width", 15,
4939 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4940 1, 100, 1.0, 10.0,
4941 labels, values, G_N_ELEMENTS(labels),
4942 sp_ddc_width_value_changed, 1, 0 );
4943 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4944 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4945 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4946 }
4948 {
4949 /* Thinning */
4950 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4951 gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4952 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4953 _("Stroke Thinning"), _("Thinning:"),
4954 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4955 "/tools/calligraphic/thinning", 10,
4956 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4957 -100, 100, 1, 10.0,
4958 labels, values, G_N_ELEMENTS(labels),
4959 sp_ddc_velthin_value_changed, 1, 0);
4960 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4961 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4962 }
4964 {
4965 /* Angle */
4966 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4967 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4968 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4969 _("Pen Angle"), _("Angle:"),
4970 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4971 "/tools/calligraphic/angle", 30,
4972 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4973 -90.0, 90.0, 1.0, 10.0,
4974 labels, values, G_N_ELEMENTS(labels),
4975 sp_ddc_angle_value_changed, 1, 0 );
4976 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4977 g_object_set_data( holder, "angle_action", eact );
4978 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4979 calligraphy_angle = eact;
4980 }
4982 {
4983 /* Fixation */
4984 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4985 gdouble values[] = {0, 20, 40, 60, 90, 100};
4986 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4987 _("Fixation"), _("Fixation:"),
4988 _("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"),
4989 "/tools/calligraphic/flatness", 90,
4990 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4991 0.0, 100, 1.0, 10.0,
4992 labels, values, G_N_ELEMENTS(labels),
4993 sp_ddc_flatness_value_changed, 1, 0);
4994 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4995 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4996 }
4998 {
4999 /* Cap Rounding */
5000 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
5001 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
5002 // TRANSLATORS: "cap" means "end" (both start and finish) here
5003 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
5004 _("Cap rounding"), _("Caps:"),
5005 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
5006 "/tools/calligraphic/cap_rounding", 0.0,
5007 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5008 0.0, 5.0, 0.01, 0.1,
5009 labels, values, G_N_ELEMENTS(labels),
5010 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
5011 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5012 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5013 }
5015 {
5016 /* Tremor */
5017 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
5018 gdouble values[] = {0, 10, 20, 40, 60, 100};
5019 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
5020 _("Stroke Tremor"), _("Tremor:"),
5021 _("Increase to make strokes rugged and trembling"),
5022 "/tools/calligraphic/tremor", 0.0,
5023 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5024 0.0, 100, 1, 10.0,
5025 labels, values, G_N_ELEMENTS(labels),
5026 sp_ddc_tremor_value_changed, 1, 0);
5028 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5029 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5030 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5031 }
5033 {
5034 /* Wiggle */
5035 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
5036 gdouble values[] = {0, 20, 40, 60, 100};
5037 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
5038 _("Pen Wiggle"), _("Wiggle:"),
5039 _("Increase to make the pen waver and wiggle"),
5040 "/tools/calligraphic/wiggle", 0.0,
5041 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5042 0.0, 100, 1, 10.0,
5043 labels, values, G_N_ELEMENTS(labels),
5044 sp_ddc_wiggle_value_changed, 1, 0);
5045 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5046 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5047 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5048 }
5050 {
5051 /* Mass */
5052 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
5053 gdouble values[] = {0.0, 2, 10, 20, 50, 100};
5054 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
5055 _("Pen Mass"), _("Mass:"),
5056 _("Increase to make the pen drag behind, as if slowed by inertia"),
5057 "/tools/calligraphic/mass", 2.0,
5058 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5059 0.0, 100, 1, 10.0,
5060 labels, values, G_N_ELEMENTS(labels),
5061 sp_ddc_mass_value_changed, 1, 0);
5062 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5063 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5064 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5065 }
5068 /* Trace Background button */
5069 {
5070 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
5071 _("Trace Background"),
5072 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
5073 INKSCAPE_ICON_DRAW_TRACE_BACKGROUND,
5074 Inkscape::ICON_SIZE_DECORATION );
5075 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5076 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
5077 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/tracebackground", false) );
5078 g_object_set_data( holder, "tracebackground", act );
5079 }
5081 /* Use Pressure button */
5082 {
5083 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
5084 _("Pressure"),
5085 _("Use the pressure of the input device to alter the width of the pen"),
5086 INKSCAPE_ICON_DRAW_USE_PRESSURE,
5087 Inkscape::ICON_SIZE_DECORATION );
5088 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5089 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
5090 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usepressure", true) );
5091 g_object_set_data( holder, "usepressure", act );
5092 }
5094 /* Use Tilt button */
5095 {
5096 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
5097 _("Tilt"),
5098 _("Use the tilt of the input device to alter the angle of the pen's nib"),
5099 INKSCAPE_ICON_DRAW_USE_TILT,
5100 Inkscape::ICON_SIZE_DECORATION );
5101 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5102 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
5103 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs->getBool("/tools/calligraphic/usetilt", true) );
5104 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usetilt", true) );
5105 g_object_set_data( holder, "usetilt", act );
5106 }
5108 /*calligraphic profile */
5109 {
5110 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5111 EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
5112 ege_select_one_action_set_appearance (act1, "compact");
5113 g_object_set_data (holder, "profile_selector", act1 );
5115 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
5117 sp_dcc_build_presets_list (holder);
5119 g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
5120 gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
5121 }
5122 }
5123 }
5126 //########################
5127 //## Circle / Arc ##
5128 //########################
5130 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
5131 {
5132 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
5133 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
5135 if (v1 == 0 && v2 == 0) {
5136 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
5137 gtk_action_set_sensitive( ocb, FALSE );
5138 gtk_action_set_sensitive( make_whole, FALSE );
5139 }
5140 } else {
5141 gtk_action_set_sensitive( ocb, TRUE );
5142 gtk_action_set_sensitive( make_whole, TRUE );
5143 }
5144 }
5146 static void
5147 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
5148 {
5149 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5151 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5152 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5153 prefs->setDouble(Glib::ustring("/tools/shapes/arc/") + value_name, adj->value);
5154 }
5156 // quit if run by the attr_changed listener
5157 if (g_object_get_data( tbl, "freeze" )) {
5158 return;
5159 }
5161 // in turn, prevent listener from responding
5162 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5164 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
5166 bool modmade = false;
5167 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5168 items != NULL;
5169 items = items->next)
5170 {
5171 SPItem *item = SP_ITEM(items->data);
5173 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
5175 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
5176 SPArc *arc = SP_ARC(item);
5178 if (!strcmp(value_name, "start"))
5179 ge->start = (adj->value * M_PI)/ 180;
5180 else
5181 ge->end = (adj->value * M_PI)/ 180;
5183 sp_genericellipse_normalize(ge);
5184 ((SPObject *)arc)->updateRepr();
5185 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
5187 modmade = true;
5188 }
5189 }
5191 g_free(namespaced_name);
5193 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
5195 sp_arctb_sensitivize( tbl, adj->value, other->value );
5197 if (modmade) {
5198 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
5199 _("Arc: Change start/end"));
5200 }
5202 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5203 }
5206 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
5207 {
5208 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
5209 }
5211 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
5212 {
5213 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
5214 }
5217 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
5218 {
5219 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5220 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5221 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5222 prefs->setBool("/tools/shapes/arc/open", ege_select_one_action_get_active(act) != 0);
5223 }
5225 // quit if run by the attr_changed listener
5226 if (g_object_get_data( tbl, "freeze" )) {
5227 return;
5228 }
5230 // in turn, prevent listener from responding
5231 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5233 bool modmade = false;
5235 if ( ege_select_one_action_get_active(act) != 0 ) {
5236 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5237 items != NULL;
5238 items = items->next)
5239 {
5240 if (SP_IS_ARC((SPItem *) items->data)) {
5241 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5242 repr->setAttribute("sodipodi:open", "true");
5243 SP_OBJECT((SPItem *) items->data)->updateRepr();
5244 modmade = true;
5245 }
5246 }
5247 } else {
5248 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5249 items != NULL;
5250 items = items->next)
5251 {
5252 if (SP_IS_ARC((SPItem *) items->data)) {
5253 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5254 repr->setAttribute("sodipodi:open", NULL);
5255 SP_OBJECT((SPItem *) items->data)->updateRepr();
5256 modmade = true;
5257 }
5258 }
5259 }
5261 if (modmade) {
5262 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
5263 _("Arc: Change open/closed"));
5264 }
5266 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5267 }
5269 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
5270 {
5271 GtkAdjustment *adj;
5272 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
5273 gtk_adjustment_set_value(adj, 0.0);
5274 gtk_adjustment_value_changed(adj);
5276 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
5277 gtk_adjustment_set_value(adj, 0.0);
5278 gtk_adjustment_value_changed(adj);
5280 spinbutton_defocus( GTK_OBJECT(obj) );
5281 }
5283 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
5284 gchar const */*old_value*/, gchar const */*new_value*/,
5285 bool /*is_interactive*/, gpointer data)
5286 {
5287 GObject *tbl = G_OBJECT(data);
5289 // quit if run by the _changed callbacks
5290 if (g_object_get_data( tbl, "freeze" )) {
5291 return;
5292 }
5294 // in turn, prevent callbacks from responding
5295 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5297 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
5298 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
5300 GtkAdjustment *adj1,*adj2;
5301 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
5302 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
5303 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
5304 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
5306 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
5308 char const *openstr = NULL;
5309 openstr = repr->attribute("sodipodi:open");
5310 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
5312 if (openstr) {
5313 ege_select_one_action_set_active( ocb, 1 );
5314 } else {
5315 ege_select_one_action_set_active( ocb, 0 );
5316 }
5318 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5319 }
5321 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
5322 NULL, /* child_added */
5323 NULL, /* child_removed */
5324 arc_tb_event_attr_changed,
5325 NULL, /* content_changed */
5326 NULL /* order_changed */
5327 };
5330 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
5331 {
5332 int n_selected = 0;
5333 Inkscape::XML::Node *repr = NULL;
5335 purge_repr_listener( tbl, tbl );
5337 for (GSList const *items = selection->itemList();
5338 items != NULL;
5339 items = items->next)
5340 {
5341 if (SP_IS_ARC((SPItem *) items->data)) {
5342 n_selected++;
5343 repr = SP_OBJECT_REPR((SPItem *) items->data);
5344 }
5345 }
5347 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
5349 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
5350 if (n_selected == 0) {
5351 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
5352 } else if (n_selected == 1) {
5353 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
5354 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5356 if (repr) {
5357 g_object_set_data( tbl, "repr", repr );
5358 Inkscape::GC::anchor(repr);
5359 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
5360 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
5361 }
5362 } else {
5363 // FIXME: implement averaging of all parameters for multiple selected
5364 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
5365 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5366 sp_arctb_sensitivize( tbl, 1, 0 );
5367 }
5368 }
5371 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5372 {
5373 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5375 EgeAdjustmentAction* eact = 0;
5376 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
5379 {
5380 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
5381 ege_output_action_set_use_markup( act, TRUE );
5382 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5383 g_object_set_data( holder, "mode_action", act );
5384 }
5386 /* Start */
5387 {
5388 eact = create_adjustment_action( "ArcStartAction",
5389 _("Start"), _("Start:"),
5390 _("The angle (in degrees) from the horizontal to the arc's start point"),
5391 "/tools/shapes/arc/start", 0.0,
5392 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
5393 -360.0, 360.0, 1.0, 10.0,
5394 0, 0, 0,
5395 sp_arctb_start_value_changed);
5396 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5397 }
5399 /* End */
5400 {
5401 eact = create_adjustment_action( "ArcEndAction",
5402 _("End"), _("End:"),
5403 _("The angle (in degrees) from the horizontal to the arc's end point"),
5404 "/tools/shapes/arc/end", 0.0,
5405 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
5406 -360.0, 360.0, 1.0, 10.0,
5407 0, 0, 0,
5408 sp_arctb_end_value_changed);
5409 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5410 }
5412 /* Segments / Pie checkbox */
5413 {
5414 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5416 GtkTreeIter iter;
5417 gtk_list_store_append( model, &iter );
5418 gtk_list_store_set( model, &iter,
5419 0, _("Closed arc"),
5420 1, _("Switch to segment (closed shape with two radii)"),
5421 2, INKSCAPE_ICON_DRAW_ELLIPSE_SEGMENT,
5422 -1 );
5424 gtk_list_store_append( model, &iter );
5425 gtk_list_store_set( model, &iter,
5426 0, _("Open Arc"),
5427 1, _("Switch to arc (unclosed shape)"),
5428 2, INKSCAPE_ICON_DRAW_ELLIPSE_ARC,
5429 -1 );
5431 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5432 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5433 g_object_set_data( holder, "open_action", act );
5435 ege_select_one_action_set_appearance( act, "full" );
5436 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5437 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5438 ege_select_one_action_set_icon_column( act, 2 );
5439 ege_select_one_action_set_icon_size( act, secondarySize );
5440 ege_select_one_action_set_tooltip_column( act, 1 );
5442 bool isClosed = !prefs->getBool("/tools/shapes/arc/open", false);
5443 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
5444 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
5445 }
5447 /* Make Whole */
5448 {
5449 InkAction* inky = ink_action_new( "ArcResetAction",
5450 _("Make whole"),
5451 _("Make the shape a whole ellipse, not arc or segment"),
5452 INKSCAPE_ICON_DRAW_ELLIPSE_WHOLE,
5453 secondarySize );
5454 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
5455 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5456 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
5457 g_object_set_data( holder, "make_whole", inky );
5458 }
5460 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
5461 // sensitivize make whole and open checkbox
5462 {
5463 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
5464 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
5465 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
5466 }
5469 sigc::connection *connection = new sigc::connection(
5470 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
5471 );
5472 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
5473 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
5474 }
5479 // toggle button callbacks and updaters
5481 //########################
5482 //## Dropper ##
5483 //########################
5485 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
5486 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5487 prefs->setInt( "/tools/dropper/pick", gtk_toggle_action_get_active( act ) );
5488 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
5489 if ( set_action ) {
5490 if ( gtk_toggle_action_get_active( act ) ) {
5491 gtk_action_set_sensitive( set_action, TRUE );
5492 } else {
5493 gtk_action_set_sensitive( set_action, FALSE );
5494 }
5495 }
5497 spinbutton_defocus(GTK_OBJECT(tbl));
5498 }
5500 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
5501 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5502 prefs->setBool( "/tools/dropper/setalpha", gtk_toggle_action_get_active( act ) );
5503 spinbutton_defocus(GTK_OBJECT(tbl));
5504 }
5507 /**
5508 * Dropper auxiliary toolbar construction and setup.
5509 *
5510 * TODO: Would like to add swatch of current color.
5511 * TODO: Add queue of last 5 or so colors selected with new swatches so that
5512 * can drag and drop places. Will provide a nice mixing palette.
5513 */
5514 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
5515 {
5516 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5517 gint pickAlpha = prefs->getInt( "/tools/dropper/pick", 1 );
5519 {
5520 EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
5521 ege_output_action_set_use_markup( act, TRUE );
5522 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5523 }
5525 {
5526 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
5527 _("Pick opacity"),
5528 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
5529 NULL,
5530 Inkscape::ICON_SIZE_DECORATION );
5531 g_object_set( act, "short_label", _("Pick"), NULL );
5532 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5533 g_object_set_data( holder, "pick_action", act );
5534 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
5535 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
5536 }
5538 {
5539 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
5540 _("Assign opacity"),
5541 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
5542 NULL,
5543 Inkscape::ICON_SIZE_DECORATION );
5544 g_object_set( act, "short_label", _("Assign"), NULL );
5545 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5546 g_object_set_data( holder, "set_action", act );
5547 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/dropper/setalpha", true) );
5548 // make sure it's disabled if we're not picking alpha
5549 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
5550 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
5551 }
5552 }
5555 //########################
5556 //## LPETool ##
5557 //########################
5559 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
5561 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
5562 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
5563 {
5564 using namespace Inkscape::LivePathEffect;
5566 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
5567 SPEventContext *ec = desktop->event_context;
5568 if (!SP_IS_LPETOOL_CONTEXT(ec)) {
5569 return;
5570 }
5572 // only take action if run by the attr_changed listener
5573 if (!g_object_get_data(tbl, "freeze")) {
5574 // in turn, prevent listener from responding
5575 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5577 gint mode = ege_select_one_action_get_active(act);
5578 EffectType type = lpesubtools[mode].type;
5580 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5581 bool success = lpetool_try_construction(lc, type);
5582 if (success) {
5583 // since the construction was already performed, we set the state back to inactive
5584 ege_select_one_action_set_active(act, 0);
5585 mode = 0;
5586 } else {
5587 // switch to the chosen subtool
5588 SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
5589 }
5591 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5592 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5593 prefs->setInt( "/tools/lpetool/mode", mode );
5594 }
5596 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
5597 }
5598 }
5600 void sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject */*tbl*/)
5601 {
5602 SPEventContext *ec = selection->desktop()->event_context;
5603 if (!SP_IS_LPETOOL_CONTEXT(ec))
5604 return;
5606 lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
5607 }
5609 void
5610 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
5611 {
5612 using namespace Inkscape::LivePathEffect;
5613 SPEventContext *ec = selection->desktop()->event_context;
5614 if (!SP_IS_LPETOOL_CONTEXT(ec))
5615 return;
5616 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
5618 lpetool_delete_measuring_items(lc);
5619 lpetool_create_measuring_items(lc, selection);
5621 // activate line segment combo box if a single item with LPELineSegment is selected
5622 GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
5623 SPItem *item = selection->singleItem();
5624 if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
5625 SPLPEItem *lpeitem = SP_LPE_ITEM(item);
5626 Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
5627 if (lpe && lpe->effectType() == LINE_SEGMENT) {
5628 LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
5629 g_object_set_data(tbl, "currentlpe", lpe);
5630 g_object_set_data(tbl, "currentlpeitem", lpeitem);
5631 gtk_action_set_sensitive(w, TRUE);
5632 ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
5633 } else {
5634 g_object_set_data(tbl, "currentlpe", NULL);
5635 g_object_set_data(tbl, "currentlpeitem", NULL);
5636 gtk_action_set_sensitive(w, FALSE);
5637 }
5638 } else {
5639 g_object_set_data(tbl, "currentlpe", NULL);
5640 g_object_set_data(tbl, "currentlpeitem", NULL);
5641 gtk_action_set_sensitive(w, FALSE);
5642 }
5643 }
5645 static void
5646 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
5647 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5648 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5650 bool show = gtk_toggle_action_get_active( act );
5651 prefs->setBool("/tools/lpetool/show_bbox", show);
5653 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5654 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5655 lpetool_context_reset_limiting_bbox(lc);
5656 }
5657 }
5659 static void
5660 lpetool_toggle_show_measuring_info (GtkToggleAction *act, GObject *tbl) {
5661 SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
5662 if (!tools_isactive(desktop, TOOLS_LPETOOL))
5663 return;
5665 GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
5666 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5667 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5668 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5669 bool show = gtk_toggle_action_get_active( act );
5670 prefs->setBool("/tools/lpetool/show_measuring_info", show);
5671 lpetool_show_measuring_info(lc, show);
5672 gtk_action_set_sensitive(GTK_ACTION(unitact), show);
5673 }
5674 }
5676 static void lpetool_unit_changed(GtkAction* /*act*/, GObject* tbl) {
5677 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
5678 SPUnit const *unit = tracker->getActiveUnit();
5679 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5680 prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5682 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5683 if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
5684 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5685 lpetool_delete_measuring_items(lc);
5686 lpetool_create_measuring_items(lc);
5687 }
5688 }
5690 static void
5691 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
5692 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5693 Inkscape::Selection *selection = desktop->selection;
5695 Geom::OptRect bbox = selection->bounds();
5697 if (bbox) {
5698 Geom::Point A(bbox->min());
5699 Geom::Point B(bbox->max());
5701 A *= desktop->doc2dt();
5702 B *= desktop->doc2dt();
5704 // TODO: should we provide a way to store points in prefs?
5705 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5706 prefs->setDouble("/tools/lpetool/bbox_upperleftx", A[Geom::X]);
5707 prefs->setDouble("/tools/lpetool/bbox_upperlefty", A[Geom::Y]);
5708 prefs->setDouble("/tools/lpetool/bbox_lowerrightx", B[Geom::X]);
5709 prefs->setDouble("/tools/lpetool/bbox_lowerrighty", B[Geom::Y]);
5711 lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
5712 }
5714 gtk_toggle_action_set_active(act, false);
5715 }
5717 static void
5718 sp_line_segment_build_list(GObject *tbl)
5719 {
5720 g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
5722 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
5723 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
5724 gtk_list_store_clear (model);
5726 // TODO: we add the entries of rht combo box manually; later this should be done automatically
5727 {
5728 GtkTreeIter iter;
5729 gtk_list_store_append( model, &iter );
5730 gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
5731 gtk_list_store_append( model, &iter );
5732 gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
5733 gtk_list_store_append( model, &iter );
5734 gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
5735 gtk_list_store_append( model, &iter );
5736 gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
5737 }
5739 g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5740 }
5742 static void
5743 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
5744 using namespace Inkscape::LivePathEffect;
5746 // quit if run by the attr_changed listener
5747 if (g_object_get_data(tbl, "freeze")) {
5748 return;
5749 }
5751 // in turn, prevent listener from responding
5752 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5754 LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
5755 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5756 if (lpeitem) {
5757 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5758 lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
5759 sp_lpe_item_update_patheffect(lpeitem, true, true);
5760 }
5762 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5763 }
5765 static void
5766 lpetool_open_lpe_dialog (GtkToggleAction *act, gpointer data) {
5767 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5769 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5770 sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5771 }
5772 gtk_toggle_action_set_active(act, false);
5773 }
5775 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5776 {
5777 UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5778 tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5779 g_object_set_data(holder, "tracker", tracker);
5780 SPUnit const *unit = tracker->getActiveUnit();
5782 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5783 prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5785 /** Automatically create a list of LPEs that get added to the toolbar **/
5786 {
5787 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5789 GtkTreeIter iter;
5791 // the first toggle button represents the state that no subtool is active (remove this when
5792 // this can be modeled by EgeSelectOneAction or some other action)
5793 gtk_list_store_append( model, &iter );
5794 gtk_list_store_set( model, &iter,
5795 0, _("All inactive"),
5796 1, _("No geometric tool is active"),
5797 2, "draw-geometry-inactive",
5798 -1 );
5800 Inkscape::LivePathEffect::EffectType type;
5801 for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5802 type = lpesubtools[i].type;
5803 gtk_list_store_append( model, &iter );
5804 gtk_list_store_set( model, &iter,
5805 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5806 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5807 2, lpesubtools[i].icon_name,
5808 -1 );
5809 }
5811 EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5812 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5813 g_object_set_data( holder, "lpetool_mode_action", act );
5815 ege_select_one_action_set_appearance( act, "full" );
5816 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5817 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5818 ege_select_one_action_set_icon_column( act, 2 );
5819 ege_select_one_action_set_tooltip_column( act, 1 );
5821 gint lpeToolMode = prefs->getInt("/tools/lpetool/mode", 0);
5822 ege_select_one_action_set_active( act, lpeToolMode );
5823 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5824 }
5826 /* Show limiting bounding box */
5827 {
5828 InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5829 _("Show limiting bounding box"),
5830 _("Show bounding box (used to cut infinite lines)"),
5831 "show-bounding-box",
5832 Inkscape::ICON_SIZE_DECORATION );
5833 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5834 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5835 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_bbox", true ) );
5836 }
5838 /* Set limiting bounding box to bbox of current selection */
5839 {
5840 InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5841 _("Get limiting bounding box from selection"),
5842 _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5843 "draw-geometry-set-bounding-box",
5844 Inkscape::ICON_SIZE_DECORATION );
5845 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5846 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5847 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5848 }
5851 /* Combo box to choose line segment type */
5852 {
5853 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5854 EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5855 ege_select_one_action_set_appearance (act, "compact");
5856 g_object_set_data (holder, "lpetool_line_segment_action", act );
5858 g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5860 sp_line_segment_build_list (holder);
5862 g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5863 gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5864 gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5865 }
5867 /* Display measuring info for selected items */
5868 {
5869 InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5870 _("Display measuring info"),
5871 _("Display measuring info for selected items"),
5872 "draw-geometry-show-measuring-info",
5873 Inkscape::ICON_SIZE_DECORATION );
5874 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5875 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
5876 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_measuring_info", true ) );
5877 }
5879 // add the units menu
5880 {
5881 GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5882 gtk_action_group_add_action( mainActions, act );
5883 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5884 g_object_set_data(holder, "lpetool_units_action", act);
5885 gtk_action_set_sensitive(act, prefs->getBool("/tools/lpetool/show_measuring_info", true));
5886 }
5888 /* Open LPE dialog (to adapt parameters numerically) */
5889 {
5890 InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
5891 _("Open LPE dialog"),
5892 _("Open LPE dialog (to adapt parameters numerically)"),
5893 "dialog-geometry",
5894 Inkscape::ICON_SIZE_DECORATION );
5895 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5896 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
5897 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5898 }
5900 //watch selection
5901 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5903 sigc::connection *c_selection_modified =
5904 new sigc::connection (sp_desktop_selection (desktop)->connectModified
5905 (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
5906 pool->add_connection ("selection-modified", c_selection_modified);
5908 sigc::connection *c_selection_changed =
5909 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5910 (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5911 pool->add_connection ("selection-changed", c_selection_changed);
5912 }
5914 //########################
5915 //## Eraser ##
5916 //########################
5918 static void sp_erc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
5919 {
5920 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5921 prefs->setDouble( "/tools/eraser/width", adj->value );
5922 update_presets_list(tbl);
5923 }
5925 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5926 {
5927 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5928 bool eraserMode = ege_select_one_action_get_active( act ) != 0;
5929 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5930 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5931 prefs->setBool( "/tools/eraser/mode", eraserMode );
5932 }
5934 // only take action if run by the attr_changed listener
5935 if (!g_object_get_data( tbl, "freeze" )) {
5936 // in turn, prevent listener from responding
5937 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5939 if ( eraserMode != 0 ) {
5940 } else {
5941 }
5942 // TODO finish implementation
5944 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5945 }
5946 }
5948 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5949 {
5950 {
5951 /* Width */
5952 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5953 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5954 EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5955 _("Pen Width"), _("Width:"),
5956 _("The width of the eraser pen (relative to the visible canvas area)"),
5957 "/tools/eraser/width", 15,
5958 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5959 1, 100, 1.0, 10.0,
5960 labels, values, G_N_ELEMENTS(labels),
5961 sp_erc_width_value_changed, 1, 0);
5962 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5963 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5964 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5965 }
5967 {
5968 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5970 GtkTreeIter iter;
5971 gtk_list_store_append( model, &iter );
5972 gtk_list_store_set( model, &iter,
5973 0, _("Delete"),
5974 1, _("Delete objects touched by the eraser"),
5975 2, INKSCAPE_ICON_DRAW_ERASER_DELETE_OBJECTS,
5976 -1 );
5978 gtk_list_store_append( model, &iter );
5979 gtk_list_store_set( model, &iter,
5980 0, _("Cut"),
5981 1, _("Cut out from objects"),
5982 2, INKSCAPE_ICON_PATH_DIFFERENCE,
5983 -1 );
5985 EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5986 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5987 g_object_set_data( holder, "eraser_mode_action", act );
5989 ege_select_one_action_set_appearance( act, "full" );
5990 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5991 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5992 ege_select_one_action_set_icon_column( act, 2 );
5993 ege_select_one_action_set_tooltip_column( act, 1 );
5995 /// @todo Convert to boolean?
5996 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5997 gint eraserMode = prefs->getBool("/tools/eraser/mode") ? 1 : 0;
5998 ege_select_one_action_set_active( act, eraserMode );
5999 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
6000 }
6002 }
6004 //########################
6005 //## Text Toolbox ##
6006 //########################
6007 /*
6008 static void
6009 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
6010 {
6011 //Call back for letter sizing spinbutton
6012 }
6014 static void
6015 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
6016 {
6017 //Call back for line height spinbutton
6018 }
6020 static void
6021 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
6022 {
6023 //Call back for horizontal kerning spinbutton
6024 }
6026 static void
6027 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
6028 {
6029 //Call back for vertical kerning spinbutton
6030 }
6032 static void
6033 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
6034 {
6035 //Call back for letter rotation spinbutton
6036 }*/
6038 namespace {
6040 void
6041 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
6042 {
6043 // quit if run by the _changed callbacks
6044 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6045 return;
6046 }
6048 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6050 SPStyle *query =
6051 sp_style_new (SP_ACTIVE_DOCUMENT);
6053 int result_family =
6054 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6056 int result_style =
6057 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6059 int result_numbers =
6060 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6062 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6064 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6065 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
6066 // there are no texts in selection, read from prefs
6068 sp_style_read_from_prefs(query, "/tools/text");
6070 if (g_object_get_data(tbl, "text_style_from_prefs")) {
6071 // do not reset the toolbar style from prefs if we already did it last time
6072 sp_style_unref(query);
6073 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6074 return;
6075 }
6076 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
6077 } else {
6078 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
6079 }
6081 if (query->text)
6082 {
6083 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
6084 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6085 gtk_entry_set_text (GTK_ENTRY (entry), "");
6087 } else if (query->text->font_specification.value || query->text->font_family.value) {
6089 Gtk::ComboBoxEntry *combo = (Gtk::ComboBoxEntry *) (g_object_get_data (G_OBJECT (tbl), "family-entry-combo"));
6090 GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6092 // Get the font that corresponds
6093 Glib::ustring familyName;
6095 font_instance * font = font_factory::Default()->FaceFromStyle(query);
6096 if (font) {
6097 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
6098 font->Unref();
6099 font = NULL;
6100 }
6102 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
6104 Gtk::TreeIter iter;
6105 try {
6106 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
6107 Glib::RefPtr<Gtk::TreeModel> model = combo->get_model();
6108 iter = model->get_iter(path);
6109 } catch (...) {
6110 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
6111 sp_style_unref(query);
6112 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6113 return;
6114 }
6116 combo->set_active (iter);
6117 }
6119 //Size
6120 {
6121 GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
6122 gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
6123 gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
6124 g_free(str);
6125 }
6127 //Anchor
6128 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
6129 {
6130 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
6131 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6132 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6133 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6134 }
6135 else
6136 {
6137 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
6138 {
6139 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
6140 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6141 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6142 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6143 }
6144 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
6145 {
6146 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
6147 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6148 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6149 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6150 }
6151 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
6152 {
6153 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
6154 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6155 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6156 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6157 }
6158 }
6160 //Style
6161 {
6162 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
6164 gboolean active = gtk_toggle_button_get_active (button);
6165 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));
6167 if (active != check)
6168 {
6169 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6170 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6171 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6172 }
6173 }
6175 {
6176 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
6178 gboolean active = gtk_toggle_button_get_active (button);
6179 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
6181 if (active != check)
6182 {
6183 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6184 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6185 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6186 }
6187 }
6189 //Orientation
6190 //locking both buttons, changing one affect all group (both)
6191 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
6192 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6194 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
6195 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
6197 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
6198 {
6199 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6200 }
6201 else
6202 {
6203 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
6204 }
6205 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6206 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
6207 }
6209 sp_style_unref(query);
6211 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6212 }
6214 void
6215 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
6216 {
6217 sp_text_toolbox_selection_changed (selection, tbl);
6218 }
6220 void
6221 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
6222 {
6223 sp_text_toolbox_selection_changed (NULL, tbl);
6224 }
6226 void
6227 sp_text_toolbox_family_changed (GtkComboBoxEntry *,
6228 GObject *tbl)
6229 {
6230 // quit if run by the _changed callbacks
6231 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6232 return;
6233 }
6235 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6237 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6238 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6239 const gchar* family = gtk_entry_get_text (GTK_ENTRY (entry));
6241 //g_print ("family changed to: %s\n", family);
6243 SPStyle *query =
6244 sp_style_new (SP_ACTIVE_DOCUMENT);
6246 int result_fontspec =
6247 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6249 SPCSSAttr *css = sp_repr_css_attr_new ();
6251 // First try to get the font spec from the stored value
6252 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
6254 if (fontSpec.empty()) {
6255 // Construct a new font specification if it does not yet exist
6256 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6257 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6258 fontFromStyle->Unref();
6259 }
6261 if (!fontSpec.empty()) {
6263 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
6265 if (!newFontSpec.empty()) {
6267 if (fontSpec != newFontSpec) {
6269 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
6271 if (font) {
6272 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6274 // Set all the these just in case they were altered when finding the best
6275 // match for the new family and old style...
6277 gchar c[256];
6279 font->Family(c, 256);
6281 sp_repr_css_set_property (css, "font-family", c);
6283 font->Attribute( "weight", c, 256);
6284 sp_repr_css_set_property (css, "font-weight", c);
6286 font->Attribute("style", c, 256);
6287 sp_repr_css_set_property (css, "font-style", c);
6289 font->Attribute("stretch", c, 256);
6290 sp_repr_css_set_property (css, "font-stretch", c);
6292 font->Attribute("variant", c, 256);
6293 sp_repr_css_set_property (css, "font-variant", c);
6295 font->Unref();
6296 }
6297 }
6299 } else {
6300 // If the old font on selection (or default) was not existing on the system,
6301 // ReplaceFontSpecificationFamily does not work. In that case we fall back to blindly
6302 // setting the family reported by the family chooser.
6304 //g_print ("fallback setting family: %s\n", family);
6305 sp_repr_css_set_property (css, "-inkscape-font-specification", family);
6306 sp_repr_css_set_property (css, "font-family", family);
6307 }
6308 }
6310 // If querying returned nothing, set the default style of the tool (for new texts)
6311 if (result_fontspec == QUERY_STYLE_NOTHING)
6312 {
6313 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6314 prefs->mergeStyle("/tools/text/style", css);
6315 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
6316 }
6317 else
6318 {
6319 sp_desktop_set_style (desktop, css, true, true);
6320 }
6322 sp_style_unref(query);
6324 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6325 _("Text: Change font family"));
6326 sp_repr_css_attr_unref (css);
6328 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6330 // unfreeze
6331 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6333 // focus to canvas
6334 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6335 }
6338 void
6339 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
6340 gpointer data)
6341 {
6342 if (g_object_get_data (G_OBJECT (button), "block")) return;
6343 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
6344 int prop = GPOINTER_TO_INT(data);
6346 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6347 SPCSSAttr *css = sp_repr_css_attr_new ();
6349 switch (prop)
6350 {
6351 case 0:
6352 {
6353 sp_repr_css_set_property (css, "text-anchor", "start");
6354 sp_repr_css_set_property (css, "text-align", "start");
6355 break;
6356 }
6357 case 1:
6358 {
6359 sp_repr_css_set_property (css, "text-anchor", "middle");
6360 sp_repr_css_set_property (css, "text-align", "center");
6361 break;
6362 }
6364 case 2:
6365 {
6366 sp_repr_css_set_property (css, "text-anchor", "end");
6367 sp_repr_css_set_property (css, "text-align", "end");
6368 break;
6369 }
6371 case 3:
6372 {
6373 sp_repr_css_set_property (css, "text-anchor", "start");
6374 sp_repr_css_set_property (css, "text-align", "justify");
6375 break;
6376 }
6377 }
6379 SPStyle *query =
6380 sp_style_new (SP_ACTIVE_DOCUMENT);
6381 int result_numbers =
6382 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6384 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6385 if (result_numbers == QUERY_STYLE_NOTHING)
6386 {
6387 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6388 prefs->mergeStyle("/tools/text/style", css);
6389 }
6391 sp_style_unref(query);
6393 sp_desktop_set_style (desktop, css, true, true);
6394 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6395 _("Text: Change alignment"));
6396 sp_repr_css_attr_unref (css);
6398 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6399 }
6401 void
6402 sp_text_toolbox_style_toggled (GtkToggleButton *button,
6403 gpointer data)
6404 {
6405 if (g_object_get_data (G_OBJECT (button), "block")) return;
6407 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6408 SPCSSAttr *css = sp_repr_css_attr_new ();
6409 int prop = GPOINTER_TO_INT(data);
6410 bool active = gtk_toggle_button_get_active (button);
6412 SPStyle *query =
6413 sp_style_new (SP_ACTIVE_DOCUMENT);
6415 int result_fontspec =
6416 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6418 //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6419 //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6420 //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6422 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
6423 Glib::ustring newFontSpec = "";
6425 if (fontSpec.empty()) {
6426 // Construct a new font specification if it does not yet exist
6427 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6428 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6429 fontFromStyle->Unref();
6430 }
6432 bool nochange = true;
6433 switch (prop)
6434 {
6435 case 0:
6436 {
6437 if (!fontSpec.empty()) {
6438 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
6439 if (!newFontSpec.empty()) {
6440 // Don't even set the bold if the font didn't exist on the system
6441 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
6442 nochange = false;
6443 }
6444 }
6445 // set or reset the button according
6446 if(nochange) {
6447 gboolean check = gtk_toggle_button_get_active (button);
6449 if (active != check)
6450 {
6451 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6452 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6453 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6454 }
6455 }
6457 break;
6458 }
6460 case 1:
6461 {
6462 if (!fontSpec.empty()) {
6463 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
6464 if (!newFontSpec.empty()) {
6465 // Don't even set the italic if the font didn't exist on the system
6466 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
6467 nochange = false;
6468 }
6469 }
6470 if(nochange) {
6471 gboolean check = gtk_toggle_button_get_active (button);
6473 if (active != check)
6474 {
6475 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6476 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6477 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6478 }
6479 }
6480 break;
6481 }
6482 }
6484 if (!newFontSpec.empty()) {
6485 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6486 }
6488 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6489 if (result_fontspec == QUERY_STYLE_NOTHING)
6490 {
6491 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6492 prefs->mergeStyle("/tools/text/style", css);
6493 }
6495 sp_style_unref(query);
6497 sp_desktop_set_style (desktop, css, true, true);
6498 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6499 _("Text: Change font style"));
6500 sp_repr_css_attr_unref (css);
6502 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6503 }
6505 void
6506 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
6507 gpointer data)
6508 {
6509 if (g_object_get_data (G_OBJECT (button), "block")) {
6510 return;
6511 }
6513 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6514 SPCSSAttr *css = sp_repr_css_attr_new ();
6515 int prop = GPOINTER_TO_INT(data);
6517 switch (prop)
6518 {
6519 case 0:
6520 {
6521 sp_repr_css_set_property (css, "writing-mode", "lr");
6522 break;
6523 }
6525 case 1:
6526 {
6527 sp_repr_css_set_property (css, "writing-mode", "tb");
6528 break;
6529 }
6530 }
6532 SPStyle *query =
6533 sp_style_new (SP_ACTIVE_DOCUMENT);
6534 int result_numbers =
6535 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6537 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6538 if (result_numbers == QUERY_STYLE_NOTHING)
6539 {
6540 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6541 prefs->mergeStyle("/tools/text/style", css);
6542 }
6544 sp_desktop_set_style (desktop, css, true, true);
6545 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6546 _("Text: Change orientation"));
6547 sp_repr_css_attr_unref (css);
6549 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6550 }
6552 gboolean
6553 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6554 {
6555 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6556 if (!desktop) return FALSE;
6558 switch (get_group0_keyval (event)) {
6559 case GDK_KP_Enter: // chosen
6560 case GDK_Return:
6561 // unfreeze and update, which will defocus
6562 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6563 sp_text_toolbox_family_changed (NULL, tbl);
6564 return TRUE; // I consumed the event
6565 break;
6566 case GDK_Escape:
6567 // defocus
6568 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6569 return TRUE; // I consumed the event
6570 break;
6571 }
6572 return FALSE;
6573 }
6575 gboolean
6576 sp_text_toolbox_family_list_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject */*tbl*/)
6577 {
6578 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6579 if (!desktop) return FALSE;
6581 switch (get_group0_keyval (event)) {
6582 case GDK_KP_Enter:
6583 case GDK_Return:
6584 case GDK_Escape: // defocus
6585 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6586 return TRUE; // I consumed the event
6587 break;
6588 case GDK_w:
6589 case GDK_W:
6590 if (event->state & GDK_CONTROL_MASK) {
6591 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6592 return TRUE; // I consumed the event
6593 }
6594 break;
6595 }
6596 return FALSE;
6597 }
6600 void
6601 sp_text_toolbox_size_changed (GtkComboBox *cbox,
6602 GObject *tbl)
6603 {
6604 // quit if run by the _changed callbacks
6605 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6606 return;
6607 }
6609 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6611 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6613 // If this is not from selecting a size in the list (in which case get_active will give the
6614 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
6615 // process this event. This fixes GTK's stupid insistence on sending an activate change every
6616 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
6617 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed")) {
6618 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6619 return;
6620 }
6622 gdouble value = -1;
6623 {
6624 gchar *endptr;
6625 gchar *const text = gtk_combo_box_get_active_text(cbox);
6626 if (text) {
6627 value = g_strtod(text, &endptr);
6628 if (endptr == text) { // Conversion failed, non-numeric input.
6629 value = -1;
6630 }
6631 g_free(text);
6632 }
6633 }
6634 if (value <= 0) {
6635 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6636 return; // could not parse value
6637 }
6639 SPCSSAttr *css = sp_repr_css_attr_new ();
6640 Inkscape::CSSOStringStream osfs;
6641 osfs << value;
6642 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
6644 SPStyle *query =
6645 sp_style_new (SP_ACTIVE_DOCUMENT);
6646 int result_numbers =
6647 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6649 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6650 if (result_numbers == QUERY_STYLE_NOTHING)
6651 {
6652 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6653 prefs->mergeStyle("/tools/text/style", css);
6654 }
6656 sp_style_unref(query);
6658 sp_desktop_set_style (desktop, css, true, true);
6659 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
6660 _("Text: Change font size"));
6661 sp_repr_css_attr_unref (css);
6663 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6665 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6666 }
6668 gboolean
6669 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
6670 {
6671 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6672 if (!desktop) return FALSE;
6674 if (!g_object_get_data (tbl, "esc-pressed")) {
6675 g_object_set_data (tbl, "enter-pressed", gpointer(1));
6676 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6677 sp_text_toolbox_size_changed (cbox, tbl);
6678 g_object_set_data (tbl, "enter-pressed", gpointer(0));
6679 }
6680 return FALSE; // I consumed the event
6681 }
6684 gboolean
6685 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6686 {
6687 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6688 if (!desktop) return FALSE;
6690 switch (get_group0_keyval (event)) {
6691 case GDK_Escape: // defocus
6692 g_object_set_data (tbl, "esc-pressed", gpointer(1));
6693 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6694 g_object_set_data (tbl, "esc-pressed", gpointer(0));
6695 return TRUE; // I consumed the event
6696 break;
6697 case GDK_Return: // defocus
6698 case GDK_KP_Enter:
6699 g_object_set_data (tbl, "enter-pressed", gpointer(1));
6700 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6701 sp_text_toolbox_size_changed (cbox, tbl);
6702 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6703 g_object_set_data (tbl, "enter-pressed", gpointer(0));
6704 return TRUE; // I consumed the event
6705 break;
6706 }
6707 return FALSE;
6708 }
6710 // While editing font name in the entry, disable family_changed by freezing, otherwise completion
6711 // does not work!
6712 gboolean
6713 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
6714 GdkEventFocus */*event*/,
6715 GObject *tbl)
6716 {
6717 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6718 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1); // select all
6719 return FALSE;
6720 }
6722 gboolean
6723 sp_text_toolbox_entry_focus_out (GtkWidget *entry,
6724 GdkEventFocus */*event*/,
6725 GObject *tbl)
6726 {
6727 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6728 gtk_entry_select_region (GTK_ENTRY (entry), 0, 0); // deselect
6729 return FALSE;
6730 }
6732 void
6733 cell_data_func (GtkCellLayout */*cell_layout*/,
6734 GtkCellRenderer *cell,
6735 GtkTreeModel *tree_model,
6736 GtkTreeIter *iter,
6737 gpointer /*data*/)
6738 {
6739 gchar *family;
6740 gtk_tree_model_get(tree_model, iter, 0, &family, -1);
6741 gchar *const family_escaped = g_markup_escape_text(family, -1);
6743 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6744 int show_sample = prefs->getInt("/tools/text/show_sample_in_list", 1);
6745 if (show_sample) {
6747 Glib::ustring sample = prefs->getString("/tools/text/font_sample");
6748 gchar *const sample_escaped = g_markup_escape_text(sample.data(), -1);
6750 std::stringstream markup;
6751 markup << family_escaped << " <span foreground='darkgray' font_family='"
6752 << family_escaped << "'>" << sample_escaped << "</span>";
6753 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6755 g_free(sample_escaped);
6756 } else {
6757 g_object_set (G_OBJECT (cell), "markup", family_escaped, NULL);
6758 }
6760 g_free(family);
6761 g_free(family_escaped);
6762 }
6764 gboolean text_toolbox_completion_match_selected(GtkEntryCompletion */*widget*/,
6765 GtkTreeModel *model,
6766 GtkTreeIter *iter,
6767 GObject *tbl)
6768 {
6769 // We intercept this signal so as to fire family_changed at once (without it, you'd have to
6770 // press Enter again after choosing a completion)
6771 gchar *family = 0;
6772 gtk_tree_model_get(model, iter, 0, &family, -1);
6774 GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6775 gtk_entry_set_text (GTK_ENTRY (entry), family);
6777 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6778 sp_text_toolbox_family_changed (NULL, tbl);
6779 return TRUE;
6780 }
6783 static void
6784 cbe_add_completion (GtkComboBoxEntry *cbe, GObject *tbl){
6785 GtkEntry *entry;
6786 GtkEntryCompletion *completion;
6787 GtkTreeModel *model;
6789 entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(cbe)));
6790 completion = gtk_entry_completion_new();
6791 model = gtk_combo_box_get_model(GTK_COMBO_BOX(cbe));
6792 gtk_entry_completion_set_model(completion, model);
6793 gtk_entry_completion_set_text_column(completion, 0);
6794 gtk_entry_completion_set_inline_completion(completion, FALSE);
6795 gtk_entry_completion_set_inline_selection(completion, FALSE);
6796 gtk_entry_completion_set_popup_completion(completion, TRUE);
6797 gtk_entry_set_completion(entry, completion);
6799 g_signal_connect (G_OBJECT (completion), "match-selected", G_CALLBACK (text_toolbox_completion_match_selected), tbl);
6801 g_object_unref(completion);
6802 }
6804 void sp_text_toolbox_family_popnotify(GtkComboBox *widget,
6805 void */*property*/,
6806 GObject *tbl)
6807 {
6808 // while the drop-down is open, we disable font family changing, reenabling it only when it closes
6810 gboolean shown;
6811 g_object_get (G_OBJECT(widget), "popup-shown", &shown, NULL);
6812 if (shown) {
6813 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6814 //g_print("POP: notify: SHOWN\n");
6815 } else {
6816 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6818 // stupid GTK doesn't let us attach to events in the drop-down window, so we peek here to
6819 // find out if the drop down was closed by Enter and if so, manually update (only
6820 // necessary on Windows, on Linux it updates itself - what a mess, but we'll manage)
6821 GdkEvent *ev = gtk_get_current_event();
6822 if (ev) {
6823 //g_print ("ev type: %d\n", ev->type);
6824 if (ev->type == GDK_KEY_PRESS) {
6825 switch (get_group0_keyval ((GdkEventKey *) ev)) {
6826 case GDK_KP_Enter: // chosen
6827 case GDK_Return:
6828 {
6829 // make sure the chosen one is inserted into the entry
6830 GtkComboBox *combo = GTK_COMBO_BOX (((Gtk::ComboBox *) (g_object_get_data (tbl, "family-entry-combo")))->gobj());
6831 GtkTreeModel *model = gtk_combo_box_get_model(combo);
6832 GtkTreeIter iter;
6833 gboolean has_active = gtk_combo_box_get_active_iter (combo, &iter);
6834 if (has_active) {
6835 gchar *family;
6836 gtk_tree_model_get(model, &iter, 0, &family, -1);
6837 GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6838 gtk_entry_set_text (GTK_ENTRY (entry), family);
6839 }
6841 // update
6842 sp_text_toolbox_family_changed (NULL, tbl);
6843 break;
6844 }
6845 }
6846 }
6847 }
6849 // regardless of whether we updated, defocus the widget
6850 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6851 if (desktop)
6852 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6853 //g_print("POP: notify: HIDDEN\n");
6854 }
6855 }
6857 GtkWidget*
6858 sp_text_toolbox_new (SPDesktop *desktop)
6859 {
6860 GtkToolbar *tbl = GTK_TOOLBAR(gtk_toolbar_new());
6861 GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("/toolbox/secondary", 1));
6863 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
6864 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
6866 GtkTooltips *tt = gtk_tooltips_new();
6868 ////////////Family
6869 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
6870 Gtk::ComboBoxEntry *font_sel = Gtk::manage(new Gtk::ComboBoxEntry(store));
6872 gtk_rc_parse_string (
6873 "style \"dropdown-as-list-style\"\n"
6874 "{\n"
6875 " GtkComboBox::appears-as-list = 1\n"
6876 "}\n"
6877 "widget \"*.toolbox-fontfamily-list\" style \"dropdown-as-list-style\"");
6878 gtk_widget_set_name(GTK_WIDGET (font_sel->gobj()), "toolbox-fontfamily-list");
6879 gtk_tooltips_set_tip (tt, GTK_WIDGET (font_sel->gobj()), _("Select font family (Alt+X to access)"), "");
6881 g_signal_connect (G_OBJECT (font_sel->gobj()), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
6883 cbe_add_completion(font_sel->gobj(), G_OBJECT(tbl));
6885 gtk_toolbar_append_widget( tbl, (GtkWidget*) font_sel->gobj(), "", "");
6886 g_object_set_data (G_OBJECT (tbl), "family-entry-combo", font_sel);
6888 // expand the field a bit so as to view more of the previews in the drop-down
6889 GtkRequisition req;
6890 gtk_widget_size_request (GTK_WIDGET (font_sel->gobj()), &req);
6891 gtk_widget_set_size_request (GTK_WIDGET (font_sel->gobj()), MIN(req.width + 50, 500), -1);
6893 GtkWidget* entry = (GtkWidget*) font_sel->get_entry()->gobj();
6894 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6896 g_signal_connect (G_OBJECT (font_sel->gobj()), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6897 g_signal_connect (G_OBJECT (font_sel->gobj()), "notify::popup-shown",
6898 G_CALLBACK (sp_text_toolbox_family_popnotify), tbl);
6899 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
6900 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
6901 g_signal_connect (G_OBJECT (entry), "focus-out-event", G_CALLBACK (sp_text_toolbox_entry_focus_out), tbl);
6903 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
6904 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
6906 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
6907 gtk_cell_layout_clear( GTK_CELL_LAYOUT(font_sel->gobj()) );
6908 gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(font_sel->gobj()) , cell , TRUE );
6909 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT(font_sel->gobj()), cell, GtkCellLayoutDataFunc (cell_data_func), NULL, NULL);
6911 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
6912 GtkWidget *box = gtk_event_box_new ();
6913 gtk_container_add (GTK_CONTAINER (box), image);
6914 gtk_toolbar_append_widget( tbl, box, "", "");
6915 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
6916 gtk_tooltips_set_tip (tt, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
6917 gtk_widget_hide (GTK_WIDGET (box));
6918 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
6920 ////////////Size
6921 gchar const *const sizes[] = {
6922 "4", "6", "8", "9", "10", "11", "12", "13", "14",
6923 "16", "18", "20", "22", "24", "28",
6924 "32", "36", "40", "48", "56", "64", "72", "144"
6925 };
6927 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
6928 for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
6929 gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
6930 }
6931 gtk_widget_set_size_request (cbox, 80, -1);
6932 gtk_toolbar_append_widget( tbl, cbox, "", "");
6933 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
6934 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
6935 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
6936 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
6938 ////////////Text anchor
6939 GtkWidget *group = gtk_radio_button_new (NULL);
6940 GtkWidget *row = gtk_hbox_new (FALSE, 4);
6941 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
6943 // left
6944 GtkWidget *rbutton = group;
6945 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6946 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
6947 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6949 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6950 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
6951 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
6952 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
6954 // center
6955 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6956 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6957 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
6958 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6960 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6961 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
6962 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
6963 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
6965 // right
6966 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6967 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6968 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
6969 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6971 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6972 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
6973 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
6974 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
6976 // fill
6977 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6978 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6979 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
6980 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6982 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6983 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
6984 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
6985 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
6987 gtk_toolbar_append_widget( tbl, row, "", "");
6989 //spacer
6990 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6992 ////////////Text style
6993 row = gtk_hbox_new (FALSE, 4);
6995 // bold
6996 rbutton = gtk_toggle_button_new ();
6997 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6998 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
6999 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7000 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
7002 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7003 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
7004 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
7006 // italic
7007 rbutton = gtk_toggle_button_new ();
7008 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7009 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
7010 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7011 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
7013 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7014 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
7015 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
7017 gtk_toolbar_append_widget( tbl, row, "", "");
7019 //spacer
7020 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
7022 // Text orientation
7023 group = gtk_radio_button_new (NULL);
7024 row = gtk_hbox_new (FALSE, 4);
7025 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
7027 // horizontal
7028 rbutton = group;
7029 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7030 gtk_container_add (GTK_CONTAINER (rbutton),
7031 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_HORIZONTAL));
7032 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7033 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
7035 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7036 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
7037 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
7039 // vertical
7040 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7041 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7042 gtk_container_add (GTK_CONTAINER (rbutton),
7043 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_VERTICAL));
7044 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7045 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
7047 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7048 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
7049 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
7050 gtk_toolbar_append_widget( tbl, row, "", "" );
7053 //watch selection
7054 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
7056 sigc::connection *c_selection_changed =
7057 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
7058 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
7059 pool->add_connection ("selection-changed", c_selection_changed);
7061 sigc::connection *c_selection_modified =
7062 new sigc::connection (sp_desktop_selection (desktop)->connectModified
7063 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
7064 pool->add_connection ("selection-modified", c_selection_modified);
7066 sigc::connection *c_subselection_changed =
7067 new sigc::connection (desktop->connectToolSubselectionChanged
7068 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
7069 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
7071 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
7074 gtk_widget_show_all( GTK_WIDGET(tbl) );
7076 return GTK_WIDGET(tbl);
7077 } // end of sp_text_toolbox_new()
7079 }//<unnamed> namespace
7082 //#########################
7083 //## Connector ##
7084 //#########################
7086 static void sp_connector_mode_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7087 {
7088 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7089 prefs->setBool("/tools/connector/mode",
7090 gtk_toggle_action_get_active( act ));
7091 }
7093 static void sp_connector_path_set_avoid(void)
7094 {
7095 cc_selection_set_avoid(true);
7096 }
7099 static void sp_connector_path_set_ignore(void)
7100 {
7101 cc_selection_set_avoid(false);
7102 }
7104 static void sp_connector_orthogonal_toggled( GtkToggleAction* act, GObject *tbl )
7105 {
7106 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7107 Inkscape::Selection * selection = sp_desktop_selection(desktop);
7108 SPDocument *doc = sp_desktop_document(desktop);
7110 if (!sp_document_get_undo_sensitive(doc))
7111 {
7112 return;
7113 }
7116 // quit if run by the _changed callbacks
7117 if (g_object_get_data( tbl, "freeze" )) {
7118 return;
7119 }
7121 // in turn, prevent callbacks from responding
7122 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7124 bool is_orthog = gtk_toggle_action_get_active( act );
7125 gchar orthog_str[] = "orthogonal";
7126 gchar polyline_str[] = "polyline";
7127 gchar *value = is_orthog ? orthog_str : polyline_str ;
7129 bool modmade = false;
7130 GSList *l = (GSList *) selection->itemList();
7131 while (l) {
7132 SPItem *item = (SPItem *) l->data;
7134 if (cc_item_is_connector(item)) {
7135 sp_object_setAttribute(item, "inkscape:connector-type",
7136 value, false);
7137 item->avoidRef->handleSettingChange();
7138 modmade = true;
7139 }
7140 l = l->next;
7141 }
7143 if (!modmade)
7144 {
7145 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7146 prefs->setBool("/tools/connector/orthogonal", is_orthog);
7147 }
7149 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7150 is_orthog ? _("Set connector type: orthogonal"): _("Set connector type: polyline"));
7152 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7153 }
7155 static void connector_curvature_changed(GtkAdjustment *adj, GObject* tbl)
7156 {
7157 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7158 Inkscape::Selection * selection = sp_desktop_selection(desktop);
7159 SPDocument *doc = sp_desktop_document(desktop);
7161 if (!sp_document_get_undo_sensitive(doc))
7162 {
7163 return;
7164 }
7167 // quit if run by the _changed callbacks
7168 if (g_object_get_data( tbl, "freeze" )) {
7169 return;
7170 }
7172 // in turn, prevent callbacks from responding
7173 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7175 gdouble newValue = gtk_adjustment_get_value(adj);
7176 gchar value[G_ASCII_DTOSTR_BUF_SIZE];
7177 g_ascii_dtostr(value, G_ASCII_DTOSTR_BUF_SIZE, newValue);
7179 bool modmade = false;
7180 GSList *l = (GSList *) selection->itemList();
7181 while (l) {
7182 SPItem *item = (SPItem *) l->data;
7184 if (cc_item_is_connector(item)) {
7185 sp_object_setAttribute(item, "inkscape:connector-curvature",
7186 value, false);
7187 item->avoidRef->handleSettingChange();
7188 modmade = true;
7189 }
7190 l = l->next;
7191 }
7193 if (!modmade)
7194 {
7195 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7196 prefs->setDouble(Glib::ustring("/tools/connector/curvature"), newValue);
7197 }
7199 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7200 _("Change connector curvature"));
7202 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7203 }
7206 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
7207 {
7208 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7209 SPDocument *doc = sp_desktop_document(desktop);
7211 if (!sp_document_get_undo_sensitive(doc))
7212 {
7213 return;
7214 }
7216 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7218 if ( !repr->attribute("inkscape:connector-spacing") &&
7219 ( adj->value == defaultConnSpacing )) {
7220 // Don't need to update the repr if the attribute doesn't
7221 // exist and it is being set to the default value -- as will
7222 // happen at startup.
7223 return;
7224 }
7226 // quit if run by the attr_changed listener
7227 if (g_object_get_data( tbl, "freeze" )) {
7228 return;
7229 }
7231 // in turn, prevent listener from responding
7232 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
7234 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
7235 SP_OBJECT(desktop->namedview)->updateRepr();
7237 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
7238 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
7239 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
7240 Geom::Matrix m = Geom::identity();
7241 avoid_item_move(&m, item);
7242 }
7244 if (items) {
7245 g_slist_free(items);
7246 }
7248 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7249 _("Change connector spacing"));
7251 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7252 }
7254 static void sp_connector_graph_layout(void)
7255 {
7256 if (!SP_ACTIVE_DESKTOP) return;
7257 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7259 // hack for clones, see comment in align-and-distribute.cpp
7260 int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7261 prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7263 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
7265 prefs->setInt("/options/clonecompensation/value", saved_compensation);
7267 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
7268 }
7270 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7271 {
7272 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7273 prefs->setBool("/tools/connector/directedlayout",
7274 gtk_toggle_action_get_active( act ));
7275 }
7277 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7278 {
7279 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7280 prefs->setBool("/tools/connector/avoidoverlaplayout",
7281 gtk_toggle_action_get_active( act ));
7282 }
7285 static void connector_length_changed(GtkAdjustment *adj, GObject* /*tbl*/)
7286 {
7287 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7288 prefs->setDouble("/tools/connector/length", adj->value);
7289 }
7291 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
7292 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
7293 bool /*is_interactive*/, gpointer data)
7294 {
7295 GtkWidget *tbl = GTK_WIDGET(data);
7297 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
7298 return;
7299 }
7300 if (strcmp(name, "inkscape:connector-spacing") == 0)
7301 {
7302 GtkAdjustment *adj = (GtkAdjustment*)
7303 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
7304 gdouble spacing = defaultConnSpacing;
7305 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
7307 gtk_adjustment_set_value(adj, spacing);
7308 gtk_adjustment_value_changed(adj);
7309 }
7311 spinbutton_defocus(GTK_OBJECT(tbl));
7312 }
7314 static void sp_connector_new_connection_point(GtkWidget *, GObject *tbl)
7315 {
7316 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7317 SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7319 if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE)
7320 cc_create_connection_point(cc);
7321 }
7323 static void sp_connector_remove_connection_point(GtkWidget *, GObject *tbl)
7324 {
7325 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7326 SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7328 if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE)
7329 cc_remove_connection_point(cc);
7330 }
7332 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
7333 NULL, /* child_added */
7334 NULL, /* child_removed */
7335 connector_tb_event_attr_changed,
7336 NULL, /* content_changed */
7337 NULL /* order_changed */
7338 };
7340 static void sp_connector_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
7341 {
7342 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "curvature" ) );
7343 GtkToggleAction *act = GTK_TOGGLE_ACTION( g_object_get_data( tbl, "orthogonal" ) );
7344 SPItem *item = selection->singleItem();
7345 if (SP_IS_PATH(item))
7346 {
7347 gdouble curvature = SP_PATH(item)->connEndPair.getCurvature();
7348 bool is_orthog = SP_PATH(item)->connEndPair.isOrthogonal();
7349 gtk_toggle_action_set_active(act, is_orthog);
7350 gtk_adjustment_set_value(adj, curvature);
7351 }
7353 }
7355 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
7356 {
7357 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7358 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
7360 // Editing mode toggle button
7361 {
7362 InkToggleAction* act = ink_toggle_action_new( "ConnectorEditModeAction",
7363 _("EditMode"),
7364 _("Switch between connection point editing and connector drawing mode"),
7365 INKSCAPE_ICON_CONNECTOR_EDIT,
7366 Inkscape::ICON_SIZE_DECORATION );
7367 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7369 bool tbuttonstate = prefs->getBool("/tools/connector/mode");
7370 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7371 g_object_set_data( holder, "mode", act );
7372 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_mode_toggled), holder );
7373 }
7376 {
7377 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
7378 _("Avoid"),
7379 _("Make connectors avoid selected objects"),
7380 INKSCAPE_ICON_CONNECTOR_AVOID,
7381 secondarySize );
7382 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
7383 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7384 }
7386 {
7387 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
7388 _("Ignore"),
7389 _("Make connectors ignore selected objects"),
7390 INKSCAPE_ICON_CONNECTOR_IGNORE,
7391 secondarySize );
7392 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
7393 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7394 }
7396 // Orthogonal connectors toggle button
7397 {
7398 InkToggleAction* act = ink_toggle_action_new( "ConnectorOrthogonalAction",
7399 _("Orthogonal"),
7400 _("Make connector orthogonal or polyline"),
7401 INKSCAPE_ICON_CONNECTOR_ORTHOGONAL,
7402 Inkscape::ICON_SIZE_DECORATION );
7403 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7405 bool tbuttonstate = prefs->getBool("/tools/connector/orthogonal");
7406 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7407 g_object_set_data( holder, "orthogonal", act );
7408 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_orthogonal_toggled), holder );
7409 }
7411 EgeAdjustmentAction* eact = 0;
7412 // Curvature spinbox
7413 eact = create_adjustment_action( "ConnectorCurvatureAction",
7414 _("Connector Curvature"), _("Curvature:"),
7415 _("The amount of connectors curvature"),
7416 "/tools/connector/curvature", defaultConnCurvature,
7417 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-curvature",
7418 0, 100, 1.0, 10.0,
7419 0, 0, 0,
7420 connector_curvature_changed, 1, 0 );
7421 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7423 // Spacing spinbox
7424 eact = create_adjustment_action( "ConnectorSpacingAction",
7425 _("Connector Spacing"), _("Spacing:"),
7426 _("The amount of space left around objects by auto-routing connectors"),
7427 "/tools/connector/spacing", defaultConnSpacing,
7428 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
7429 0, 100, 1.0, 10.0,
7430 0, 0, 0,
7431 connector_spacing_changed, 1, 0 );
7432 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7434 // Graph (connector network) layout
7435 {
7436 InkAction* inky = ink_action_new( "ConnectorGraphAction",
7437 _("Graph"),
7438 _("Nicely arrange selected connector network"),
7439 INKSCAPE_ICON_DISTRIBUTE_GRAPH,
7440 secondarySize );
7441 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
7442 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7443 }
7445 // Default connector length spinbox
7446 eact = create_adjustment_action( "ConnectorLengthAction",
7447 _("Connector Length"), _("Length:"),
7448 _("Ideal length for connectors when layout is applied"),
7449 "/tools/connector/length", 100,
7450 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
7451 10, 1000, 10.0, 100.0,
7452 0, 0, 0,
7453 connector_length_changed, 1, 0 );
7454 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7457 // Directed edges toggle button
7458 {
7459 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
7460 _("Downwards"),
7461 _("Make connectors with end-markers (arrows) point downwards"),
7462 INKSCAPE_ICON_DISTRIBUTE_GRAPH_DIRECTED,
7463 Inkscape::ICON_SIZE_DECORATION );
7464 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7466 bool tbuttonstate = prefs->getBool("/tools/connector/directedlayout");
7467 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7469 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
7470 sigc::connection *connection = new sigc::connection(sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_connector_toolbox_selection_changed), (GObject *)holder))
7471 );
7472 }
7474 // Avoid overlaps toggle button
7475 {
7476 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
7477 _("Remove overlaps"),
7478 _("Do not allow overlapping shapes"),
7479 INKSCAPE_ICON_DISTRIBUTE_REMOVE_OVERLAPS,
7480 Inkscape::ICON_SIZE_DECORATION );
7481 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7483 bool tbuttonstate = prefs->getBool("/tools/connector/avoidoverlaplayout");
7484 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), (tbuttonstate ? TRUE : FALSE ));
7486 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
7487 }
7490 // New connection point button
7491 {
7492 InkAction* inky = ink_action_new( "ConnectorNewConnPointAction",
7493 _("New connection point"),
7494 _("Add a new connection point to the currently selected item"),
7495 INKSCAPE_ICON_CONNECTOR_NEW_CONNPOINT,
7496 secondarySize );
7497 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_new_connection_point), holder );
7498 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7499 }
7501 // Remove selected connection point button
7503 {
7504 InkAction* inky = ink_action_new( "ConnectorRemoveConnPointAction",
7505 _("Remove connection point"),
7506 _("Remove the currently selected connection point"),
7507 INKSCAPE_ICON_CONNECTOR_REMOVE_CONNPOINT,
7508 secondarySize );
7509 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_remove_connection_point), holder );
7510 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7511 }
7514 // Code to watch for changes to the connector-spacing attribute in
7515 // the XML.
7516 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7517 g_assert(repr != NULL);
7519 purge_repr_listener( holder, holder );
7521 if (repr) {
7522 g_object_set_data( holder, "repr", repr );
7523 Inkscape::GC::anchor(repr);
7524 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
7525 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
7526 }
7527 } // end of sp_connector_toolbox_prep()
7530 //#########################
7531 //## Paintbucket ##
7532 //#########################
7534 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
7535 {
7536 gint channels = ege_select_one_action_get_active( act );
7537 flood_channels_set_channels( channels );
7538 }
7540 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
7541 {
7542 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7543 prefs->setInt("/tools/paintbucket/threshold", (gint)adj->value);
7544 }
7546 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
7547 {
7548 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7549 prefs->setBool("/tools/paintbucket/autogap", ege_select_one_action_get_active( act ));
7550 }
7552 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
7553 {
7554 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
7555 SPUnit const *unit = tracker->getActiveUnit();
7556 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7558 prefs->setDouble("/tools/paintbucket/offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
7559 prefs->setString("/tools/paintbucket/offsetunits", sp_unit_get_abbreviation(unit));
7560 }
7562 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
7563 {
7564 // FIXME: make defaults settable via Inkscape Options
7565 struct KeyValue {
7566 char const *key;
7567 double value;
7568 } const key_values[] = {
7569 {"threshold", 15},
7570 {"offset", 0.0}
7571 };
7573 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
7574 KeyValue const &kv = key_values[i];
7575 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
7576 if ( adj ) {
7577 gtk_adjustment_set_value(adj, kv.value);
7578 }
7579 }
7581 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
7582 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
7583 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
7584 ege_select_one_action_set_active( autogap_action, 0 );
7585 }
7587 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
7588 {
7589 EgeAdjustmentAction* eact = 0;
7590 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7592 {
7593 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7595 GList* items = 0;
7596 gint count = 0;
7597 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
7598 {
7599 GtkTreeIter iter;
7600 gtk_list_store_append( model, &iter );
7601 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7602 count++;
7603 }
7604 g_list_free( items );
7605 items = 0;
7606 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
7607 g_object_set( act1, "short_label", _("Fill by:"), NULL );
7608 ege_select_one_action_set_appearance( act1, "compact" );
7609 ege_select_one_action_set_active( act1, prefs->getInt("/tools/paintbucket/channels", 0) );
7610 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
7611 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
7612 g_object_set_data( holder, "channels_action", act1 );
7613 }
7615 // Spacing spinbox
7616 {
7617 eact = create_adjustment_action(
7618 "ThresholdAction",
7619 _("Fill Threshold"), _("Threshold:"),
7620 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
7621 "/tools/paintbucket/threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
7622 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
7623 0, 0, 0,
7624 paintbucket_threshold_changed, 1, 0 );
7626 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
7627 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7628 }
7630 // Create the units menu.
7631 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
7632 Glib::ustring stored_unit = prefs->getString("/tools/paintbucket/offsetunits");
7633 if (!stored_unit.empty())
7634 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit.data()));
7635 g_object_set_data( holder, "tracker", tracker );
7636 {
7637 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
7638 gtk_action_group_add_action( mainActions, act );
7639 }
7641 // Offset spinbox
7642 {
7643 eact = create_adjustment_action(
7644 "OffsetAction",
7645 _("Grow/shrink by"), _("Grow/shrink by:"),
7646 _("The amount to grow (positive) or shrink (negative) the created fill path"),
7647 "/tools/paintbucket/offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
7648 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
7649 0, 0, 0,
7650 paintbucket_offset_changed, 1, 2);
7651 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
7653 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7654 }
7656 /* Auto Gap */
7657 {
7658 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7660 GList* items = 0;
7661 gint count = 0;
7662 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
7663 {
7664 GtkTreeIter iter;
7665 gtk_list_store_append( model, &iter );
7666 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7667 count++;
7668 }
7669 g_list_free( items );
7670 items = 0;
7671 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
7672 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
7673 ege_select_one_action_set_appearance( act2, "compact" );
7674 ege_select_one_action_set_active( act2, prefs->getBool("/tools/paintbucket/autogap") );
7675 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
7676 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
7677 g_object_set_data( holder, "autogap_action", act2 );
7678 }
7680 /* Reset */
7681 {
7682 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
7683 _("Defaults"),
7684 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
7685 GTK_STOCK_CLEAR );
7686 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
7687 gtk_action_group_add_action( mainActions, act );
7688 gtk_action_set_sensitive( act, TRUE );
7689 }
7691 }
7693 /*
7694 Local Variables:
7695 mode:c++
7696 c-file-style:"stroustrup"
7697 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
7698 indent-tabs-mode:nil
7699 fill-column:99
7700 End:
7701 */
7702 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :