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