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 enum BarId {
110 BAR_TOOL = 0,
111 BAR_AUX,
112 BAR_COMMANDS,
113 BAR_SNAP,
114 };
116 #define BAR_ID_KEY "BarIdValue"
119 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
120 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
121 static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
122 static void sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
123 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
126 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
127 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
128 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
129 static void sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
130 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
131 static void sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
132 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
133 static void sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
134 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
135 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
136 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
138 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
140 using Inkscape::UI::ToolboxFactory;
143 Inkscape::IconSize ToolboxFactory::prefToSize( Glib::ustring const &path, int base ) {
144 static Inkscape::IconSize sizeChoices[] = {
145 Inkscape::ICON_SIZE_LARGE_TOOLBAR,
146 Inkscape::ICON_SIZE_SMALL_TOOLBAR,
147 Inkscape::ICON_SIZE_MENU
148 };
149 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
150 int index = prefs->getIntLimited( path, base, 0, G_N_ELEMENTS(sizeChoices) );
151 return sizeChoices[index];
152 }
154 static struct {
155 gchar const *type_name;
156 gchar const *data_name;
157 sp_verb_t verb;
158 sp_verb_t doubleclick_verb;
159 } const tools[] = {
160 { "SPSelectContext", "select_tool", SP_VERB_CONTEXT_SELECT, SP_VERB_CONTEXT_SELECT_PREFS},
161 { "SPNodeContext", "node_tool", SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
162 { "SPTweakContext", "tweak_tool", SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
163 { "SPSprayContext", "spray_tool", SP_VERB_CONTEXT_SPRAY, SP_VERB_CONTEXT_SPRAY_PREFS },
164 { "SPZoomContext", "zoom_tool", SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
165 { "SPRectContext", "rect_tool", SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
166 { "Box3DContext", "3dbox_tool", SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
167 { "SPArcContext", "arc_tool", SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
168 { "SPStarContext", "star_tool", SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
169 { "SPSpiralContext", "spiral_tool", SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
170 { "SPPencilContext", "pencil_tool", SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
171 { "SPPenContext", "pen_tool", SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
172 { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
173 { "SPLPEToolContext", "lpetool_tool", SP_VERB_CONTEXT_LPETOOL, SP_VERB_CONTEXT_LPETOOL_PREFS },
174 { "SPEraserContext", "eraser_tool", SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
175 { "SPFloodContext", "paintbucket_tool", SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
176 { "SPTextContext", "text_tool", SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
177 { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
178 { "SPGradientContext", "gradient_tool", SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
179 { "SPDropperContext", "dropper_tool", SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
180 { NULL, NULL, 0, 0 }
181 };
183 static struct {
184 gchar const *type_name;
185 gchar const *data_name;
186 GtkWidget *(*create_func)(SPDesktop *desktop);
187 void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
188 gchar const *ui_name;
189 gint swatch_verb_id;
190 gchar const *swatch_tool;
191 gchar const *swatch_tip;
192 } const aux_toolboxes[] = {
193 { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep, "SelectToolbar",
194 SP_VERB_INVALID, 0, 0},
195 { "SPNodeContext", "node_toolbox", 0, sp_node_toolbox_prep, "NodeToolbar",
196 SP_VERB_INVALID, 0, 0},
197 { "SPTweakContext", "tweak_toolbox", 0, sp_tweak_toolbox_prep, "TweakToolbar",
198 SP_VERB_CONTEXT_TWEAK_PREFS, "/tools/tweak", N_("Color/opacity used for color tweaking")},
199 { "SPSprayContext", "spray_toolbox", 0, sp_spray_toolbox_prep, "SprayToolbar",
200 SP_VERB_CONTEXT_SPRAY_PREFS, "/tools/spray", N_("Color/opacity used for color spraying")},
201 { "SPZoomContext", "zoom_toolbox", 0, sp_zoom_toolbox_prep, "ZoomToolbar",
202 SP_VERB_INVALID, 0, 0},
203 { "SPStarContext", "star_toolbox", 0, sp_star_toolbox_prep, "StarToolbar",
204 SP_VERB_CONTEXT_STAR_PREFS, "/tools/shapes/star", N_("Style of new stars")},
205 { "SPRectContext", "rect_toolbox", 0, sp_rect_toolbox_prep, "RectToolbar",
206 SP_VERB_CONTEXT_RECT_PREFS, "/tools/shapes/rect", N_("Style of new rectangles")},
207 { "Box3DContext", "3dbox_toolbox", 0, box3d_toolbox_prep, "3DBoxToolbar",
208 SP_VERB_CONTEXT_3DBOX_PREFS, "/tools/shapes/3dbox", N_("Style of new 3D boxes")},
209 { "SPArcContext", "arc_toolbox", 0, sp_arc_toolbox_prep, "ArcToolbar",
210 SP_VERB_CONTEXT_ARC_PREFS, "/tools/shapes/arc", N_("Style of new ellipses")},
211 { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep, "SpiralToolbar",
212 SP_VERB_CONTEXT_SPIRAL_PREFS, "/tools/shapes/spiral", N_("Style of new spirals")},
213 { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep, "PencilToolbar",
214 SP_VERB_CONTEXT_PENCIL_PREFS, "/tools/freehand/pencil", N_("Style of new paths created by Pencil")},
215 { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep, "PenToolbar",
216 SP_VERB_CONTEXT_PEN_PREFS, "/tools/freehand/pen", N_("Style of new paths created by Pen")},
217 { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
218 SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "/tools/calligraphic", N_("Style of new calligraphic strokes")},
219 { "SPEraserContext", "eraser_toolbox", 0, sp_eraser_toolbox_prep,"EraserToolbar",
220 SP_VERB_CONTEXT_ERASER_PREFS, "/tools/eraser", _("TBD")},
221 { "SPLPEToolContext", "lpetool_toolbox", 0, sp_lpetool_toolbox_prep, "LPEToolToolbar",
222 SP_VERB_CONTEXT_LPETOOL_PREFS, "/tools/lpetool", _("TBD")},
223 { "SPTextContext", "text_toolbox", sp_text_toolbox_new, 0, 0,
224 SP_VERB_INVALID, 0, 0},
225 { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep, "DropperToolbar",
226 SP_VERB_INVALID, 0, 0},
227 { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0, 0,
228 SP_VERB_INVALID, 0, 0},
229 { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep, "ConnectorToolbar",
230 SP_VERB_INVALID, 0, 0},
231 { "SPFloodContext", "paintbucket_toolbox", 0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
232 SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "/tools/paintbucket", N_("Style of Paint Bucket fill objects")},
233 { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
234 };
236 #define TOOLBAR_SLIDER_HINT "full"
238 static gchar const * ui_descr =
239 "<ui>"
240 " <toolbar name='SelectToolbar'>"
241 " <toolitem action='EditSelectAll' />"
242 " <toolitem action='EditSelectAllInAllLayers' />"
243 " <toolitem action='EditDeselect' />"
244 " <separator />"
245 " <toolitem action='ObjectRotate90CCW' />"
246 " <toolitem action='ObjectRotate90' />"
247 " <toolitem action='ObjectFlipHorizontally' />"
248 " <toolitem action='ObjectFlipVertically' />"
249 " <separator />"
250 " <toolitem action='SelectionToBack' />"
251 " <toolitem action='SelectionLower' />"
252 " <toolitem action='SelectionRaise' />"
253 " <toolitem action='SelectionToFront' />"
254 " <separator />"
255 " <toolitem action='XAction' />"
256 " <toolitem action='YAction' />"
257 " <toolitem action='WidthAction' />"
258 " <toolitem action='LockAction' />"
259 " <toolitem action='HeightAction' />"
260 " <toolitem action='UnitsAction' />"
261 " <separator />"
262 " <toolitem action='transform_affect_label' />"
263 " <toolitem action='transform_stroke' />"
264 " <toolitem action='transform_corners' />"
265 " <toolitem action='transform_gradient' />"
266 " <toolitem action='transform_pattern' />"
267 " </toolbar>"
269 " <toolbar name='NodeToolbar'>"
270 " <toolitem action='NodeInsertAction' />"
271 " <toolitem action='NodeDeleteAction' />"
272 " <separator />"
273 " <toolitem action='NodeJoinAction' />"
274 " <toolitem action='NodeBreakAction' />"
275 " <separator />"
276 " <toolitem action='NodeJoinSegmentAction' />"
277 " <toolitem action='NodeDeleteSegmentAction' />"
278 " <separator />"
279 " <toolitem action='NodeCuspAction' />"
280 " <toolitem action='NodeSmoothAction' />"
281 " <toolitem action='NodeSymmetricAction' />"
282 " <toolitem action='NodeAutoAction' />"
283 " <separator />"
284 " <toolitem action='NodeLineAction' />"
285 " <toolitem action='NodeCurveAction' />"
286 " <separator />"
287 " <toolitem action='ObjectToPath' />"
288 " <toolitem action='StrokeToPath' />"
289 " <separator />"
290 " <toolitem action='NodeXAction' />"
291 " <toolitem action='NodeYAction' />"
292 " <toolitem action='NodeUnitsAction' />"
293 " <separator />"
294 " <toolitem action='ObjectEditClipPathAction' />"
295 " <toolitem action='ObjectEditMaskPathAction' />"
296 " <toolitem action='EditNextLPEParameterAction' />"
297 " <separator />"
298 " <toolitem action='NodesShowHandlesAction' />"
299 " <toolitem action='NodesShowHelperpath' />"
300 " </toolbar>"
302 " <toolbar name='TweakToolbar'>"
303 " <toolitem action='TweakWidthAction' />"
304 " <separator />"
305 " <toolitem action='TweakForceAction' />"
306 " <toolitem action='TweakPressureAction' />"
307 " <separator />"
308 " <toolitem action='TweakModeAction' />"
309 " <separator />"
310 " <toolitem action='TweakFidelityAction' />"
311 " <separator />"
312 " <toolitem action='TweakChannelsLabel' />"
313 " <toolitem action='TweakDoH' />"
314 " <toolitem action='TweakDoS' />"
315 " <toolitem action='TweakDoL' />"
316 " <toolitem action='TweakDoO' />"
317 " </toolbar>"
319 " <toolbar name='SprayToolbar'>"
320 " <toolitem action='SprayModeAction' />"
321 " <separator />"
322 " <separator />"
323 " <toolitem action='SprayWidthAction' />"
324 " <toolitem action='SprayPressureAction' />"
325 " <toolitem action='SprayPopulationAction' />"
326 " <separator />"
327 " <toolitem action='SprayRotationAction' />"
328 " <toolitem action='SprayScaleAction' />"
329 " <separator />"
330 " <toolitem action='SprayStandard_deviationAction' />"
331 " <toolitem action='SprayMeanAction' />"
332 " </toolbar>"
334 " <toolbar name='ZoomToolbar'>"
335 " <toolitem action='ZoomIn' />"
336 " <toolitem action='ZoomOut' />"
337 " <separator />"
338 " <toolitem action='Zoom1:0' />"
339 " <toolitem action='Zoom1:2' />"
340 " <toolitem action='Zoom2:1' />"
341 " <separator />"
342 " <toolitem action='ZoomSelection' />"
343 " <toolitem action='ZoomDrawing' />"
344 " <toolitem action='ZoomPage' />"
345 " <toolitem action='ZoomPageWidth' />"
346 " <separator />"
347 " <toolitem action='ZoomPrev' />"
348 " <toolitem action='ZoomNext' />"
349 " </toolbar>"
351 " <toolbar name='StarToolbar'>"
352 " <separator />"
353 " <toolitem action='StarStateAction' />"
354 " <separator />"
355 " <toolitem action='FlatAction' />"
356 " <separator />"
357 " <toolitem action='MagnitudeAction' />"
358 " <toolitem action='SpokeAction' />"
359 " <toolitem action='RoundednessAction' />"
360 " <toolitem action='RandomizationAction' />"
361 " <separator />"
362 " <toolitem action='StarResetAction' />"
363 " </toolbar>"
365 " <toolbar name='RectToolbar'>"
366 " <toolitem action='RectStateAction' />"
367 " <toolitem action='RectWidthAction' />"
368 " <toolitem action='RectHeightAction' />"
369 " <toolitem action='RadiusXAction' />"
370 " <toolitem action='RadiusYAction' />"
371 " <toolitem action='RectUnitsAction' />"
372 " <separator />"
373 " <toolitem action='RectResetAction' />"
374 " </toolbar>"
376 " <toolbar name='3DBoxToolbar'>"
377 " <toolitem action='3DBoxAngleXAction' />"
378 " <toolitem action='3DBoxVPXStateAction' />"
379 " <separator />"
380 " <toolitem action='3DBoxAngleYAction' />"
381 " <toolitem action='3DBoxVPYStateAction' />"
382 " <separator />"
383 " <toolitem action='3DBoxAngleZAction' />"
384 " <toolitem action='3DBoxVPZStateAction' />"
385 " </toolbar>"
387 " <toolbar name='SpiralToolbar'>"
388 " <toolitem action='SpiralStateAction' />"
389 " <toolitem action='SpiralRevolutionAction' />"
390 " <toolitem action='SpiralExpansionAction' />"
391 " <toolitem action='SpiralT0Action' />"
392 " <separator />"
393 " <toolitem action='SpiralResetAction' />"
394 " </toolbar>"
396 " <toolbar name='PenToolbar'>"
397 " <toolitem action='FreehandModeActionPen' />"
398 " <separator />"
399 " <toolitem action='SetPenShapeAction'/>"
400 " </toolbar>"
402 " <toolbar name='PencilToolbar'>"
403 " <toolitem action='FreehandModeActionPencil' />"
404 " <separator />"
405 " <toolitem action='PencilToleranceAction' />"
406 " <separator />"
407 " <toolitem action='PencilResetAction' />"
408 " <separator />"
409 " <toolitem action='SetPencilShapeAction'/>"
410 " </toolbar>"
412 " <toolbar name='CalligraphyToolbar'>"
413 " <separator />"
414 " <toolitem action='SetProfileAction'/>"
415 " <separator />"
416 " <toolitem action='CalligraphyWidthAction' />"
417 " <toolitem action='PressureAction' />"
418 " <toolitem action='TraceAction' />"
419 " <toolitem action='ThinningAction' />"
420 " <separator />"
421 " <toolitem action='AngleAction' />"
422 " <toolitem action='TiltAction' />"
423 " <toolitem action='FixationAction' />"
424 " <separator />"
425 " <toolitem action='CapRoundingAction' />"
426 " <separator />"
427 " <toolitem action='TremorAction' />"
428 " <toolitem action='WiggleAction' />"
429 " <toolitem action='MassAction' />"
430 " <separator />"
431 " </toolbar>"
433 " <toolbar name='ArcToolbar'>"
434 " <toolitem action='ArcStateAction' />"
435 " <separator />"
436 " <toolitem action='ArcStartAction' />"
437 " <toolitem action='ArcEndAction' />"
438 " <separator />"
439 " <toolitem action='ArcOpenAction' />"
440 " <separator />"
441 " <toolitem action='ArcResetAction' />"
442 " <separator />"
443 " </toolbar>"
445 " <toolbar name='PaintbucketToolbar'>"
446 " <toolitem action='ChannelsAction' />"
447 " <separator />"
448 " <toolitem action='ThresholdAction' />"
449 " <separator />"
450 " <toolitem action='OffsetAction' />"
451 " <toolitem action='PaintbucketUnitsAction' />"
452 " <separator />"
453 " <toolitem action='AutoGapAction' />"
454 " <separator />"
455 " <toolitem action='PaintbucketResetAction' />"
456 " </toolbar>"
458 " <toolbar name='EraserToolbar'>"
459 " <toolitem action='EraserWidthAction' />"
460 " <separator />"
461 " <toolitem action='EraserModeAction' />"
462 " </toolbar>"
464 " <toolbar name='LPEToolToolbar'>"
465 " <toolitem action='LPEToolModeAction' />"
466 " <separator />"
467 " <toolitem action='LPEShowBBoxAction' />"
468 " <toolitem action='LPEBBoxFromSelectionAction' />"
469 " <separator />"
470 " <toolitem action='LPELineSegmentAction' />"
471 " <separator />"
472 " <toolitem action='LPEMeasuringAction' />"
473 " <toolitem action='LPEToolUnitsAction' />"
474 " <separator />"
475 " <toolitem action='LPEOpenLPEDialogAction' />"
476 " </toolbar>"
478 " <toolbar name='DropperToolbar'>"
479 " <toolitem action='DropperOpacityAction' />"
480 " <toolitem action='DropperPickAlphaAction' />"
481 " <toolitem action='DropperSetAlphaAction' />"
482 " </toolbar>"
484 " <toolbar name='ConnectorToolbar'>"
485 " <toolitem action='ConnectorEditModeAction' />"
486 " <toolitem action='ConnectorAvoidAction' />"
487 " <toolitem action='ConnectorIgnoreAction' />"
488 " <toolitem action='ConnectorOrthogonalAction' />"
489 " <toolitem action='ConnectorCurvatureAction' />"
490 " <toolitem action='ConnectorSpacingAction' />"
491 " <toolitem action='ConnectorGraphAction' />"
492 " <toolitem action='ConnectorLengthAction' />"
493 " <toolitem action='ConnectorDirectedAction' />"
494 " <toolitem action='ConnectorOverlapAction' />"
495 " <toolitem action='ConnectorNewConnPointAction' />"
496 " <toolitem action='ConnectorRemoveConnPointAction' />"
497 " </toolbar>"
499 "</ui>"
500 ;
502 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
504 void setup_snap_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
506 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
507 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
509 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
510 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
512 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
513 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
515 GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick( GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
516 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
517 Inkscape::UI::View::View *view, GtkTooltips *tt);
519 class VerbAction : public Gtk::Action {
520 public:
521 static Glib::RefPtr<VerbAction> create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
523 virtual ~VerbAction();
524 virtual void set_active(bool active = true);
526 protected:
527 virtual Gtk::Widget* create_menu_item_vfunc();
528 virtual Gtk::Widget* create_tool_item_vfunc();
530 virtual void connect_proxy_vfunc(Gtk::Widget* proxy);
531 virtual void disconnect_proxy_vfunc(Gtk::Widget* proxy);
533 virtual void on_activate();
535 private:
536 Inkscape::Verb* verb;
537 Inkscape::Verb* verb2;
538 Inkscape::UI::View::View *view;
539 GtkTooltips *tooltips;
540 bool active;
542 VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
543 };
546 Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips)
547 {
548 Glib::RefPtr<VerbAction> result;
549 SPAction *action = verb->get_action(view);
550 if ( action ) {
551 //SPAction* action2 = verb2 ? verb2->get_action(view) : 0;
552 result = Glib::RefPtr<VerbAction>(new VerbAction(verb, verb2, view, tooltips));
553 }
555 return result;
556 }
558 VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips) :
559 Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(verb->get_image()), Glib::ustring(_(verb->get_name())), Glib::ustring(_(verb->get_tip()))),
560 verb(verb),
561 verb2(verb2),
562 view(view),
563 tooltips(tooltips),
564 active(false)
565 {
566 }
568 VerbAction::~VerbAction()
569 {
570 }
572 Gtk::Widget* VerbAction::create_menu_item_vfunc()
573 {
574 // First call in to get the icon rendered if present in SVG
575 Gtk::Widget *widget = sp_icon_get_icon( property_stock_id().get_value().get_string(), Inkscape::ICON_SIZE_MENU );
576 delete widget;
577 widget = 0;
579 Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
580 // g_message("create_menu_item_vfunc() = %p for '%s'", widg, verb->get_id());
581 return widg;
582 }
584 Gtk::Widget* VerbAction::create_tool_item_vfunc()
585 {
586 // Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
587 Inkscape::IconSize toolboxSize = ToolboxFactory::prefToSize("/toolbox/tools/small");
588 GtkWidget* toolbox = 0;
589 GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
590 SP_BUTTON_TYPE_TOGGLE,
591 verb,
592 verb2,
593 view,
594 tooltips );
595 if ( active ) {
596 sp_button_toggle_set_down( SP_BUTTON(button), active);
597 }
598 gtk_widget_show_all( button );
599 Gtk::Widget* wrapped = Glib::wrap(button);
600 Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
601 holder->add(*wrapped);
603 // g_message("create_tool_item_vfunc() = %p for '%s'", holder, verb->get_id());
604 return holder;
605 }
607 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
608 {
609 // g_message("connect_proxy_vfunc(%p) for '%s'", proxy, verb->get_id());
610 Gtk::Action::connect_proxy_vfunc(proxy);
611 }
613 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
614 {
615 // g_message("disconnect_proxy_vfunc(%p) for '%s'", proxy, verb->get_id());
616 Gtk::Action::disconnect_proxy_vfunc(proxy);
617 }
619 void VerbAction::set_active(bool active)
620 {
621 this->active = active;
622 Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
623 for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
624 Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
625 if (ti) {
626 // *should* have one child that is the SPButton
627 Gtk::Widget* child = ti->get_child();
628 if ( child && SP_IS_BUTTON(child->gobj()) ) {
629 SPButton* button = SP_BUTTON(child->gobj());
630 sp_button_toggle_set_down( button, active );
631 }
632 }
633 }
634 }
636 void VerbAction::on_activate()
637 {
638 if ( verb ) {
639 SPAction *action = verb->get_action(view);
640 if ( action ) {
641 sp_action_perform(action, 0);
642 }
643 }
644 }
646 /* Global text entry widgets necessary for update */
647 /* GtkWidget *dropper_rgb_entry,
648 *dropper_opacity_entry ; */
649 // should be made a private member once this is converted to class
651 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
652 connection->disconnect();
653 delete connection;
654 }
656 static void purge_repr_listener( GObject* obj, GObject* tbl )
657 {
658 (void)obj;
659 Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
660 if (oldrepr) { // remove old listener
661 sp_repr_remove_listener_by_data(oldrepr, tbl);
662 Inkscape::GC::release(oldrepr);
663 oldrepr = 0;
664 g_object_set_data( tbl, "repr", NULL );
665 }
666 }
668 GtkWidget *
669 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
670 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
671 Inkscape::UI::View::View *view, GtkTooltips *tt)
672 {
673 SPAction *action = verb->get_action(view);
674 if (!action) return NULL;
676 SPAction *doubleclick_action;
677 if (doubleclick_verb)
678 doubleclick_action = doubleclick_verb->get_action(view);
679 else
680 doubleclick_action = NULL;
682 /* fixme: Handle sensitive/unsensitive */
683 /* fixme: Implement sp_button_new_from_action */
684 GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
685 gtk_widget_show(b);
688 unsigned int shortcut = sp_shortcut_get_primary(verb);
689 if (shortcut) {
690 gchar key[256];
691 sp_ui_shortcut_string(shortcut, key);
692 gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
693 if ( t ) {
694 gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
695 }
696 g_free(tip);
697 } else {
698 if ( t ) {
699 gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
700 }
701 }
703 return b;
704 }
707 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
708 {
709 SPAction* targetAction = SP_ACTION(user_data);
710 if ( targetAction ) {
711 sp_action_perform( targetAction, NULL );
712 }
713 }
715 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
716 {
717 if ( data ) {
718 GtkAction* act = GTK_ACTION(data);
719 gtk_action_set_sensitive( act, sensitive );
720 }
721 }
723 static SPActionEventVector action_event_vector = {
724 {NULL},
725 NULL,
726 NULL,
727 sp_action_action_set_sensitive,
728 NULL,
729 NULL
730 };
732 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
733 {
734 GtkAction* act = 0;
736 SPAction* targetAction = verb->get_action(view);
737 InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size );
738 act = GTK_ACTION(inky);
739 gtk_action_set_sensitive( act, targetAction->sensitive );
741 g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
743 SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
744 nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
746 return act;
747 }
749 Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
750 {
751 Inkscape::UI::View::View *view = desktop;
752 gint verbsToUse[] = {
753 // disabled until we have icons for them:
754 //find
755 //SP_VERB_EDIT_TILE,
756 //SP_VERB_EDIT_UNTILE,
757 SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
758 SP_VERB_DIALOG_DISPLAY,
759 SP_VERB_DIALOG_FILL_STROKE,
760 SP_VERB_DIALOG_NAMEDVIEW,
761 SP_VERB_DIALOG_TEXT,
762 SP_VERB_DIALOG_XML_EDITOR,
763 SP_VERB_DIALOG_LAYERS,
764 SP_VERB_EDIT_CLONE,
765 SP_VERB_EDIT_COPY,
766 SP_VERB_EDIT_CUT,
767 SP_VERB_EDIT_DUPLICATE,
768 SP_VERB_EDIT_PASTE,
769 SP_VERB_EDIT_REDO,
770 SP_VERB_EDIT_UNDO,
771 SP_VERB_EDIT_UNLINK_CLONE,
772 SP_VERB_FILE_EXPORT,
773 SP_VERB_FILE_IMPORT,
774 SP_VERB_FILE_NEW,
775 SP_VERB_FILE_OPEN,
776 SP_VERB_FILE_PRINT,
777 SP_VERB_FILE_SAVE,
778 SP_VERB_OBJECT_TO_CURVE,
779 SP_VERB_SELECTION_GROUP,
780 SP_VERB_SELECTION_OUTLINE,
781 SP_VERB_SELECTION_UNGROUP,
782 SP_VERB_ZOOM_1_1,
783 SP_VERB_ZOOM_1_2,
784 SP_VERB_ZOOM_2_1,
785 SP_VERB_ZOOM_DRAWING,
786 SP_VERB_ZOOM_IN,
787 SP_VERB_ZOOM_NEXT,
788 SP_VERB_ZOOM_OUT,
789 SP_VERB_ZOOM_PAGE,
790 SP_VERB_ZOOM_PAGE_WIDTH,
791 SP_VERB_ZOOM_PREV,
792 SP_VERB_ZOOM_SELECTION,
793 };
795 Inkscape::IconSize toolboxSize = ToolboxFactory::prefToSize("/toolbox/small");
797 static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
798 Glib::RefPtr<Gtk::ActionGroup> mainActions;
799 if ( groups.find(desktop) != groups.end() ) {
800 mainActions = groups[desktop];
801 }
803 if ( !mainActions ) {
804 mainActions = Gtk::ActionGroup::create("main");
805 groups[desktop] = mainActions;
806 }
808 for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
809 Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
810 if ( verb ) {
811 if (!mainActions->get_action(verb->get_id())) {
812 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
813 mainActions->add(Glib::wrap(act));
814 }
815 }
816 }
818 if ( !mainActions->get_action("ToolZoom") ) {
819 GtkTooltips *tt = gtk_tooltips_new();
820 for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
821 Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
822 if ( va ) {
823 mainActions->add(va);
824 if ( i == 0 ) {
825 va->set_active(true);
826 }
827 }
828 }
829 }
832 return mainActions;
833 }
836 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
837 {
838 gtk_widget_set_size_request( widget,
839 widget->allocation.width,
840 widget->allocation.height );
841 }
843 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
844 {
845 gtk_widget_set_size_request( widget, -1, -1 );
846 }
848 static GtkWidget* toolboxNewCommon( GtkWidget* tb, BarId id, GtkPositionType handlePos )
849 {
850 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
852 gtk_widget_set_sensitive(tb, FALSE);
854 GtkWidget *hb = gtk_handle_box_new();
855 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), handlePos);
856 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
857 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
859 gtk_container_add(GTK_CONTAINER(hb), tb);
860 gtk_widget_show(GTK_WIDGET(tb));
862 sigc::connection* conn = new sigc::connection;
863 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
865 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
866 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
868 gpointer val = GINT_TO_POINTER(id);
869 g_object_set_data(G_OBJECT(hb), BAR_ID_KEY, val);
871 return hb;
872 }
874 GtkWidget *ToolboxFactory::createToolToolbox()
875 {
876 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
878 return toolboxNewCommon( tb, BAR_TOOL, GTK_POS_TOP );
879 }
881 GtkWidget *ToolboxFactory::createAuxToolbox()
882 {
883 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
885 return toolboxNewCommon( tb, BAR_AUX, GTK_POS_LEFT );
886 }
888 //####################################
889 //# Commands Bar
890 //####################################
892 GtkWidget *ToolboxFactory::createCommandsToolbox()
893 {
894 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
896 return toolboxNewCommon( tb, BAR_COMMANDS, GTK_POS_LEFT );
897 }
899 GtkWidget *ToolboxFactory::createSnapToolbox()
900 {
901 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
903 return toolboxNewCommon( tb, BAR_SNAP, GTK_POS_LEFT );
904 }
906 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
907 gchar const *label, gchar const *shortLabel, gchar const *tooltip,
908 Glib::ustring const &path, gdouble def,
909 GtkWidget *focusTarget,
910 GtkWidget *us,
911 GObject *dataKludge,
912 gboolean altx, gchar const *altx_mark,
913 gdouble lower, gdouble upper, gdouble step, gdouble page,
914 gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
915 void (*callback)(GtkAdjustment *, GObject *),
916 gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
917 {
918 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
919 GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs->getDouble(path, def) * factor,
920 lower, upper, step, page, 0 ) );
921 if (us) {
922 sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
923 }
925 gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
927 EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
928 if ( shortLabel ) {
929 g_object_set( act, "short_label", shortLabel, NULL );
930 }
932 if ( (descrCount > 0) && descrLabels && descrValues ) {
933 ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
934 }
936 if ( focusTarget ) {
937 ege_adjustment_action_set_focuswidget( act, focusTarget );
938 }
940 if ( altx && altx_mark ) {
941 g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
942 }
944 if ( dataKludge ) {
945 // Rather lame, but it's the only place where we need to get the entry name
946 // but we don't have an Entry
947 g_object_set_data( dataKludge, prefs->getEntry(path).getEntryName().data(), adj );
948 }
950 // Using a cast just to make sure we pass in the right kind of function pointer
951 g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
953 return act;
954 }
957 //####################################
958 //# node editing callbacks
959 //####################################
961 /**
962 * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
963 */
964 static ShapeEditor *get_current_shape_editor()
965 {
966 if (!SP_ACTIVE_DESKTOP) {
967 return NULL;
968 }
970 SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
972 if (!SP_IS_NODE_CONTEXT(event_context)) {
973 return NULL;
974 }
976 return event_context->shape_editor;
977 }
980 void
981 sp_node_path_edit_add(void)
982 {
983 ShapeEditor *shape_editor = get_current_shape_editor();
984 if (shape_editor) shape_editor->add_node();
985 }
987 void
988 sp_node_path_edit_delete(void)
989 {
990 ShapeEditor *shape_editor = get_current_shape_editor();
991 if (shape_editor) shape_editor->delete_nodes_preserving_shape();
992 }
994 void
995 sp_node_path_edit_delete_segment(void)
996 {
997 ShapeEditor *shape_editor = get_current_shape_editor();
998 if (shape_editor) shape_editor->delete_segment();
999 }
1001 void
1002 sp_node_path_edit_break(void)
1003 {
1004 ShapeEditor *shape_editor = get_current_shape_editor();
1005 if (shape_editor) shape_editor->break_at_nodes();
1006 }
1008 void
1009 sp_node_path_edit_join(void)
1010 {
1011 ShapeEditor *shape_editor = get_current_shape_editor();
1012 if (shape_editor) shape_editor->join_nodes();
1013 }
1015 void
1016 sp_node_path_edit_join_segment(void)
1017 {
1018 ShapeEditor *shape_editor = get_current_shape_editor();
1019 if (shape_editor) shape_editor->join_segments();
1020 }
1022 void
1023 sp_node_path_edit_toline(void)
1024 {
1025 ShapeEditor *shape_editor = get_current_shape_editor();
1026 if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1027 }
1029 void
1030 sp_node_path_edit_tocurve(void)
1031 {
1032 ShapeEditor *shape_editor = get_current_shape_editor();
1033 if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1034 }
1036 void
1037 sp_node_path_edit_cusp(void)
1038 {
1039 ShapeEditor *shape_editor = get_current_shape_editor();
1040 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1041 }
1043 void
1044 sp_node_path_edit_smooth(void)
1045 {
1046 ShapeEditor *shape_editor = get_current_shape_editor();
1047 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1048 }
1050 void
1051 sp_node_path_edit_symmetrical(void)
1052 {
1053 ShapeEditor *shape_editor = get_current_shape_editor();
1054 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1055 }
1057 void
1058 sp_node_path_edit_auto(void)
1059 {
1060 ShapeEditor *shape_editor = get_current_shape_editor();
1061 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_AUTO);
1062 }
1064 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1065 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1066 bool show = gtk_toggle_action_get_active( act );
1067 prefs->setBool("/tools/nodes/show_handles", show);
1068 ShapeEditor *shape_editor = get_current_shape_editor();
1069 if (shape_editor) shape_editor->show_handles(show);
1070 }
1072 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1073 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1074 bool show = gtk_toggle_action_get_active( act );
1075 prefs->setBool("/tools/nodes/show_helperpath", show);
1076 ShapeEditor *shape_editor = get_current_shape_editor();
1077 if (shape_editor) shape_editor->show_helperpath(show);
1078 }
1080 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1081 sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1082 }
1084 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1085 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1086 }
1088 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1089 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1090 }
1092 /* is called when the node selection is modified */
1093 static void
1094 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1095 {
1096 GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1097 GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1098 GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1099 GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1101 // quit if run by the attr_changed listener
1102 if (g_object_get_data( tbl, "freeze" )) {
1103 return;
1104 }
1106 // in turn, prevent listener from responding
1107 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1109 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1110 SPUnit const *unit = tracker->getActiveUnit();
1112 ShapeEditor *shape_editor = get_current_shape_editor();
1113 if (shape_editor && shape_editor->has_nodepath()) {
1114 Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1115 int n_selected = 0;
1116 if (nodepath) {
1117 n_selected = nodepath->numSelected();
1118 }
1120 if (n_selected == 0) {
1121 gtk_action_set_sensitive(xact, FALSE);
1122 gtk_action_set_sensitive(yact, FALSE);
1123 } else {
1124 gtk_action_set_sensitive(xact, TRUE);
1125 gtk_action_set_sensitive(yact, TRUE);
1126 Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1127 Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1129 if (n_selected == 1) {
1130 Geom::Point sel_node = nodepath->singleSelectedCoords();
1131 if (oldx != sel_node[Geom::X] || oldy != sel_node[Geom::Y]) {
1132 gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[Geom::X], *unit));
1133 gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[Geom::Y], *unit));
1134 }
1135 } else {
1136 boost::optional<Geom::Coord> x = sp_node_selected_common_coord(nodepath, Geom::X);
1137 boost::optional<Geom::Coord> y = sp_node_selected_common_coord(nodepath, Geom::Y);
1138 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1139 /* Note: Currently x and y will always have a value, even if the coordinates of the
1140 selected nodes don't coincide (in this case we use the coordinates of the center
1141 of the bounding box). So the entries are never set to zero. */
1142 // FIXME: Maybe we should clear the entry if several nodes are selected
1143 // instead of providing a kind of average value
1144 gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1145 gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1146 }
1147 }
1148 }
1149 } else {
1150 // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1151 gtk_action_set_sensitive(xact, FALSE);
1152 gtk_action_set_sensitive(yact, FALSE);
1153 }
1155 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1156 }
1158 static void
1159 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1160 {
1161 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1162 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1164 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1165 SPUnit const *unit = tracker->getActiveUnit();
1167 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1168 prefs->setDouble(Glib::ustring("/tools/nodes/") + value_name, sp_units_get_pixels(adj->value, *unit));
1169 }
1171 // quit if run by the attr_changed listener
1172 if (g_object_get_data( tbl, "freeze" )) {
1173 return;
1174 }
1176 // in turn, prevent listener from responding
1177 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1179 ShapeEditor *shape_editor = get_current_shape_editor();
1180 if (shape_editor && shape_editor->has_nodepath()) {
1181 double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1182 if (!strcmp(value_name, "x")) {
1183 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::X);
1184 }
1185 if (!strcmp(value_name, "y")) {
1186 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::Y);
1187 }
1188 }
1190 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1191 }
1193 static void
1194 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1195 {
1196 sp_node_path_value_changed(adj, tbl, "x");
1197 }
1199 static void
1200 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1201 {
1202 sp_node_path_value_changed(adj, tbl, "y");
1203 }
1205 void
1206 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1207 {
1208 {
1209 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1210 SPItem *item = selection->singleItem();
1211 if (item && SP_IS_LPE_ITEM(item)) {
1212 if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1213 gtk_action_set_sensitive(w, TRUE);
1214 } else {
1215 gtk_action_set_sensitive(w, FALSE);
1216 }
1217 } else {
1218 gtk_action_set_sensitive(w, FALSE);
1219 }
1220 }
1222 {
1223 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1224 SPItem *item = selection->singleItem();
1225 if (item && item->clip_ref && item->clip_ref->getObject()) {
1226 gtk_action_set_sensitive(w, TRUE);
1227 } else {
1228 gtk_action_set_sensitive(w, FALSE);
1229 }
1230 }
1232 {
1233 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1234 SPItem *item = selection->singleItem();
1235 if (item && item->mask_ref && item->mask_ref->getObject()) {
1236 gtk_action_set_sensitive(w, TRUE);
1237 } else {
1238 gtk_action_set_sensitive(w, FALSE);
1239 }
1240 }
1241 }
1243 void
1244 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1245 {
1246 sp_node_toolbox_sel_changed (selection, tbl);
1247 }
1251 //################################
1252 //## Node Editing Toolbox ##
1253 //################################
1255 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1256 {
1257 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1258 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1259 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1260 g_object_set_data( holder, "tracker", tracker );
1262 Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
1264 {
1265 InkAction* inky = ink_action_new( "NodeInsertAction",
1266 _("Insert node"),
1267 _("Insert new nodes into selected segments"),
1268 INKSCAPE_ICON_NODE_ADD,
1269 secondarySize );
1270 g_object_set( inky, "short_label", _("Insert"), NULL );
1271 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1272 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1273 }
1275 {
1276 InkAction* inky = ink_action_new( "NodeDeleteAction",
1277 _("Delete node"),
1278 _("Delete selected nodes"),
1279 INKSCAPE_ICON_NODE_DELETE,
1280 secondarySize );
1281 g_object_set( inky, "short_label", _("Delete"), NULL );
1282 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1283 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1284 }
1286 {
1287 InkAction* inky = ink_action_new( "NodeJoinAction",
1288 _("Join endnodes"),
1289 _("Join selected endnodes"),
1290 INKSCAPE_ICON_NODE_JOIN,
1291 secondarySize );
1292 g_object_set( inky, "short_label", _("Join"), NULL );
1293 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1294 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1295 }
1297 {
1298 InkAction* inky = ink_action_new( "NodeBreakAction",
1299 _("Break nodes"),
1300 _("Break path at selected nodes"),
1301 INKSCAPE_ICON_NODE_BREAK,
1302 secondarySize );
1303 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1304 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1305 }
1308 {
1309 InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1310 _("Join with segment"),
1311 _("Join selected endnodes with a new segment"),
1312 INKSCAPE_ICON_NODE_JOIN_SEGMENT,
1313 secondarySize );
1314 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1315 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1316 }
1318 {
1319 InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1320 _("Delete segment"),
1321 _("Delete segment between two non-endpoint nodes"),
1322 INKSCAPE_ICON_NODE_DELETE_SEGMENT,
1323 secondarySize );
1324 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1325 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1326 }
1328 {
1329 InkAction* inky = ink_action_new( "NodeCuspAction",
1330 _("Node Cusp"),
1331 _("Make selected nodes corner"),
1332 INKSCAPE_ICON_NODE_TYPE_CUSP,
1333 secondarySize );
1334 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1335 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1336 }
1338 {
1339 InkAction* inky = ink_action_new( "NodeSmoothAction",
1340 _("Node Smooth"),
1341 _("Make selected nodes smooth"),
1342 INKSCAPE_ICON_NODE_TYPE_SMOOTH,
1343 secondarySize );
1344 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1345 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1346 }
1348 {
1349 InkAction* inky = ink_action_new( "NodeSymmetricAction",
1350 _("Node Symmetric"),
1351 _("Make selected nodes symmetric"),
1352 INKSCAPE_ICON_NODE_TYPE_SYMMETRIC,
1353 secondarySize );
1354 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1355 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1356 }
1358 {
1359 InkAction* inky = ink_action_new( "NodeAutoAction",
1360 _("Node Auto"),
1361 _("Make selected nodes auto-smooth"),
1362 INKSCAPE_ICON_NODE_TYPE_AUTO_SMOOTH,
1363 secondarySize );
1364 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_auto), 0 );
1365 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1366 }
1368 {
1369 InkAction* inky = ink_action_new( "NodeLineAction",
1370 _("Node Line"),
1371 _("Make selected segments lines"),
1372 INKSCAPE_ICON_NODE_SEGMENT_LINE,
1373 secondarySize );
1374 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1375 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1376 }
1378 {
1379 InkAction* inky = ink_action_new( "NodeCurveAction",
1380 _("Node Curve"),
1381 _("Make selected segments curves"),
1382 INKSCAPE_ICON_NODE_SEGMENT_CURVE,
1383 secondarySize );
1384 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1385 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1386 }
1388 {
1389 InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1390 _("Show Handles"),
1391 _("Show the Bezier handles of selected nodes"),
1392 INKSCAPE_ICON_SHOW_NODE_HANDLES,
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_handles), desktop );
1396 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_handles", true) );
1397 }
1399 {
1400 InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1401 _("Show Outline"),
1402 _("Show the outline of the path"),
1403 INKSCAPE_ICON_SHOW_PATH_OUTLINE,
1404 Inkscape::ICON_SIZE_DECORATION );
1405 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1406 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1407 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_helperpath", false) );
1408 }
1410 {
1411 InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1412 _("Next path effect parameter"),
1413 _("Show next path effect parameter for editing"),
1414 INKSCAPE_ICON_PATH_EFFECT_PARAMETER_NEXT,
1415 Inkscape::ICON_SIZE_DECORATION );
1416 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1417 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1418 g_object_set_data( holder, "nodes_lpeedit", inky);
1419 }
1421 {
1422 InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1423 _("Edit clipping path"),
1424 _("Edit the clipping path of the object"),
1425 INKSCAPE_ICON_PATH_CLIP_EDIT,
1426 Inkscape::ICON_SIZE_DECORATION );
1427 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1428 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1429 g_object_set_data( holder, "nodes_clippathedit", inky);
1430 }
1432 {
1433 InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1434 _("Edit mask path"),
1435 _("Edit the mask of the object"),
1436 INKSCAPE_ICON_PATH_MASK_EDIT,
1437 Inkscape::ICON_SIZE_DECORATION );
1438 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1439 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1440 g_object_set_data( holder, "nodes_maskedit", inky);
1441 }
1443 /* X coord of selected node(s) */
1444 {
1445 EgeAdjustmentAction* eact = 0;
1446 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1447 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1448 eact = create_adjustment_action( "NodeXAction",
1449 _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1450 "/tools/nodes/Xcoord", 0,
1451 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1452 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1453 labels, values, G_N_ELEMENTS(labels),
1454 sp_node_path_x_value_changed );
1455 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1456 g_object_set_data( holder, "nodes_x_action", eact );
1457 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1458 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1459 }
1461 /* Y coord of selected node(s) */
1462 {
1463 EgeAdjustmentAction* eact = 0;
1464 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1465 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1466 eact = create_adjustment_action( "NodeYAction",
1467 _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1468 "/tools/nodes/Ycoord", 0,
1469 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1470 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1471 labels, values, G_N_ELEMENTS(labels),
1472 sp_node_path_y_value_changed );
1473 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1474 g_object_set_data( holder, "nodes_y_action", eact );
1475 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1476 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1477 }
1479 // add the units menu
1480 {
1481 GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1482 gtk_action_group_add_action( mainActions, act );
1483 }
1486 sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1488 //watch selection
1489 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1491 sigc::connection *c_selection_changed =
1492 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1493 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1494 pool->add_connection ("selection-changed", c_selection_changed);
1496 sigc::connection *c_selection_modified =
1497 new sigc::connection (sp_desktop_selection (desktop)->connectModified
1498 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1499 pool->add_connection ("selection-modified", c_selection_modified);
1501 sigc::connection *c_subselection_changed =
1502 new sigc::connection (desktop->connectToolSubselectionChanged
1503 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1504 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1506 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1508 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1509 } // end of sp_node_toolbox_prep()
1512 //########################
1513 //## Zoom Toolbox ##
1514 //########################
1516 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1517 {
1518 // no custom GtkAction setup needed
1519 } // end of sp_zoom_toolbox_prep()
1521 void ToolboxFactory::setToolboxDesktop(GtkWidget *toolbox, SPDesktop *desktop)
1522 {
1523 sigc::connection *conn = static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1524 "event_context_connection"));
1526 BarId id = static_cast<BarId>( GPOINTER_TO_INT(g_object_get_data(G_OBJECT(toolbox), BAR_ID_KEY)) );
1528 SetupFunction setup_func = 0;
1529 UpdateFunction update_func = 0;
1531 switch (id) {
1532 case BAR_TOOL:
1533 setup_func = setup_tool_toolbox;
1534 update_func = update_tool_toolbox;
1535 break;
1537 case BAR_AUX:
1538 toolbox = gtk_bin_get_child(GTK_BIN(toolbox));
1539 setup_func = setup_aux_toolbox;
1540 update_func = update_aux_toolbox;
1541 break;
1543 case BAR_COMMANDS:
1544 setup_func = setup_commands_toolbox;
1545 update_func = update_commands_toolbox;
1546 break;
1548 case BAR_SNAP:
1549 setup_func = setup_snap_toolbox;
1550 update_func = updateSnapToolbox;
1551 break;
1552 default:
1553 g_warning("Unexpected toolbox id encountered.");
1554 }
1556 gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1557 SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1559 if (old_desktop) {
1560 GList *children, *iter;
1562 children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1563 for ( iter = children ; iter ; iter = iter->next ) {
1564 gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1565 }
1566 g_list_free(children);
1567 }
1569 g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1571 if (desktop && setup_func && update_func) {
1572 gtk_widget_set_sensitive(toolbox, TRUE);
1573 setup_func(toolbox, desktop);
1574 update_func(desktop, desktop->event_context, toolbox);
1575 *conn = desktop->connectEventContextChanged(sigc::bind (sigc::ptr_fun(update_func), toolbox));
1576 } else {
1577 gtk_widget_set_sensitive(toolbox, FALSE);
1578 }
1580 } // end of sp_toolbox_set_desktop()
1583 static void setupToolboxCommon( GtkWidget *toolbox,
1584 SPDesktop *desktop,
1585 gchar const *descr,
1586 gchar const* toolbarName,
1587 gchar const* sizePref )
1588 {
1589 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1590 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1592 GtkUIManager* mgr = gtk_ui_manager_new();
1593 GError* errVal = 0;
1595 GtkOrientation orientation = GTK_ORIENTATION_HORIZONTAL;
1597 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1598 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1600 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, toolbarName );
1601 if ( prefs->getBool("/toolbox/icononly", true) ) {
1602 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1603 }
1605 Inkscape::IconSize toolboxSize = ToolboxFactory::prefToSize(sizePref);
1606 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1608 if (GTK_IS_HANDLE_BOX(toolbox)) {
1609 // g_message("GRABBING ORIENTATION [%s]", toolbarName);
1610 GtkPositionType pos = gtk_handle_box_get_handle_position(GTK_HANDLE_BOX(toolbox));
1611 orientation = ((pos == GTK_POS_LEFT) || (pos == GTK_POS_RIGHT)) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
1612 }
1613 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), orientation);
1614 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1616 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1618 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1619 if ( child ) {
1620 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1621 }
1623 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1624 }
1626 void ToolboxFactory::setOrientation(GtkWidget* toolbox, GtkOrientation orientation)
1627 {
1628 //g_message("Set orientation for %p to be %d", toolbox, orientation);
1629 //GType type = GTK_WIDGET_TYPE(toolbox);
1630 //g_message(" [%s]", g_type_name(type));
1631 //g_message(" %p", g_object_get_data(G_OBJECT(toolbox), BAR_ID_KEY));
1633 if (GTK_IS_BIN(toolbox)) {
1634 //g_message(" is a BIN");
1635 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1636 if (child) {
1637 //GType type2 = GTK_WIDGET_TYPE(child);
1638 //g_message(" child [%s]", g_type_name(type2));
1640 if (GTK_IS_BOX(child)) {
1641 //g_message(" is a BOX");
1643 GList* children = gtk_container_get_children(GTK_CONTAINER(child));
1644 if (children && children->data) {
1645 //GtkWidget* child2 = GTK_WIDGET(children->data);
1646 //GType type3 = GTK_WIDGET_TYPE(child2);
1647 //g_message(" child [%s]", g_type_name(type3));
1648 g_message("need to add dynamic switch");
1650 g_list_free(children);
1651 } else {
1652 //g_message(" has no children %p", children);
1653 // The call is being made before the toolbox proper has been setup.
1654 if (GTK_IS_HANDLE_BOX(toolbox)) {
1655 GtkPositionType pos = (orientation == GTK_ORIENTATION_HORIZONTAL) ? GTK_POS_LEFT : GTK_POS_TOP;
1656 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(toolbox), pos);
1657 //g_message("Setting position");
1658 }
1659 }
1660 }
1661 }
1662 }
1666 }
1668 static void
1669 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1670 {
1671 gchar const * descr =
1672 "<ui>"
1673 " <toolbar name='ToolToolbar'>"
1674 " <toolitem action='ToolSelector' />"
1675 " <toolitem action='ToolNode' />"
1676 " <toolitem action='ToolTweak' />"
1677 " <toolitem action='ToolSpray' />"
1678 " <toolitem action='ToolZoom' />"
1679 " <toolitem action='ToolRect' />"
1680 " <toolitem action='Tool3DBox' />"
1681 " <toolitem action='ToolArc' />"
1682 " <toolitem action='ToolStar' />"
1683 " <toolitem action='ToolSpiral' />"
1684 " <toolitem action='ToolPencil' />"
1685 " <toolitem action='ToolPen' />"
1686 " <toolitem action='ToolCalligraphic' />"
1687 " <toolitem action='ToolEraser' />"
1688 // " <toolitem action='ToolLPETool' />"
1689 " <toolitem action='ToolPaintBucket' />"
1690 " <toolitem action='ToolText' />"
1691 " <toolitem action='ToolConnector' />"
1692 " <toolitem action='ToolGradient' />"
1693 " <toolitem action='ToolDropper' />"
1694 " </toolbar>"
1695 "</ui>";
1697 setupToolboxCommon( toolbox, desktop, descr,
1698 "/ui/ToolToolbar",
1699 "/toolbox/tools/small");
1700 }
1702 static void
1703 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1704 {
1705 gchar const *const tname = ( eventcontext
1706 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1707 : NULL );
1708 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1710 for (int i = 0 ; tools[i].type_name ; i++ ) {
1711 Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1712 if ( act ) {
1713 bool setActive = tname && !strcmp(tname, tools[i].type_name);
1714 Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1715 if ( verbAct ) {
1716 verbAct->set_active(setActive);
1717 }
1718 }
1719 }
1720 }
1722 static void
1723 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1724 {
1725 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1726 GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1727 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1728 GtkUIManager* mgr = gtk_ui_manager_new();
1729 GError* errVal = 0;
1730 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1731 gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1733 std::map<std::string, GtkWidget*> dataHolders;
1735 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1736 if ( aux_toolboxes[i].prep_func ) {
1737 // converted to GtkActions and UIManager
1739 GtkWidget* kludge = gtk_toolbar_new();
1740 g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1741 g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1742 dataHolders[aux_toolboxes[i].type_name] = kludge;
1743 aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1744 } else {
1746 GtkWidget *sub_toolbox = 0;
1747 if (aux_toolboxes[i].create_func == NULL) {
1748 sub_toolbox = sp_empty_toolbox_new(desktop);
1749 } else {
1750 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1751 }
1753 gtk_size_group_add_widget( grouper, sub_toolbox );
1755 gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1756 g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1758 }
1759 }
1761 // Second pass to create toolbars *after* all GtkActions are created
1762 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1763 if ( aux_toolboxes[i].prep_func ) {
1764 // converted to GtkActions and UIManager
1766 GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1768 GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1769 gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1771 gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1772 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1773 g_free( tmp );
1774 tmp = 0;
1776 if ( prefs->getBool( "/toolbox/icononly", true) ) {
1777 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1778 }
1780 Inkscape::IconSize toolboxSize = ToolboxFactory::prefToSize("/toolbox/small");
1781 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1783 gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1785 if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1786 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1787 swatch->setDesktop( desktop );
1788 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1789 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1790 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1791 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 );
1792 }
1794 gtk_widget_show_all( holder );
1795 sp_set_font_size_smaller( holder );
1797 gtk_size_group_add_widget( grouper, holder );
1799 gtk_container_add( GTK_CONTAINER(toolbox), holder );
1800 g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1801 }
1802 }
1804 g_object_unref( G_OBJECT(grouper) );
1805 }
1807 static void
1808 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1809 {
1810 gchar const *tname = ( eventcontext
1811 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1812 : NULL );
1813 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1814 GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1815 if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1816 gtk_widget_show_all(sub_toolbox);
1817 g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1818 } else {
1819 gtk_widget_hide(sub_toolbox);
1820 }
1821 }
1822 }
1824 static void
1825 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1826 {
1827 gchar const * descr =
1828 "<ui>"
1829 " <toolbar name='CommandsToolbar'>"
1830 " <toolitem action='FileNew' />"
1831 " <toolitem action='FileOpen' />"
1832 " <toolitem action='FileSave' />"
1833 " <toolitem action='FilePrint' />"
1834 " <separator />"
1835 " <toolitem action='FileImport' />"
1836 " <toolitem action='FileExport' />"
1837 " <separator />"
1838 " <toolitem action='EditUndo' />"
1839 " <toolitem action='EditRedo' />"
1840 " <separator />"
1841 " <toolitem action='EditCopy' />"
1842 " <toolitem action='EditCut' />"
1843 " <toolitem action='EditPaste' />"
1844 " <separator />"
1845 " <toolitem action='ZoomSelection' />"
1846 " <toolitem action='ZoomDrawing' />"
1847 " <toolitem action='ZoomPage' />"
1848 " <separator />"
1849 " <toolitem action='EditDuplicate' />"
1850 " <toolitem action='EditClone' />"
1851 " <toolitem action='EditUnlinkClone' />"
1852 " <separator />"
1853 " <toolitem action='SelectionGroup' />"
1854 " <toolitem action='SelectionUnGroup' />"
1855 " <separator />"
1856 " <toolitem action='DialogFillStroke' />"
1857 " <toolitem action='DialogText' />"
1858 " <toolitem action='DialogLayers' />"
1859 " <toolitem action='DialogXMLEditor' />"
1860 " <toolitem action='DialogAlignDistribute' />"
1861 " <separator />"
1862 " <toolitem action='DialogPreferences' />"
1863 " <toolitem action='DialogDocumentProperties' />"
1864 " </toolbar>"
1865 "</ui>";
1867 setupToolboxCommon( toolbox, desktop, descr,
1868 "/ui/CommandsToolbar",
1869 "/toolbox/small" );
1870 }
1872 static void
1873 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1874 {
1875 }
1877 void toggle_snap_callback (GtkToggleAction *act, gpointer data) { //data points to the toolbox
1879 if (g_object_get_data(G_OBJECT(data), "freeze" )) {
1880 return;
1881 }
1883 gpointer ptr = g_object_get_data(G_OBJECT(data), "desktop");
1884 g_assert(ptr != NULL);
1886 SPDesktop *dt = reinterpret_cast<SPDesktop*>(ptr);
1887 SPNamedView *nv = sp_desktop_namedview(dt);
1888 SPDocument *doc = SP_OBJECT_DOCUMENT(nv);
1890 if (dt == NULL || nv == NULL) {
1891 g_warning("No desktop or namedview specified (in toggle_snap_callback)!");
1892 return;
1893 }
1895 Inkscape::XML::Node *repr = SP_OBJECT_REPR(nv);
1897 if (repr == NULL) {
1898 g_warning("This namedview doesn't have a xml representation attached!");
1899 return;
1900 }
1902 bool saved = sp_document_get_undo_sensitive(doc);
1903 sp_document_set_undo_sensitive(doc, false);
1905 bool v = false;
1906 SPAttributeEnum attr = (SPAttributeEnum) GPOINTER_TO_INT(g_object_get_data(G_OBJECT(act), "SP_ATTR_INKSCAPE"));
1908 switch (attr) {
1909 case SP_ATTR_INKSCAPE_SNAP_GLOBAL:
1910 dt->toggleSnapGlobal();
1911 break;
1912 case SP_ATTR_INKSCAPE_SNAP_BBOX:
1913 v = nv->snap_manager.snapprefs.getSnapModeBBox();
1914 sp_repr_set_boolean(repr, "inkscape:snap-bbox", !v);
1915 break;
1916 case SP_ATTR_INKSCAPE_BBOX_PATHS:
1917 v = nv->snap_manager.snapprefs.getSnapToBBoxPath();
1918 sp_repr_set_boolean(repr, "inkscape:bbox-paths", !v);
1919 break;
1920 case SP_ATTR_INKSCAPE_BBOX_NODES:
1921 v = nv->snap_manager.snapprefs.getSnapToBBoxNode();
1922 sp_repr_set_boolean(repr, "inkscape:bbox-nodes", !v);
1923 break;
1924 case SP_ATTR_INKSCAPE_SNAP_NODES:
1925 v = nv->snap_manager.snapprefs.getSnapModeNode();
1926 sp_repr_set_boolean(repr, "inkscape:snap-nodes", !v);
1927 break;
1928 case SP_ATTR_INKSCAPE_OBJECT_PATHS:
1929 v = nv->snap_manager.snapprefs.getSnapToItemPath();
1930 sp_repr_set_boolean(repr, "inkscape:object-paths", !v);
1931 break;
1932 case SP_ATTR_INKSCAPE_OBJECT_NODES:
1933 v = nv->snap_manager.snapprefs.getSnapToItemNode();
1934 sp_repr_set_boolean(repr, "inkscape:object-nodes", !v);
1935 break;
1936 case SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES:
1937 v = nv->snap_manager.snapprefs.getSnapSmoothNodes();
1938 sp_repr_set_boolean(repr, "inkscape:snap-smooth-nodes", !v);
1939 break;
1940 case SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS:
1941 v = nv->snap_manager.snapprefs.getSnapIntersectionCS();
1942 sp_repr_set_boolean(repr, "inkscape:snap-intersection-paths", !v);
1943 break;
1944 case SP_ATTR_INKSCAPE_SNAP_CENTER:
1945 v = nv->snap_manager.snapprefs.getIncludeItemCenter();
1946 sp_repr_set_boolean(repr, "inkscape:snap-center", !v);
1947 break;
1948 case SP_ATTR_INKSCAPE_SNAP_GRIDS:
1949 v = nv->snap_manager.snapprefs.getSnapToGrids();
1950 sp_repr_set_boolean(repr, "inkscape:snap-grids", !v);
1951 break;
1952 case SP_ATTR_INKSCAPE_SNAP_TO_GUIDES:
1953 v = nv->snap_manager.snapprefs.getSnapToGuides();
1954 sp_repr_set_boolean(repr, "inkscape:snap-to-guides", !v);
1955 break;
1956 case SP_ATTR_INKSCAPE_SNAP_PAGE:
1957 v = nv->snap_manager.snapprefs.getSnapToPageBorder();
1958 sp_repr_set_boolean(repr, "inkscape:snap-page", !v);
1959 break;
1960 /*case SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE:
1961 v = nv->snap_manager.snapprefs.getSnapIntersectionGG();
1962 sp_repr_set_boolean(repr, "inkscape:snap-intersection-grid-guide", !v);
1963 break;*/
1964 case SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS:
1965 v = nv->snap_manager.snapprefs.getSnapLineMidpoints();
1966 sp_repr_set_boolean(repr, "inkscape:snap-midpoints", !v);
1967 break;
1968 case SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS:
1969 v = nv->snap_manager.snapprefs.getSnapObjectMidpoints();
1970 sp_repr_set_boolean(repr, "inkscape:snap-object-midpoints", !v);
1971 break;
1972 case SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS:
1973 v = nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints();
1974 sp_repr_set_boolean(repr, "inkscape:snap-bbox-edge-midpoints", !v);
1975 break;
1976 case SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS:
1977 v = nv->snap_manager.snapprefs.getSnapBBoxMidpoints();
1978 sp_repr_set_boolean(repr, "inkscape:snap-bbox-midpoints", !v);
1979 break;
1980 default:
1981 g_warning("toggle_snap_callback has been called with an ID for which no action has been defined");
1982 break;
1983 }
1985 // The snapping preferences are stored in the document, and therefore toggling makes the document dirty
1986 doc->setModifiedSinceSave();
1988 sp_document_set_undo_sensitive(doc, saved);
1989 }
1991 void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1992 {
1993 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
1995 gchar const * descr =
1996 "<ui>"
1997 " <toolbar name='SnapToolbar'>"
1998 " <toolitem action='ToggleSnapGlobal' />"
1999 " <separator />"
2000 " <toolitem action='ToggleSnapFromBBoxCorner' />"
2001 " <toolitem action='ToggleSnapToBBoxPath' />"
2002 " <toolitem action='ToggleSnapToBBoxNode' />"
2003 " <toolitem action='ToggleSnapToFromBBoxEdgeMidpoints' />"
2004 " <toolitem action='ToggleSnapToFromBBoxCenters' />"
2005 " <separator />"
2006 " <toolitem action='ToggleSnapFromNode' />"
2007 " <toolitem action='ToggleSnapToItemPath' />"
2008 " <toolitem action='ToggleSnapToPathIntersections' />"
2009 " <toolitem action='ToggleSnapToItemNode' />"
2010 " <toolitem action='ToggleSnapToSmoothNodes' />"
2011 " <toolitem action='ToggleSnapToFromLineMidpoints' />"
2012 " <toolitem action='ToggleSnapToFromObjectCenters' />"
2013 " <toolitem action='ToggleSnapToFromRotationCenter' />"
2014 " <separator />"
2015 " <toolitem action='ToggleSnapToPageBorder' />"
2016 " <toolitem action='ToggleSnapToGrids' />"
2017 " <toolitem action='ToggleSnapToGuides' />"
2018 //" <toolitem action='ToggleSnapToGridGuideIntersections' />"
2019 " </toolbar>"
2020 "</ui>";
2022 Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
2024 {
2025 InkToggleAction* act = ink_toggle_action_new("ToggleSnapGlobal",
2026 _("Snap"), _("Enable snapping"), INKSCAPE_ICON_SNAP, secondarySize,
2027 SP_ATTR_INKSCAPE_SNAP_GLOBAL);
2029 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2030 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2031 }
2033 {
2034 InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromBBoxCorner",
2035 _("Bounding box"), _("Snap bounding box corners"), INKSCAPE_ICON_SNAP_BOUNDING_BOX,
2036 secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX);
2038 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2039 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2040 }
2042 {
2043 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxPath",
2044 _("Bounding box edges"), _("Snap to edges of a bounding box"),
2045 INKSCAPE_ICON_SNAP_BOUNDING_BOX_EDGES, secondarySize, SP_ATTR_INKSCAPE_BBOX_PATHS);
2047 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2048 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2049 }
2051 {
2052 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxNode",
2053 _("Bounding box corners"), _("Snap to bounding box corners"),
2054 INKSCAPE_ICON_SNAP_BOUNDING_BOX_CORNERS, secondarySize, SP_ATTR_INKSCAPE_BBOX_NODES);
2056 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2057 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2058 }
2060 {
2061 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxEdgeMidpoints",
2062 _("BBox Edge Midpoints"), _("Snap from and to midpoints of bounding box edges"),
2063 INKSCAPE_ICON_SNAP_BOUNDING_BOX_MIDPOINTS, secondarySize,
2064 SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS);
2066 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2067 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2068 }
2070 {
2071 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxCenters",
2072 _("BBox Centers"), _("Snapping from and to centers of bounding boxes"),
2073 INKSCAPE_ICON_SNAP_BOUNDING_BOX_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS);
2075 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2076 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2077 }
2079 {
2080 InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromNode",
2081 _("Nodes"), _("Snap nodes or handles"), INKSCAPE_ICON_SNAP_NODES, secondarySize, SP_ATTR_INKSCAPE_SNAP_NODES);
2083 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2084 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2085 }
2087 {
2088 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemPath",
2089 _("Paths"), _("Snap to paths"), INKSCAPE_ICON_SNAP_NODES_PATH, secondarySize,
2090 SP_ATTR_INKSCAPE_OBJECT_PATHS);
2092 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2093 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2094 }
2096 {
2097 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPathIntersections",
2098 _("Path intersections"), _("Snap to path intersections"),
2099 INKSCAPE_ICON_SNAP_NODES_INTERSECTION, secondarySize, SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS);
2101 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2102 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2103 }
2105 {
2106 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemNode",
2107 _("To nodes"), _("Snap to cusp nodes"), INKSCAPE_ICON_SNAP_NODES_CUSP, secondarySize,
2108 SP_ATTR_INKSCAPE_OBJECT_NODES);
2110 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2111 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2112 }
2114 {
2115 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToSmoothNodes",
2116 _("Smooth nodes"), _("Snap to smooth nodes"), INKSCAPE_ICON_SNAP_NODES_SMOOTH,
2117 secondarySize, SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES);
2119 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2120 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2121 }
2123 {
2124 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromLineMidpoints",
2125 _("Line Midpoints"), _("Snap from and to midpoints of line segments"),
2126 INKSCAPE_ICON_SNAP_NODES_MIDPOINT, secondarySize, SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS);
2128 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2129 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2130 }
2132 {
2133 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromObjectCenters",
2134 _("Object Centers"), _("Snap from and to centers of objects"),
2135 INKSCAPE_ICON_SNAP_NODES_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS);
2137 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2138 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2139 }
2141 {
2142 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromRotationCenter",
2143 _("Rotation Centers"), _("Snap from and to an item's rotation center"),
2144 INKSCAPE_ICON_SNAP_NODES_ROTATION_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_CENTER);
2146 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2147 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2148 }
2150 {
2151 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPageBorder",
2152 _("Page border"), _("Snap to the page border"), INKSCAPE_ICON_SNAP_PAGE,
2153 secondarySize, SP_ATTR_INKSCAPE_SNAP_PAGE);
2155 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2156 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2157 }
2159 {
2160 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGrids",
2161 _("Grids"), _("Snap to grids"), INKSCAPE_ICON_GRID_RECTANGULAR, secondarySize,
2162 SP_ATTR_INKSCAPE_SNAP_GRIDS);
2164 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2165 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2166 }
2168 {
2169 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGuides",
2170 _("Guides"), _("Snap to guides"), INKSCAPE_ICON_GUIDES, secondarySize,
2171 SP_ATTR_INKSCAPE_SNAP_TO_GUIDES);
2173 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2174 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2175 }
2177 /*{
2178 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGridGuideIntersections",
2179 _("Grid/guide intersections"), _("Snap to intersections of a grid with a guide"),
2180 INKSCAPE_ICON_SNAP_GRID_GUIDE_INTERSECTIONS, secondarySize,
2181 SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE);
2183 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2184 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2185 }*/
2187 setupToolboxCommon( toolbox, desktop, descr,
2188 "/ui/SnapToolbar",
2189 "/toolbox/secondary" );
2190 }
2192 void ToolboxFactory::updateSnapToolbox(SPDesktop *desktop, SPEventContext */*eventcontext*/, GtkWidget *toolbox)
2193 {
2194 g_assert(desktop != NULL);
2195 g_assert(toolbox != NULL);
2197 SPNamedView *nv = sp_desktop_namedview(desktop);
2198 if (nv == NULL) {
2199 g_warning("Namedview cannot be retrieved (in updateSnapToolbox)!");
2200 return;
2201 }
2203 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2205 Glib::RefPtr<Gtk::Action> act1 = mainActions->get_action("ToggleSnapGlobal");
2206 Glib::RefPtr<Gtk::Action> act2 = mainActions->get_action("ToggleSnapFromBBoxCorner");
2207 Glib::RefPtr<Gtk::Action> act3 = mainActions->get_action("ToggleSnapToBBoxPath");
2208 Glib::RefPtr<Gtk::Action> act4 = mainActions->get_action("ToggleSnapToBBoxNode");
2209 Glib::RefPtr<Gtk::Action> act4b = mainActions->get_action("ToggleSnapToFromBBoxEdgeMidpoints");
2210 Glib::RefPtr<Gtk::Action> act4c = mainActions->get_action("ToggleSnapToFromBBoxCenters");
2211 Glib::RefPtr<Gtk::Action> act5 = mainActions->get_action("ToggleSnapFromNode");
2212 Glib::RefPtr<Gtk::Action> act6 = mainActions->get_action("ToggleSnapToItemPath");
2213 Glib::RefPtr<Gtk::Action> act6b = mainActions->get_action("ToggleSnapToPathIntersections");
2214 Glib::RefPtr<Gtk::Action> act7 = mainActions->get_action("ToggleSnapToItemNode");
2215 Glib::RefPtr<Gtk::Action> act8 = mainActions->get_action("ToggleSnapToSmoothNodes");
2216 Glib::RefPtr<Gtk::Action> act9 = mainActions->get_action("ToggleSnapToFromLineMidpoints");
2217 Glib::RefPtr<Gtk::Action> act10 = mainActions->get_action("ToggleSnapToFromObjectCenters");
2218 Glib::RefPtr<Gtk::Action> act11 = mainActions->get_action("ToggleSnapToFromRotationCenter");
2219 Glib::RefPtr<Gtk::Action> act12 = mainActions->get_action("ToggleSnapToPageBorder");
2220 //Glib::RefPtr<Gtk::Action> act13 = mainActions->get_action("ToggleSnapToGridGuideIntersections");
2221 Glib::RefPtr<Gtk::Action> act14 = mainActions->get_action("ToggleSnapToGrids");
2222 Glib::RefPtr<Gtk::Action> act15 = mainActions->get_action("ToggleSnapToGuides");
2225 if (!act1) {
2226 return; // The snap actions haven't been defined yet (might be the case during startup)
2227 }
2229 // The ..._set_active calls below will toggle the buttons, but this shouldn't lead to
2230 // changes in our document because we're only updating the UI;
2231 // Setting the "freeze" parameter to true will block the code in toggle_snap_callback()
2232 g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(TRUE));
2234 bool const c1 = nv->snap_manager.snapprefs.getSnapEnabledGlobally();
2235 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act1->gobj()), c1);
2237 bool const c2 = nv->snap_manager.snapprefs.getSnapModeBBox();
2238 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act2->gobj()), c2);
2239 gtk_action_set_sensitive(GTK_ACTION(act2->gobj()), c1);
2241 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act3->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxPath());
2242 gtk_action_set_sensitive(GTK_ACTION(act3->gobj()), c1 && c2);
2243 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxNode());
2244 gtk_action_set_sensitive(GTK_ACTION(act4->gobj()), c1 && c2);
2245 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4b->gobj()), nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints());
2246 gtk_action_set_sensitive(GTK_ACTION(act4b->gobj()), c1 && c2);
2247 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4c->gobj()), nv->snap_manager.snapprefs.getSnapBBoxMidpoints());
2248 gtk_action_set_sensitive(GTK_ACTION(act4c->gobj()), c1 && c2);
2250 bool const c3 = nv->snap_manager.snapprefs.getSnapModeNode();
2251 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act5->gobj()), c3);
2252 gtk_action_set_sensitive(GTK_ACTION(act5->gobj()), c1);
2254 bool const c4 = nv->snap_manager.snapprefs.getSnapToItemPath();
2255 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6->gobj()), c4);
2256 gtk_action_set_sensitive(GTK_ACTION(act6->gobj()), c1 && c3);
2257 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6b->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionCS());
2258 gtk_action_set_sensitive(GTK_ACTION(act6b->gobj()), c1 && c3 && c4);
2259 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act7->gobj()), nv->snap_manager.snapprefs.getSnapToItemNode());
2260 gtk_action_set_sensitive(GTK_ACTION(act7->gobj()), c1 && c3);
2261 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act8->gobj()), nv->snap_manager.snapprefs.getSnapSmoothNodes());
2262 gtk_action_set_sensitive(GTK_ACTION(act8->gobj()), c1 && c3);
2263 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act9->gobj()), nv->snap_manager.snapprefs.getSnapLineMidpoints());
2264 gtk_action_set_sensitive(GTK_ACTION(act9->gobj()), c1 && c3);
2265 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act10->gobj()), nv->snap_manager.snapprefs.getSnapObjectMidpoints());
2266 gtk_action_set_sensitive(GTK_ACTION(act10->gobj()), c1 && c3);
2267 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act11->gobj()), nv->snap_manager.snapprefs.getIncludeItemCenter());
2268 gtk_action_set_sensitive(GTK_ACTION(act11->gobj()), c1 && c3);
2270 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act12->gobj()), nv->snap_manager.snapprefs.getSnapToPageBorder());
2271 gtk_action_set_sensitive(GTK_ACTION(act12->gobj()), c1);
2272 //gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act13->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionGG());
2273 //gtk_action_set_sensitive(GTK_ACTION(act13->gobj()), c1);
2275 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act14->gobj()), nv->snap_manager.snapprefs.getSnapToGrids());
2276 gtk_action_set_sensitive(GTK_ACTION(act14->gobj()), c1);
2277 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act15->gobj()), nv->snap_manager.snapprefs.getSnapToGuides());
2278 gtk_action_set_sensitive(GTK_ACTION(act15->gobj()), c1);
2281 g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(FALSE)); // unfreeze (see above)
2282 }
2284 void ToolboxFactory::showAuxToolbox(GtkWidget *toolbox_toplevel)
2285 {
2286 gtk_widget_show(toolbox_toplevel);
2287 GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
2289 GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
2290 if (!shown_toolbox) {
2291 return;
2292 }
2293 gtk_widget_show(toolbox);
2295 gtk_widget_show_all(shown_toolbox);
2296 }
2298 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop)
2299 {
2300 GtkWidget *tbl = gtk_toolbar_new();
2301 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
2302 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
2304 gtk_widget_show_all(tbl);
2305 sp_set_font_size_smaller (tbl);
2307 return tbl;
2308 }
2310 #define MODE_LABEL_WIDTH 70
2312 //########################
2313 //## Star ##
2314 //########################
2316 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2317 {
2318 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2320 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2321 // do not remember prefs if this call is initiated by an undo change, because undoing object
2322 // creation sets bogus values to its attributes before it is deleted
2323 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2324 prefs->setInt("/tools/shapes/star/magnitude", (gint)adj->value);
2325 }
2327 // quit if run by the attr_changed listener
2328 if (g_object_get_data( dataKludge, "freeze" )) {
2329 return;
2330 }
2332 // in turn, prevent listener from responding
2333 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2335 bool modmade = false;
2337 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2338 GSList const *items = selection->itemList();
2339 for (; items != NULL; items = items->next) {
2340 if (SP_IS_STAR((SPItem *) items->data)) {
2341 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2342 sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
2343 sp_repr_set_svg_double(repr, "sodipodi:arg2",
2344 (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
2345 + M_PI / (gint)adj->value));
2346 SP_OBJECT((SPItem *) items->data)->updateRepr();
2347 modmade = true;
2348 }
2349 }
2350 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2351 _("Star: Change number of corners"));
2353 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2354 }
2356 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2357 {
2358 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2360 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2361 if (!IS_NAN(adj->value)) {
2362 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2363 prefs->setDouble("/tools/shapes/star/proportion", adj->value);
2364 }
2365 }
2367 // quit if run by the attr_changed listener
2368 if (g_object_get_data( dataKludge, "freeze" )) {
2369 return;
2370 }
2372 // in turn, prevent listener from responding
2373 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2375 bool modmade = false;
2376 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2377 GSList const *items = selection->itemList();
2378 for (; items != NULL; items = items->next) {
2379 if (SP_IS_STAR((SPItem *) items->data)) {
2380 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2382 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2383 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2384 if (r2 < r1) {
2385 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
2386 } else {
2387 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
2388 }
2390 SP_OBJECT((SPItem *) items->data)->updateRepr();
2391 modmade = true;
2392 }
2393 }
2395 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2396 _("Star: Change spoke ratio"));
2398 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2399 }
2401 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
2402 {
2403 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2404 bool flat = ege_select_one_action_get_active( act ) == 0;
2406 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2407 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2408 prefs->setBool( "/tools/shapes/star/isflatsided", flat);
2409 }
2411 // quit if run by the attr_changed listener
2412 if (g_object_get_data( dataKludge, "freeze" )) {
2413 return;
2414 }
2416 // in turn, prevent listener from responding
2417 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2419 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2420 GSList const *items = selection->itemList();
2421 GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2422 bool modmade = false;
2424 if ( prop_action ) {
2425 gtk_action_set_sensitive( prop_action, !flat );
2426 }
2428 for (; items != NULL; items = items->next) {
2429 if (SP_IS_STAR((SPItem *) items->data)) {
2430 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2431 repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
2432 SP_OBJECT((SPItem *) items->data)->updateRepr();
2433 modmade = true;
2434 }
2435 }
2437 if (modmade) {
2438 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2439 flat ? _("Make polygon") : _("Make star"));
2440 }
2442 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2443 }
2445 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2446 {
2447 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2449 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2450 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2451 prefs->setDouble("/tools/shapes/star/rounded", (gdouble) adj->value);
2452 }
2454 // quit if run by the attr_changed listener
2455 if (g_object_get_data( dataKludge, "freeze" )) {
2456 return;
2457 }
2459 // in turn, prevent listener from responding
2460 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2462 bool modmade = false;
2464 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2465 GSList const *items = selection->itemList();
2466 for (; items != NULL; items = items->next) {
2467 if (SP_IS_STAR((SPItem *) items->data)) {
2468 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2469 sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2470 SP_OBJECT(items->data)->updateRepr();
2471 modmade = true;
2472 }
2473 }
2474 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2475 _("Star: Change rounding"));
2477 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2478 }
2480 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2481 {
2482 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2484 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2485 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2486 prefs->setDouble("/tools/shapes/star/randomized", (gdouble) adj->value);
2487 }
2489 // quit if run by the attr_changed listener
2490 if (g_object_get_data( dataKludge, "freeze" )) {
2491 return;
2492 }
2494 // in turn, prevent listener from responding
2495 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2497 bool modmade = false;
2499 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2500 GSList const *items = selection->itemList();
2501 for (; items != NULL; items = items->next) {
2502 if (SP_IS_STAR((SPItem *) items->data)) {
2503 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2504 sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2505 SP_OBJECT(items->data)->updateRepr();
2506 modmade = true;
2507 }
2508 }
2509 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2510 _("Star: Change randomization"));
2512 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2513 }
2516 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2517 gchar const */*old_value*/, gchar const */*new_value*/,
2518 bool /*is_interactive*/, gpointer data)
2519 {
2520 GtkWidget *tbl = GTK_WIDGET(data);
2522 // quit if run by the _changed callbacks
2523 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2524 return;
2525 }
2527 // in turn, prevent callbacks from responding
2528 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2530 GtkAdjustment *adj = 0;
2532 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2533 bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2535 if (!strcmp(name, "inkscape:randomized")) {
2536 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2537 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2538 } else if (!strcmp(name, "inkscape:rounded")) {
2539 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2540 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2541 } else if (!strcmp(name, "inkscape:flatsided")) {
2542 GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2543 char const *flatsides = repr->attribute("inkscape:flatsided");
2544 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2545 if ( flatsides && !strcmp(flatsides,"false") ) {
2546 ege_select_one_action_set_active( flat_action, 1 );
2547 gtk_action_set_sensitive( prop_action, TRUE );
2548 } else {
2549 ege_select_one_action_set_active( flat_action, 0 );
2550 gtk_action_set_sensitive( prop_action, FALSE );
2551 }
2552 } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2553 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2554 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2555 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2556 if (r2 < r1) {
2557 gtk_adjustment_set_value(adj, r2/r1);
2558 } else {
2559 gtk_adjustment_set_value(adj, r1/r2);
2560 }
2561 } else if (!strcmp(name, "sodipodi:sides")) {
2562 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2563 gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2564 }
2566 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2567 }
2570 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2571 {
2572 NULL, /* child_added */
2573 NULL, /* child_removed */
2574 star_tb_event_attr_changed,
2575 NULL, /* content_changed */
2576 NULL /* order_changed */
2577 };
2580 /**
2581 * \param selection Should not be NULL.
2582 */
2583 static void
2584 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2585 {
2586 int n_selected = 0;
2587 Inkscape::XML::Node *repr = NULL;
2589 purge_repr_listener( tbl, tbl );
2591 for (GSList const *items = selection->itemList();
2592 items != NULL;
2593 items = items->next)
2594 {
2595 if (SP_IS_STAR((SPItem *) items->data)) {
2596 n_selected++;
2597 repr = SP_OBJECT_REPR((SPItem *) items->data);
2598 }
2599 }
2601 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2603 if (n_selected == 0) {
2604 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2605 } else if (n_selected == 1) {
2606 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2608 if (repr) {
2609 g_object_set_data( tbl, "repr", repr );
2610 Inkscape::GC::anchor(repr);
2611 sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2612 sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2613 }
2614 } else {
2615 // FIXME: implement averaging of all parameters for multiple selected stars
2616 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2617 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2618 }
2619 }
2622 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2623 {
2624 // FIXME: in this and all other _default functions, set some flag telling the value_changed
2625 // callbacks to lump all the changes for all selected objects in one undo step
2627 GtkAdjustment *adj = 0;
2629 // fixme: make settable in prefs!
2630 gint mag = 5;
2631 gdouble prop = 0.5;
2632 gboolean flat = FALSE;
2633 gdouble randomized = 0;
2634 gdouble rounded = 0;
2636 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2637 ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2639 GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2640 gtk_action_set_sensitive( sb2, !flat );
2642 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2643 gtk_adjustment_set_value(adj, mag);
2644 gtk_adjustment_value_changed(adj);
2646 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2647 gtk_adjustment_set_value(adj, prop);
2648 gtk_adjustment_value_changed(adj);
2650 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2651 gtk_adjustment_set_value(adj, rounded);
2652 gtk_adjustment_value_changed(adj);
2654 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2655 gtk_adjustment_set_value(adj, randomized);
2656 gtk_adjustment_value_changed(adj);
2657 }
2660 void
2661 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2662 {
2663 GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2664 if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2665 GtkWidget *l = gtk_label_new(NULL);
2666 gtk_label_set_markup(GTK_LABEL(l), title);
2667 gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2668 if ( GTK_IS_TOOLBAR(tbl) ) {
2669 gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2670 } else {
2671 gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2672 }
2673 gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2674 }
2677 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2678 {
2679 Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
2681 {
2682 EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2683 ege_output_action_set_use_markup( act, TRUE );
2684 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2685 g_object_set_data( holder, "mode_action", act );
2686 }
2688 {
2689 EgeAdjustmentAction* eact = 0;
2690 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2691 bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2693 /* Flatsided checkbox */
2694 {
2695 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2697 GtkTreeIter iter;
2698 gtk_list_store_append( model, &iter );
2699 gtk_list_store_set( model, &iter,
2700 0, _("Polygon"),
2701 1, _("Regular polygon (with one handle) instead of a star"),
2702 2, INKSCAPE_ICON_DRAW_POLYGON,
2703 -1 );
2705 gtk_list_store_append( model, &iter );
2706 gtk_list_store_set( model, &iter,
2707 0, _("Star"),
2708 1, _("Star instead of a regular polygon (with one handle)"),
2709 2, INKSCAPE_ICON_DRAW_STAR,
2710 -1 );
2712 EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2713 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2714 g_object_set_data( holder, "flat_action", act );
2716 ege_select_one_action_set_appearance( act, "full" );
2717 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2718 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2719 ege_select_one_action_set_icon_column( act, 2 );
2720 ege_select_one_action_set_icon_size( act, secondarySize );
2721 ege_select_one_action_set_tooltip_column( act, 1 );
2723 ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2724 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2725 }
2727 /* Magnitude */
2728 {
2729 gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2730 gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2731 eact = create_adjustment_action( "MagnitudeAction",
2732 _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2733 "/tools/shapes/star/magnitude", 3,
2734 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2735 3, 1024, 1, 5,
2736 labels, values, G_N_ELEMENTS(labels),
2737 sp_stb_magnitude_value_changed,
2738 1.0, 0 );
2739 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2740 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2741 }
2743 /* Spoke ratio */
2744 {
2745 gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2746 gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2747 eact = create_adjustment_action( "SpokeAction",
2748 _("Spoke ratio"), _("Spoke ratio:"),
2749 // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2750 // Base radius is the same for the closest handle.
2751 _("Base radius to tip radius ratio"),
2752 "/tools/shapes/star/proportion", 0.5,
2753 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2754 0.01, 1.0, 0.01, 0.1,
2755 labels, values, G_N_ELEMENTS(labels),
2756 sp_stb_proportion_value_changed );
2757 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2758 g_object_set_data( holder, "prop_action", eact );
2759 }
2761 if ( !isFlatSided ) {
2762 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2763 } else {
2764 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2765 }
2767 /* Roundedness */
2768 {
2769 gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2770 gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2771 eact = create_adjustment_action( "RoundednessAction",
2772 _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2773 "/tools/shapes/star/rounded", 0.0,
2774 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2775 -10.0, 10.0, 0.01, 0.1,
2776 labels, values, G_N_ELEMENTS(labels),
2777 sp_stb_rounded_value_changed );
2778 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2779 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2780 }
2782 /* Randomization */
2783 {
2784 gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2785 gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2786 eact = create_adjustment_action( "RandomizationAction",
2787 _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2788 "/tools/shapes/star/randomized", 0.0,
2789 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2790 -10.0, 10.0, 0.001, 0.01,
2791 labels, values, G_N_ELEMENTS(labels),
2792 sp_stb_randomized_value_changed, 0.1, 3 );
2793 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2794 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2795 }
2796 }
2798 {
2799 /* Reset */
2800 {
2801 GtkAction* act = gtk_action_new( "StarResetAction",
2802 _("Defaults"),
2803 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2804 GTK_STOCK_CLEAR );
2805 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2806 gtk_action_group_add_action( mainActions, act );
2807 gtk_action_set_sensitive( act, TRUE );
2808 }
2809 }
2811 sigc::connection *connection = new sigc::connection(
2812 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2813 );
2814 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2815 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2816 }
2819 //########################
2820 //## Rect ##
2821 //########################
2823 static void sp_rtb_sensitivize( GObject *tbl )
2824 {
2825 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2826 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2827 GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2829 if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2830 gtk_action_set_sensitive( not_rounded, FALSE );
2831 } else {
2832 gtk_action_set_sensitive( not_rounded, TRUE );
2833 }
2834 }
2837 static void
2838 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2839 void (*setter)(SPRect *, gdouble))
2840 {
2841 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2843 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2844 SPUnit const *unit = tracker->getActiveUnit();
2846 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2847 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2848 prefs->setDouble(Glib::ustring("/tools/shapes/rect/") + value_name, sp_units_get_pixels(adj->value, *unit));
2849 }
2851 // quit if run by the attr_changed listener
2852 if (g_object_get_data( tbl, "freeze" )) {
2853 return;
2854 }
2856 // in turn, prevent listener from responding
2857 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2859 bool modmade = false;
2860 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2861 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2862 if (SP_IS_RECT(items->data)) {
2863 if (adj->value != 0) {
2864 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2865 } else {
2866 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2867 }
2868 modmade = true;
2869 }
2870 }
2872 sp_rtb_sensitivize( tbl );
2874 if (modmade) {
2875 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2876 _("Change rectangle"));
2877 }
2879 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2880 }
2882 static void
2883 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2884 {
2885 sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2886 }
2888 static void
2889 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2890 {
2891 sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2892 }
2894 static void
2895 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2896 {
2897 sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2898 }
2900 static void
2901 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2902 {
2903 sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2904 }
2908 static void
2909 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2910 {
2911 GtkAdjustment *adj = 0;
2913 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2914 gtk_adjustment_set_value(adj, 0.0);
2915 // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2916 gtk_adjustment_value_changed(adj);
2918 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2919 gtk_adjustment_set_value(adj, 0.0);
2920 gtk_adjustment_value_changed(adj);
2922 sp_rtb_sensitivize( obj );
2923 }
2925 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2926 gchar const */*old_value*/, gchar const */*new_value*/,
2927 bool /*is_interactive*/, gpointer data)
2928 {
2929 GObject *tbl = G_OBJECT(data);
2931 // quit if run by the _changed callbacks
2932 if (g_object_get_data( tbl, "freeze" )) {
2933 return;
2934 }
2936 // in turn, prevent callbacks from responding
2937 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2939 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2940 SPUnit const *unit = tracker->getActiveUnit();
2942 gpointer item = g_object_get_data( tbl, "item" );
2943 if (item && SP_IS_RECT(item)) {
2944 {
2945 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2946 gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2947 gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2948 }
2950 {
2951 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2952 gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2953 gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2954 }
2956 {
2957 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2958 gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2959 gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2960 }
2962 {
2963 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2964 gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2965 gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2966 }
2967 }
2969 sp_rtb_sensitivize( tbl );
2971 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2972 }
2975 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2976 NULL, /* child_added */
2977 NULL, /* child_removed */
2978 rect_tb_event_attr_changed,
2979 NULL, /* content_changed */
2980 NULL /* order_changed */
2981 };
2983 /**
2984 * \param selection should not be NULL.
2985 */
2986 static void
2987 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2988 {
2989 int n_selected = 0;
2990 Inkscape::XML::Node *repr = NULL;
2991 SPItem *item = NULL;
2993 if ( g_object_get_data( tbl, "repr" ) ) {
2994 g_object_set_data( tbl, "item", NULL );
2995 }
2996 purge_repr_listener( tbl, tbl );
2998 for (GSList const *items = selection->itemList();
2999 items != NULL;
3000 items = items->next) {
3001 if (SP_IS_RECT((SPItem *) items->data)) {
3002 n_selected++;
3003 item = (SPItem *) items->data;
3004 repr = SP_OBJECT_REPR(item);
3005 }
3006 }
3008 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3010 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3012 if (n_selected == 0) {
3013 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3015 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3016 gtk_action_set_sensitive(w, FALSE);
3017 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3018 gtk_action_set_sensitive(h, FALSE);
3020 } else if (n_selected == 1) {
3021 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3022 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3024 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3025 gtk_action_set_sensitive(w, TRUE);
3026 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3027 gtk_action_set_sensitive(h, TRUE);
3029 if (repr) {
3030 g_object_set_data( tbl, "repr", repr );
3031 g_object_set_data( tbl, "item", item );
3032 Inkscape::GC::anchor(repr);
3033 sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
3034 sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
3035 }
3036 } else {
3037 // FIXME: implement averaging of all parameters for multiple selected
3038 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3039 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3040 sp_rtb_sensitivize( tbl );
3041 }
3042 }
3045 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3046 {
3047 EgeAdjustmentAction* eact = 0;
3048 Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
3050 {
3051 EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
3052 ege_output_action_set_use_markup( act, TRUE );
3053 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3054 g_object_set_data( holder, "mode_action", act );
3055 }
3057 // rx/ry units menu: create
3058 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
3059 //tracker->addUnit( SP_UNIT_PERCENT, 0 );
3060 // fixme: add % meaning per cent of the width/height
3061 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
3062 g_object_set_data( holder, "tracker", tracker );
3064 /* W */
3065 {
3066 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3067 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3068 eact = create_adjustment_action( "RectWidthAction",
3069 _("Width"), _("W:"), _("Width of rectangle"),
3070 "/tools/shapes/rect/width", 0,
3071 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
3072 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3073 labels, values, G_N_ELEMENTS(labels),
3074 sp_rtb_width_value_changed );
3075 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3076 g_object_set_data( holder, "width_action", eact );
3077 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3078 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3079 }
3081 /* H */
3082 {
3083 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3084 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3085 eact = create_adjustment_action( "RectHeightAction",
3086 _("Height"), _("H:"), _("Height of rectangle"),
3087 "/tools/shapes/rect/height", 0,
3088 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3089 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3090 labels, values, G_N_ELEMENTS(labels),
3091 sp_rtb_height_value_changed );
3092 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3093 g_object_set_data( holder, "height_action", eact );
3094 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3095 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3096 }
3098 /* rx */
3099 {
3100 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3101 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3102 eact = create_adjustment_action( "RadiusXAction",
3103 _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
3104 "/tools/shapes/rect/rx", 0,
3105 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3106 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3107 labels, values, G_N_ELEMENTS(labels),
3108 sp_rtb_rx_value_changed);
3109 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3110 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3111 }
3113 /* ry */
3114 {
3115 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3116 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3117 eact = create_adjustment_action( "RadiusYAction",
3118 _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
3119 "/tools/shapes/rect/ry", 0,
3120 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3121 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3122 labels, values, G_N_ELEMENTS(labels),
3123 sp_rtb_ry_value_changed);
3124 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3125 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3126 }
3128 // add the units menu
3129 {
3130 GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
3131 gtk_action_group_add_action( mainActions, act );
3132 }
3134 /* Reset */
3135 {
3136 InkAction* inky = ink_action_new( "RectResetAction",
3137 _("Not rounded"),
3138 _("Make corners sharp"),
3139 INKSCAPE_ICON_RECTANGLE_MAKE_CORNERS_SHARP,
3140 secondarySize );
3141 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
3142 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3143 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3144 g_object_set_data( holder, "not_rounded", inky );
3145 }
3147 g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
3148 sp_rtb_sensitivize( holder );
3150 sigc::connection *connection = new sigc::connection(
3151 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
3152 );
3153 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3154 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3155 }
3157 //########################
3158 //## 3D Box ##
3159 //########################
3161 // normalize angle so that it lies in the interval [0,360]
3162 static double box3d_normalize_angle (double a) {
3163 double angle = a + ((int) (a/360.0))*360;
3164 if (angle < 0) {
3165 angle += 360.0;
3166 }
3167 return angle;
3168 }
3170 static void
3171 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
3172 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
3173 // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
3174 // have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
3175 // are reset).
3176 bool is_infinite = !persp3d_VP_is_finite(persp->perspective_impl, axis);
3178 if (is_infinite) {
3179 gtk_toggle_action_set_active(tact, TRUE);
3180 gtk_action_set_sensitive(act, TRUE);
3182 double angle = persp3d_get_infinite_angle(persp, axis);
3183 if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
3184 gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
3185 }
3186 } else {
3187 gtk_toggle_action_set_active(tact, FALSE);
3188 gtk_action_set_sensitive(act, FALSE);
3189 }
3190 }
3192 static void
3193 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
3194 if (!persp_repr) {
3195 g_print ("No perspective given to box3d_resync_toolbar().\n");
3196 return;
3197 }
3199 GtkWidget *tbl = GTK_WIDGET(data);
3200 GtkAdjustment *adj = 0;
3201 GtkAction *act = 0;
3202 GtkToggleAction *tact = 0;
3203 Persp3D *persp = persp3d_get_from_repr(persp_repr);
3204 if (!persp) {
3205 // Hmm, is it an error if this happens?
3206 return;
3207 }
3208 {
3209 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
3210 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
3211 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
3213 box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
3214 }
3215 {
3216 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
3217 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
3218 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
3220 box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
3221 }
3222 {
3223 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
3224 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
3225 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
3227 box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
3228 }
3229 }
3231 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3232 gchar const */*old_value*/, gchar const */*new_value*/,
3233 bool /*is_interactive*/, gpointer data)
3234 {
3235 GtkWidget *tbl = GTK_WIDGET(data);
3237 // quit if run by the attr_changed or selection changed listener
3238 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3239 return;
3240 }
3242 // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
3243 // sp_document_maybe_done() when the document is undo insensitive)
3244 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3246 // TODO: Only update the appropriate part of the toolbar
3247 // if (!strcmp(name, "inkscape:vp_z")) {
3248 box3d_resync_toolbar(repr, G_OBJECT(tbl));
3249 // }
3251 Persp3D *persp = persp3d_get_from_repr(repr);
3252 persp3d_update_box_reprs(persp);
3254 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3255 }
3257 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
3258 {
3259 NULL, /* child_added */
3260 NULL, /* child_removed */
3261 box3d_persp_tb_event_attr_changed,
3262 NULL, /* content_changed */
3263 NULL /* order_changed */
3264 };
3266 /**
3267 * \param selection Should not be NULL.
3268 */
3269 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
3270 // Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
3271 static void
3272 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3273 {
3274 // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
3275 // disable the angle entry fields for this direction (otherwise entering a value in them should only
3276 // update the perspectives with infinite VPs and leave the other ones untouched).
3278 Inkscape::XML::Node *persp_repr = NULL;
3279 purge_repr_listener(tbl, tbl);
3281 SPItem *item = selection->singleItem();
3282 if (item && SP_IS_BOX3D(item)) {
3283 // FIXME: Also deal with multiple selected boxes
3284 SPBox3D *box = SP_BOX3D(item);
3285 Persp3D *persp = box3d_get_perspective(box);
3286 persp_repr = SP_OBJECT_REPR(persp);
3287 if (persp_repr) {
3288 g_object_set_data(tbl, "repr", persp_repr);
3289 Inkscape::GC::anchor(persp_repr);
3290 sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
3291 sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
3292 }
3294 inkscape_active_document()->setCurrentPersp3D(persp3d_get_from_repr(persp_repr));
3295 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3296 prefs->setString("/tools/shapes/3dbox/persp", persp_repr->attribute("id"));
3298 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
3299 box3d_resync_toolbar(persp_repr, tbl);
3300 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
3301 }
3302 }
3304 static void
3305 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
3306 {
3307 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3308 SPDocument *document = sp_desktop_document(desktop);
3310 // quit if run by the attr_changed or selection changed listener
3311 if (g_object_get_data( dataKludge, "freeze" )) {
3312 return;
3313 }
3315 // in turn, prevent listener from responding
3316 g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
3318 std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
3319 if (sel_persps.empty()) {
3320 // this can happen when the document is created; we silently ignore it
3321 return;
3322 }
3323 Persp3D *persp = sel_persps.front();
3325 persp->perspective_impl->tmat.set_infinite_direction (axis, adj->value);
3326 SP_OBJECT(persp)->updateRepr();
3328 // TODO: use the correct axis here, too
3329 sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
3331 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
3332 }
3335 static void
3336 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3337 {
3338 box3d_angle_value_changed(adj, dataKludge, Proj::X);
3339 }
3341 static void
3342 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3343 {
3344 box3d_angle_value_changed(adj, dataKludge, Proj::Y);
3345 }
3347 static void
3348 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3349 {
3350 box3d_angle_value_changed(adj, dataKludge, Proj::Z);
3351 }
3354 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
3355 {
3356 // TODO: Take all selected perspectives into account
3357 std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
3358 if (sel_persps.empty()) {
3359 // this can happen when the document is created; we silently ignore it
3360 return;
3361 }
3362 Persp3D *persp = sel_persps.front();
3364 bool set_infinite = gtk_toggle_action_get_active(act);
3365 persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
3366 }
3368 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3369 {
3370 box3d_vp_state_changed(act, box3d_angle, Proj::X);
3371 }
3373 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3374 {
3375 box3d_vp_state_changed(act, box3d_angle, Proj::Y);
3376 }
3378 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3379 {
3380 box3d_vp_state_changed(act, box3d_angle, Proj::Z);
3381 }
3383 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3384 {
3385 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3386 EgeAdjustmentAction* eact = 0;
3387 SPDocument *document = sp_desktop_document (desktop);
3388 Persp3DImpl *persp_impl = document->getCurrentPersp3DImpl();
3390 EgeAdjustmentAction* box3d_angle_x = 0;
3391 EgeAdjustmentAction* box3d_angle_y = 0;
3392 EgeAdjustmentAction* box3d_angle_z = 0;
3394 /* Angle X */
3395 {
3396 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3397 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3398 eact = create_adjustment_action( "3DBoxAngleXAction",
3399 _("Angle in X direction"), _("Angle X:"),
3400 // Translators: PL is short for 'perspective line'
3401 _("Angle of PLs in X direction"),
3402 "/tools/shapes/3dbox/box3d_angle_x", 30,
3403 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
3404 -360.0, 360.0, 1.0, 10.0,
3405 labels, values, G_N_ELEMENTS(labels),
3406 box3d_angle_x_value_changed );
3407 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3408 g_object_set_data( holder, "box3d_angle_x_action", eact );
3409 box3d_angle_x = eact;
3410 }
3412 if (!persp_impl || !persp3d_VP_is_finite(persp_impl, Proj::X)) {
3413 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3414 } else {
3415 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3416 }
3419 /* VP X state */
3420 {
3421 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
3422 // Translators: VP is short for 'vanishing point'
3423 _("State of VP in X direction"),
3424 _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
3425 INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3426 Inkscape::ICON_SIZE_DECORATION );
3427 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3428 g_object_set_data( holder, "box3d_vp_x_state_action", act );
3429 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
3430 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3431 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3432 }
3434 /* Angle Y */
3435 {
3436 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3437 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3438 eact = create_adjustment_action( "3DBoxAngleYAction",
3439 _("Angle in Y direction"), _("Angle Y:"),
3440 // Translators: PL is short for 'perspective line'
3441 _("Angle of PLs in Y direction"),
3442 "/tools/shapes/3dbox/box3d_angle_y", 30,
3443 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3444 -360.0, 360.0, 1.0, 10.0,
3445 labels, values, G_N_ELEMENTS(labels),
3446 box3d_angle_y_value_changed );
3447 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3448 g_object_set_data( holder, "box3d_angle_y_action", eact );
3449 box3d_angle_y = eact;
3450 }
3452 if (!persp_impl || !persp3d_VP_is_finite(persp_impl, Proj::Y)) {
3453 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3454 } else {
3455 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3456 }
3458 /* VP Y state */
3459 {
3460 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
3461 // Translators: VP is short for 'vanishing point'
3462 _("State of VP in Y direction"),
3463 _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
3464 INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3465 Inkscape::ICON_SIZE_DECORATION );
3466 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3467 g_object_set_data( holder, "box3d_vp_y_state_action", act );
3468 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
3469 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3470 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3471 }
3473 /* Angle Z */
3474 {
3475 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3476 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3477 eact = create_adjustment_action( "3DBoxAngleZAction",
3478 _("Angle in Z direction"), _("Angle Z:"),
3479 // Translators: PL is short for 'perspective line'
3480 _("Angle of PLs in Z direction"),
3481 "/tools/shapes/3dbox/box3d_angle_z", 30,
3482 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3483 -360.0, 360.0, 1.0, 10.0,
3484 labels, values, G_N_ELEMENTS(labels),
3485 box3d_angle_z_value_changed );
3486 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3487 g_object_set_data( holder, "box3d_angle_z_action", eact );
3488 box3d_angle_z = eact;
3489 }
3491 if (!persp_impl || !persp3d_VP_is_finite(persp_impl, Proj::Z)) {
3492 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3493 } else {
3494 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3495 }
3497 /* VP Z state */
3498 {
3499 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3500 // Translators: VP is short for 'vanishing point'
3501 _("State of VP in Z direction"),
3502 _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3503 INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3504 Inkscape::ICON_SIZE_DECORATION );
3505 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3506 g_object_set_data( holder, "box3d_vp_z_state_action", act );
3507 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3508 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3509 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3510 }
3512 sigc::connection *connection = new sigc::connection(
3513 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3514 );
3515 g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3516 g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3517 }
3519 //########################
3520 //## Spiral ##
3521 //########################
3523 static void
3524 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, Glib::ustring const &value_name)
3525 {
3526 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3528 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3529 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3530 prefs->setDouble("/tools/shapes/spiral/" + value_name, adj->value);
3531 }
3533 // quit if run by the attr_changed listener
3534 if (g_object_get_data( tbl, "freeze" )) {
3535 return;
3536 }
3538 // in turn, prevent listener from responding
3539 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3541 gchar* namespaced_name = g_strconcat("sodipodi:", value_name.data(), NULL);
3543 bool modmade = false;
3544 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3545 items != NULL;
3546 items = items->next)
3547 {
3548 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3549 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3550 sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3551 SP_OBJECT((SPItem *) items->data)->updateRepr();
3552 modmade = true;
3553 }
3554 }
3556 g_free(namespaced_name);
3558 if (modmade) {
3559 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3560 _("Change spiral"));
3561 }
3563 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3564 }
3566 static void
3567 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3568 {
3569 sp_spl_tb_value_changed(adj, tbl, "revolution");
3570 }
3572 static void
3573 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3574 {
3575 sp_spl_tb_value_changed(adj, tbl, "expansion");
3576 }
3578 static void
3579 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3580 {
3581 sp_spl_tb_value_changed(adj, tbl, "t0");
3582 }
3584 static void
3585 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3586 {
3587 GtkWidget *tbl = GTK_WIDGET(obj);
3589 GtkAdjustment *adj;
3591 // fixme: make settable
3592 gdouble rev = 5;
3593 gdouble exp = 1.0;
3594 gdouble t0 = 0.0;
3596 adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3597 gtk_adjustment_set_value(adj, rev);
3598 gtk_adjustment_value_changed(adj);
3600 adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3601 gtk_adjustment_set_value(adj, exp);
3602 gtk_adjustment_value_changed(adj);
3604 adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3605 gtk_adjustment_set_value(adj, t0);
3606 gtk_adjustment_value_changed(adj);
3608 spinbutton_defocus(GTK_OBJECT(tbl));
3609 }
3612 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3613 gchar const */*old_value*/, gchar const */*new_value*/,
3614 bool /*is_interactive*/, gpointer data)
3615 {
3616 GtkWidget *tbl = GTK_WIDGET(data);
3618 // quit if run by the _changed callbacks
3619 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3620 return;
3621 }
3623 // in turn, prevent callbacks from responding
3624 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3626 GtkAdjustment *adj;
3627 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3628 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3630 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3631 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3633 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3634 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3636 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3637 }
3640 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3641 NULL, /* child_added */
3642 NULL, /* child_removed */
3643 spiral_tb_event_attr_changed,
3644 NULL, /* content_changed */
3645 NULL /* order_changed */
3646 };
3648 static void
3649 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3650 {
3651 int n_selected = 0;
3652 Inkscape::XML::Node *repr = NULL;
3654 purge_repr_listener( tbl, tbl );
3656 for (GSList const *items = selection->itemList();
3657 items != NULL;
3658 items = items->next)
3659 {
3660 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3661 n_selected++;
3662 repr = SP_OBJECT_REPR((SPItem *) items->data);
3663 }
3664 }
3666 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3668 if (n_selected == 0) {
3669 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3670 } else if (n_selected == 1) {
3671 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3673 if (repr) {
3674 g_object_set_data( tbl, "repr", repr );
3675 Inkscape::GC::anchor(repr);
3676 sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3677 sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3678 }
3679 } else {
3680 // FIXME: implement averaging of all parameters for multiple selected
3681 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3682 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3683 }
3684 }
3687 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3688 {
3689 EgeAdjustmentAction* eact = 0;
3690 Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
3692 {
3693 EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3694 ege_output_action_set_use_markup( act, TRUE );
3695 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3696 g_object_set_data( holder, "mode_action", act );
3697 }
3699 /* Revolution */
3700 {
3701 gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3702 gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3703 eact = create_adjustment_action( "SpiralRevolutionAction",
3704 _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3705 "/tools/shapes/spiral/revolution", 3.0,
3706 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3707 0.01, 1024.0, 0.1, 1.0,
3708 labels, values, G_N_ELEMENTS(labels),
3709 sp_spl_tb_revolution_value_changed, 1, 2);
3710 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3711 }
3713 /* Expansion */
3714 {
3715 gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3716 gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3717 eact = create_adjustment_action( "SpiralExpansionAction",
3718 _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3719 "/tools/shapes/spiral/expansion", 1.0,
3720 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3721 0.0, 1000.0, 0.01, 1.0,
3722 labels, values, G_N_ELEMENTS(labels),
3723 sp_spl_tb_expansion_value_changed);
3724 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3725 }
3727 /* T0 */
3728 {
3729 gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3730 gdouble values[] = {0, 0.5, 0.9};
3731 eact = create_adjustment_action( "SpiralT0Action",
3732 _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3733 "/tools/shapes/spiral/t0", 0.0,
3734 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3735 0.0, 0.999, 0.01, 1.0,
3736 labels, values, G_N_ELEMENTS(labels),
3737 sp_spl_tb_t0_value_changed);
3738 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3739 }
3741 /* Reset */
3742 {
3743 InkAction* inky = ink_action_new( "SpiralResetAction",
3744 _("Defaults"),
3745 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3746 GTK_STOCK_CLEAR,
3747 secondarySize );
3748 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3749 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3750 }
3753 sigc::connection *connection = new sigc::connection(
3754 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3755 );
3756 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3757 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3758 }
3760 //########################
3761 //## Pen/Pencil ##
3762 //########################
3764 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3765 static Glib::ustring const
3766 freehand_tool_name(GObject *dataKludge)
3767 {
3768 SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3769 return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3770 ? "/tools/freehand/pen"
3771 : "/tools/freehand/pencil" );
3772 }
3774 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3775 {
3776 gint mode = ege_select_one_action_get_active(act);
3778 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3779 prefs->setInt(freehand_tool_name(tbl) + "/freehand-mode", mode);
3781 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3783 // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3784 // preparatory work here
3785 if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3786 SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3787 sp_pen_context_set_polyline_mode(pc);
3788 }
3789 }
3791 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3792 {
3793 /* Freehand mode toggle buttons */
3794 {
3795 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3796 guint freehandMode = prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/freehand-mode" : "/tools/freehand/pen/freehand-mode" ), 0);
3797 Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
3799 {
3800 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3802 GtkTreeIter iter;
3803 gtk_list_store_append( model, &iter );
3804 gtk_list_store_set( model, &iter,
3805 0, _("Bezier"),
3806 1, _("Create regular Bezier path"),
3807 2, INKSCAPE_ICON_PATH_MODE_BEZIER,
3808 -1 );
3810 gtk_list_store_append( model, &iter );
3811 gtk_list_store_set( model, &iter,
3812 0, _("Spiro"),
3813 1, _("Create Spiro path"),
3814 2, INKSCAPE_ICON_PATH_MODE_SPIRO,
3815 -1 );
3817 if (!tool_is_pencil) {
3818 gtk_list_store_append( model, &iter );
3819 gtk_list_store_set( model, &iter,
3820 0, _("Zigzag"),
3821 1, _("Create a sequence of straight line segments"),
3822 2, INKSCAPE_ICON_PATH_MODE_POLYLINE,
3823 -1 );
3825 gtk_list_store_append( model, &iter );
3826 gtk_list_store_set( model, &iter,
3827 0, _("Paraxial"),
3828 1, _("Create a sequence of paraxial line segments"),
3829 2, INKSCAPE_ICON_PATH_MODE_POLYLINE_PARAXIAL,
3830 -1 );
3831 }
3833 EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3834 "FreehandModeActionPencil" :
3835 "FreehandModeActionPen",
3836 (_("Mode:")), (_("Mode of new lines drawn by this tool")), NULL, GTK_TREE_MODEL(model) );
3837 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3839 ege_select_one_action_set_appearance( act, "full" );
3840 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3841 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3842 ege_select_one_action_set_icon_column( act, 2 );
3843 ege_select_one_action_set_icon_size( act, secondarySize );
3844 ege_select_one_action_set_tooltip_column( act, 1 );
3846 ege_select_one_action_set_active( act, freehandMode);
3847 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3848 }
3849 }
3850 }
3852 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3853 gint shape = ege_select_one_action_get_active( act );
3854 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3855 prefs->setInt(freehand_tool_name(dataKludge) + "/shape", shape);
3856 }
3858 /**
3859 * \brief Generate the list of freehand advanced shape option entries.
3860 */
3861 GList * freehand_shape_dropdown_items_list() {
3862 GList *glist = NULL;
3864 glist = g_list_append (glist, _("None"));
3865 glist = g_list_append (glist, _("Triangle in"));
3866 glist = g_list_append (glist, _("Triangle out"));
3867 glist = g_list_append (glist, _("Ellipse"));
3868 glist = g_list_append (glist, _("From clipboard"));
3870 return glist;
3871 }
3873 static void
3874 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3875 /*advanced shape options */
3876 {
3877 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3878 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3880 GList* items = 0;
3881 gint count = 0;
3882 for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3883 {
3884 GtkTreeIter iter;
3885 gtk_list_store_append( model, &iter );
3886 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3887 count++;
3888 }
3889 g_list_free( items );
3890 items = 0;
3891 EgeSelectOneAction* act1 = ege_select_one_action_new(
3892 tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3893 _("Shape:"), (_("Shape of new paths drawn by this tool")), NULL, GTK_TREE_MODEL(model));
3894 g_object_set( act1, "short_label", _("Shape:"), NULL );
3895 ege_select_one_action_set_appearance( act1, "compact" );
3896 ege_select_one_action_set_active( act1, prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/shape" : "/tools/freehand/pen/shape" ), 0) );
3897 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3898 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3899 g_object_set_data( holder, "shape_action", act1 );
3900 }
3901 }
3903 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3904 {
3905 sp_add_freehand_mode_toggle(mainActions, holder, false);
3906 freehand_add_advanced_shape_options(mainActions, holder, false);
3907 }
3910 static void
3911 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3912 {
3913 GtkWidget *tbl = GTK_WIDGET(obj);
3915 GtkAdjustment *adj;
3917 // fixme: make settable
3918 gdouble tolerance = 4;
3920 adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3921 gtk_adjustment_set_value(adj, tolerance);
3922 gtk_adjustment_value_changed(adj);
3924 spinbutton_defocus(GTK_OBJECT(tbl));
3925 }
3927 static void
3928 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3929 {
3930 // quit if run by the attr_changed listener
3931 if (g_object_get_data( tbl, "freeze" )) {
3932 return;
3933 }
3934 // in turn, prevent listener from responding
3935 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3936 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3937 prefs->setDouble("/tools/freehand/pencil/tolerance", adj->value);
3938 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3939 }
3941 /*
3942 class PencilToleranceObserver : public Inkscape::Preferences::Observer {
3943 public:
3944 PencilToleranceObserver(Glib::ustring const &path, GObject *x) : Observer(path), _obj(x)
3945 {
3946 g_object_set_data(_obj, "prefobserver", this);
3947 }
3948 virtual ~PencilToleranceObserver() {
3949 if (g_object_get_data(_obj, "prefobserver") == this) {
3950 g_object_set_data(_obj, "prefobserver", NULL);
3951 }
3952 }
3953 virtual void notify(Inkscape::Preferences::Entry const &val) {
3954 GObject* tbl = _obj;
3955 if (g_object_get_data( tbl, "freeze" )) {
3956 return;
3957 }
3958 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3960 GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl, "tolerance");
3962 double v = val.getDouble(adj->value);
3963 gtk_adjustment_set_value(adj, v);
3964 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3965 }
3966 private:
3967 GObject *_obj;
3968 };
3969 */
3971 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3972 {
3973 sp_add_freehand_mode_toggle(mainActions, holder, true);
3975 EgeAdjustmentAction* eact = 0;
3977 /* Tolerance */
3978 {
3979 gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
3980 gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
3981 eact = create_adjustment_action( "PencilToleranceAction",
3982 _("Smoothing:"), _("Smoothing: "),
3983 _("How much smoothing (simplifying) is applied to the line"),
3984 "/tools/freehand/pencil/tolerance",
3985 3.0,
3986 GTK_WIDGET(desktop->canvas), NULL,
3987 holder, TRUE, "altx-pencil",
3988 1, 100.0, 0.5, 1.0,
3989 labels, values, G_N_ELEMENTS(labels),
3990 sp_pencil_tb_tolerance_value_changed,
3991 1, 2);
3992 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3993 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3994 }
3996 /* advanced shape options */
3997 freehand_add_advanced_shape_options(mainActions, holder, true);
3999 /* Reset */
4000 {
4001 InkAction* inky = ink_action_new( "PencilResetAction",
4002 _("Defaults"),
4003 _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
4004 GTK_STOCK_CLEAR,
4005 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4006 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
4007 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4008 }
4010 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4012 }
4015 //########################
4016 //## Tweak ##
4017 //########################
4019 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4020 {
4021 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4022 prefs->setDouble( "/tools/tweak/width", adj->value * 0.01 );
4023 }
4025 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4026 {
4027 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4028 prefs->setDouble( "/tools/tweak/force", adj->value * 0.01 );
4029 }
4031 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4032 {
4033 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4034 prefs->setBool("/tools/tweak/usepressure", gtk_toggle_action_get_active(act));
4035 }
4037 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4038 {
4039 int mode = ege_select_one_action_get_active( act );
4040 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4041 prefs->setInt("/tools/tweak/mode", mode);
4043 GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
4044 GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
4045 GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
4046 GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
4047 GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
4048 GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
4049 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
4050 if (doh) gtk_action_set_sensitive (doh, TRUE);
4051 if (dos) gtk_action_set_sensitive (dos, TRUE);
4052 if (dol) gtk_action_set_sensitive (dol, TRUE);
4053 if (doo) gtk_action_set_sensitive (doo, TRUE);
4054 if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
4055 if (fid) gtk_action_set_sensitive (fid, FALSE);
4056 } else {
4057 if (doh) gtk_action_set_sensitive (doh, FALSE);
4058 if (dos) gtk_action_set_sensitive (dos, FALSE);
4059 if (dol) gtk_action_set_sensitive (dol, FALSE);
4060 if (doo) gtk_action_set_sensitive (doo, FALSE);
4061 if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
4062 if (fid) gtk_action_set_sensitive (fid, TRUE);
4063 }
4064 }
4066 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4067 {
4068 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4069 prefs->setDouble( "/tools/tweak/fidelity", adj->value * 0.01 );
4070 }
4072 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
4073 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4074 prefs->setBool("/tools/tweak/doh", gtk_toggle_action_get_active(act));
4075 }
4076 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
4077 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4078 prefs->setBool("/tools/tweak/dos", gtk_toggle_action_get_active(act));
4079 }
4080 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
4081 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4082 prefs->setBool("/tools/tweak/dol", gtk_toggle_action_get_active(act));
4083 }
4084 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
4085 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4086 prefs->setBool("/tools/tweak/doo", gtk_toggle_action_get_active(act));
4087 }
4089 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4090 {
4091 Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
4092 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4094 {
4095 /* Width */
4096 gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
4097 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4098 EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
4099 _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
4100 "/tools/tweak/width", 15,
4101 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
4102 1, 100, 1.0, 10.0,
4103 labels, values, G_N_ELEMENTS(labels),
4104 sp_tweak_width_value_changed, 0.01, 0, 100 );
4105 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4106 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4107 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4108 }
4111 {
4112 /* Force */
4113 gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
4114 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4115 EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
4116 _("Force"), _("Force:"), _("The force of the tweak action"),
4117 "/tools/tweak/force", 20,
4118 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
4119 1, 100, 1.0, 10.0,
4120 labels, values, G_N_ELEMENTS(labels),
4121 sp_tweak_force_value_changed, 0.01, 0, 100 );
4122 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4123 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4124 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4125 }
4127 /* Mode */
4128 {
4129 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4131 GtkTreeIter iter;
4132 gtk_list_store_append( model, &iter );
4133 gtk_list_store_set( model, &iter,
4134 0, _("Move mode"),
4135 1, _("Move objects in any direction"),
4136 2, INKSCAPE_ICON_OBJECT_TWEAK_PUSH,
4137 -1 );
4139 gtk_list_store_append( model, &iter );
4140 gtk_list_store_set( model, &iter,
4141 0, _("Move in/out mode"),
4142 1, _("Move objects towards cursor; with Shift from cursor"),
4143 2, INKSCAPE_ICON_OBJECT_TWEAK_ATTRACT,
4144 -1 );
4146 gtk_list_store_append( model, &iter );
4147 gtk_list_store_set( model, &iter,
4148 0, _("Move jitter mode"),
4149 1, _("Move objects in random directions"),
4150 2, INKSCAPE_ICON_OBJECT_TWEAK_RANDOMIZE,
4151 -1 );
4153 gtk_list_store_append( model, &iter );
4154 gtk_list_store_set( model, &iter,
4155 0, _("Scale mode"),
4156 1, _("Shrink objects, with Shift enlarge"),
4157 2, INKSCAPE_ICON_OBJECT_TWEAK_SHRINK,
4158 -1 );
4160 gtk_list_store_append( model, &iter );
4161 gtk_list_store_set( model, &iter,
4162 0, _("Rotate mode"),
4163 1, _("Rotate objects, with Shift counterclockwise"),
4164 2, INKSCAPE_ICON_OBJECT_TWEAK_ROTATE,
4165 -1 );
4167 gtk_list_store_append( model, &iter );
4168 gtk_list_store_set( model, &iter,
4169 0, _("Duplicate/delete mode"),
4170 1, _("Duplicate objects, with Shift delete"),
4171 2, INKSCAPE_ICON_OBJECT_TWEAK_DUPLICATE,
4172 -1 );
4174 gtk_list_store_append( model, &iter );
4175 gtk_list_store_set( model, &iter,
4176 0, _("Push mode"),
4177 1, _("Push parts of paths in any direction"),
4178 2, INKSCAPE_ICON_PATH_TWEAK_PUSH,
4179 -1 );
4181 gtk_list_store_append( model, &iter );
4182 gtk_list_store_set( model, &iter,
4183 0, _("Shrink/grow mode"),
4184 1, _("Shrink (inset) parts of paths; with Shift grow (outset)"),
4185 2, INKSCAPE_ICON_PATH_TWEAK_SHRINK,
4186 -1 );
4188 gtk_list_store_append( model, &iter );
4189 gtk_list_store_set( model, &iter,
4190 0, _("Attract/repel mode"),
4191 1, _("Attract parts of paths towards cursor; with Shift from cursor"),
4192 2, INKSCAPE_ICON_PATH_TWEAK_ATTRACT,
4193 -1 );
4195 gtk_list_store_append( model, &iter );
4196 gtk_list_store_set( model, &iter,
4197 0, _("Roughen mode"),
4198 1, _("Roughen parts of paths"),
4199 2, INKSCAPE_ICON_PATH_TWEAK_ROUGHEN,
4200 -1 );
4202 gtk_list_store_append( model, &iter );
4203 gtk_list_store_set( model, &iter,
4204 0, _("Color paint mode"),
4205 1, _("Paint the tool's color upon selected objects"),
4206 2, INKSCAPE_ICON_OBJECT_TWEAK_PAINT,
4207 -1 );
4209 gtk_list_store_append( model, &iter );
4210 gtk_list_store_set( model, &iter,
4211 0, _("Color jitter mode"),
4212 1, _("Jitter the colors of selected objects"),
4213 2, INKSCAPE_ICON_OBJECT_TWEAK_JITTER_COLOR,
4214 -1 );
4216 gtk_list_store_append( model, &iter );
4217 gtk_list_store_set( model, &iter,
4218 0, _("Blur mode"),
4219 1, _("Blur selected objects more; with Shift, blur less"),
4220 2, INKSCAPE_ICON_OBJECT_TWEAK_BLUR,
4221 -1 );
4224 EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4225 g_object_set( act, "short_label", _("Mode:"), NULL );
4226 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4227 g_object_set_data( holder, "mode_action", act );
4229 ege_select_one_action_set_appearance( act, "full" );
4230 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4231 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4232 ege_select_one_action_set_icon_column( act, 2 );
4233 ege_select_one_action_set_icon_size( act, secondarySize );
4234 ege_select_one_action_set_tooltip_column( act, 1 );
4236 gint mode = prefs->getInt("/tools/tweak/mode", 0);
4237 ege_select_one_action_set_active( act, mode );
4238 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
4240 g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
4241 }
4243 guint mode = prefs->getInt("/tools/tweak/mode", 0);
4245 {
4246 EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
4247 ege_output_action_set_use_markup( act, TRUE );
4248 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4249 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4250 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4251 g_object_set_data( holder, "tweak_channels_label", act);
4252 }
4254 {
4255 InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
4256 _("Hue"),
4257 _("In color mode, act on objects' hue"),
4258 NULL,
4259 Inkscape::ICON_SIZE_DECORATION );
4260 //TRANSLATORS: "H" here stands for hue
4261 g_object_set( act, "short_label", _("H"), NULL );
4262 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4263 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
4264 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doh", true) );
4265 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4266 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4267 g_object_set_data( holder, "tweak_doh", act);
4268 }
4269 {
4270 InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
4271 _("Saturation"),
4272 _("In color mode, act on objects' saturation"),
4273 NULL,
4274 Inkscape::ICON_SIZE_DECORATION );
4275 //TRANSLATORS: "S" here stands for Saturation
4276 g_object_set( act, "short_label", _("S"), NULL );
4277 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4278 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
4279 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dos", true) );
4280 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4281 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4282 g_object_set_data( holder, "tweak_dos", act );
4283 }
4284 {
4285 InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
4286 _("Lightness"),
4287 _("In color mode, act on objects' lightness"),
4288 NULL,
4289 Inkscape::ICON_SIZE_DECORATION );
4290 //TRANSLATORS: "L" here stands for Lightness
4291 g_object_set( act, "short_label", _("L"), NULL );
4292 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4293 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
4294 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dol", true) );
4295 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4296 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4297 g_object_set_data( holder, "tweak_dol", act );
4298 }
4299 {
4300 InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
4301 _("Opacity"),
4302 _("In color mode, act on objects' opacity"),
4303 NULL,
4304 Inkscape::ICON_SIZE_DECORATION );
4305 //TRANSLATORS: "O" here stands for Opacity
4306 g_object_set( act, "short_label", _("O"), NULL );
4307 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4308 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
4309 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doo", true) );
4310 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4311 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4312 g_object_set_data( holder, "tweak_doo", act );
4313 }
4315 { /* Fidelity */
4316 gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
4317 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4318 EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
4319 _("Fidelity"), _("Fidelity:"),
4320 _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
4321 "/tools/tweak/fidelity", 50,
4322 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
4323 1, 100, 1.0, 10.0,
4324 labels, values, G_N_ELEMENTS(labels),
4325 sp_tweak_fidelity_value_changed, 0.01, 0, 100 );
4326 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4327 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4328 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
4329 gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
4330 g_object_set_data( holder, "tweak_fidelity", eact );
4331 }
4334 /* Use Pressure button */
4335 {
4336 InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
4337 _("Pressure"),
4338 _("Use the pressure of the input device to alter the force of tweak action"),
4339 INKSCAPE_ICON_DRAW_USE_PRESSURE,
4340 Inkscape::ICON_SIZE_DECORATION );
4341 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4342 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
4343 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/usepressure", true) );
4344 }
4346 }
4349 //########################
4350 //## Spray ##
4351 //########################
4353 static void sp_spray_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4354 {
4355 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4356 prefs->setDouble( "/tools/spray/width", adj->value );
4357 }
4359 static void sp_spray_mean_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4360 {
4361 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4362 prefs->setDouble( "/tools/spray/mean", adj->value );
4363 }
4365 static void sp_spray_standard_deviation_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4366 {
4367 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4368 prefs->setDouble( "/tools/spray/standard_deviation", adj->value );
4369 }
4371 static void sp_spray_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4372 {
4373 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4374 prefs->setBool("/tools/spray/usepressure", gtk_toggle_action_get_active(act));
4375 }
4377 static void sp_spray_mode_changed( EgeSelectOneAction *act, GObject */*tbl*/ )
4378 {
4379 int mode = ege_select_one_action_get_active( act );
4380 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4381 prefs->setInt("/tools/spray/mode", mode);
4382 }
4384 static void sp_spray_population_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4385 {
4386 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4387 prefs->setDouble( "/tools/spray/population", adj->value );
4388 }
4390 static void sp_spray_rotation_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4391 {
4392 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4393 prefs->setDouble( "/tools/spray/rotation_variation", adj->value );
4394 }
4396 static void sp_spray_scale_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4397 {
4398 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4399 prefs->setDouble( "/tools/spray/scale_variation", adj->value );
4400 }
4403 static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4404 {
4405 Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
4406 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4408 {
4409 /* Width */
4410 gchar const* labels[] = {_("(narrow spray)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad spray)")};
4411 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4412 EgeAdjustmentAction *eact = create_adjustment_action( "SprayWidthAction",
4413 _("Width"), _("Width:"), _("The width of the spray area (relative to the visible canvas area)"),
4414 "/tools/spray/width", 15,
4415 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spray",
4416 1, 100, 1.0, 10.0,
4417 labels, values, G_N_ELEMENTS(labels),
4418 sp_spray_width_value_changed, 1, 0 );
4419 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4420 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4421 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4422 }
4424 {
4425 /* Mean */
4426 gchar const* labels[] = {_("(minimum mean)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum mean)")};
4427 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4428 EgeAdjustmentAction *eact = create_adjustment_action( "SprayMeanAction",
4429 _("Focus"), _("Focus:"), _("0 to spray a spot. Increase to enlarge the ring radius."),
4430 "/tools/spray/mean", 0,
4431 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-mean",
4432 0, 100, 1.0, 10.0,
4433 labels, values, G_N_ELEMENTS(labels),
4434 sp_spray_mean_value_changed, 1, 0 );
4435 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4436 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4437 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4438 }
4440 {
4441 /* Standard_deviation */
4442 gchar const* labels[] = {_("(minimum scatter)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum scatter)")};
4443 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4444 EgeAdjustmentAction *eact = create_adjustment_action( "SprayStandard_deviationAction",
4445 _("Scatter"), _("Scatter:"), _("Increase to scatter sprayed objects."),
4446 "/tools/spray/standard_deviation", 70,
4447 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-standard_deviation",
4448 1, 100, 1.0, 10.0,
4449 labels, values, G_N_ELEMENTS(labels),
4450 sp_spray_standard_deviation_value_changed, 1, 0 );
4451 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4452 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4453 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4454 }
4456 /* Mode */
4457 {
4458 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4460 GtkTreeIter iter;
4461 gtk_list_store_append( model, &iter );
4462 gtk_list_store_set( model, &iter,
4463 0, _("Spray with copies"),
4464 1, _("Spray copies of the initial selection"),
4465 2, INKSCAPE_ICON_SPRAY_COPY_MODE,
4466 -1 );
4468 gtk_list_store_append( model, &iter );
4469 gtk_list_store_set( model, &iter,
4470 0, _("Spray with clones"),
4471 1, _("Spray clones of the initial selection"),
4472 2, INKSCAPE_ICON_SPRAY_CLONE_MODE,
4473 -1 );
4475 gtk_list_store_append( model, &iter );
4476 gtk_list_store_set( model, &iter,
4477 0, _("Spray single path"),
4478 1, _("Spray objects in a single path"),
4479 2, INKSCAPE_ICON_SPRAY_UNION_MODE,
4480 -1 );
4482 EgeSelectOneAction* act = ege_select_one_action_new( "SprayModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4483 g_object_set( act, "short_label", _("Mode:"), NULL );
4484 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4485 g_object_set_data( holder, "mode_action", act );
4487 ege_select_one_action_set_appearance( act, "full" );
4488 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4489 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4490 ege_select_one_action_set_icon_column( act, 2 );
4491 ege_select_one_action_set_icon_size( act, secondarySize );
4492 ege_select_one_action_set_tooltip_column( act, 1 );
4494 gint mode = prefs->getInt("/tools/spray/mode", 1);
4495 ege_select_one_action_set_active( act, mode );
4496 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_spray_mode_changed), holder );
4498 g_object_set_data( G_OBJECT(holder), "spray_tool_mode", act);
4499 }
4501 { /* Population */
4502 gchar const* labels[] = {_("(low population)"), 0, 0, _("(default)"), 0, 0, _("(high population)")};
4503 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4504 EgeAdjustmentAction *eact = create_adjustment_action( "SprayPopulationAction",
4505 _("Amount"), _("Amount:"),
4506 _("Adjusts the number of items sprayed per clic."),
4507 "/tools/spray/population", 70,
4508 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-population",
4509 1, 100, 1.0, 10.0,
4510 labels, values, G_N_ELEMENTS(labels),
4511 sp_spray_population_value_changed, 1, 0 );
4512 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4513 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4514 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4515 g_object_set_data( holder, "spray_population", eact );
4516 }
4518 /* Use Pressure button */
4519 {
4520 InkToggleAction* act = ink_toggle_action_new( "SprayPressureAction",
4521 _("Pressure"),
4522 _("Use the pressure of the input device to alter the amount of sprayed objects."),
4523 "use_pressure",
4524 Inkscape::ICON_SIZE_DECORATION );
4525 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4526 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_spray_pressure_state_changed), NULL);
4527 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/spray/usepressure", true) );
4528 }
4530 { /* Rotation */
4531 gchar const* labels[] = {_("(low rotation variation)"), 0, 0, _("(default)"), 0, 0, _("(high rotation variation)")};
4532 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4533 EgeAdjustmentAction *eact = create_adjustment_action( "SprayRotationAction",
4534 _("Rotation"), _("Rotation:"),
4535 // xgettext:no-c-format
4536 _("Variation of the rotation of the sprayed objects. 0% for the same rotation than the original object."),
4537 "/tools/spray/rotation_variation", 0,
4538 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-rotation",
4539 0, 100, 1.0, 10.0,
4540 labels, values, G_N_ELEMENTS(labels),
4541 sp_spray_rotation_value_changed, 1, 0 );
4542 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4543 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4544 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4545 g_object_set_data( holder, "spray_rotation", eact );
4546 }
4548 { /* Scale */
4549 gchar const* labels[] = {_("(low scale variation)"), 0, 0, _("(default)"), 0, 0, _("(high scale variation)")};
4550 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4551 EgeAdjustmentAction *eact = create_adjustment_action( "SprayScaleAction",
4552 _("Scale"), _("Scale:"),
4553 // xgettext:no-c-format
4554 _("Variation in the scale of the sprayed objects. 0% for the same scale than the original object."),
4555 "/tools/spray/scale_variation", 0,
4556 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-scale",
4557 0, 100, 1.0, 10.0,
4558 labels, values, G_N_ELEMENTS(labels),
4559 sp_spray_scale_value_changed, 1, 0 );
4560 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4561 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4562 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4563 g_object_set_data( holder, "spray_scale", eact );
4564 }
4568 }
4571 //########################
4572 //## Calligraphy ##
4573 //########################
4574 static void update_presets_list (GObject *tbl)
4575 {
4576 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4577 if (g_object_get_data(tbl, "presets_blocked"))
4578 return;
4580 EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4581 if (!sel) {
4582 // WTF!? This will cause a segfault if ever reached
4583 //ege_select_one_action_set_active(sel, 0);
4584 return;
4585 }
4587 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4589 int ege_index = 1;
4590 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++ege_index) {
4591 bool match = true;
4593 std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(*i);
4594 for (std::vector<Inkscape::Preferences::Entry>::iterator j = preset.begin(); j != preset.end(); ++j) {
4595 Glib::ustring entry_name = j->getEntryName();
4596 if (entry_name == "id" || entry_name == "name") continue;
4598 void *widget = g_object_get_data(tbl, entry_name.data());
4599 if (widget) {
4600 if (GTK_IS_ADJUSTMENT(widget)) {
4601 double v = j->getDouble();
4602 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4603 //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
4604 if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
4605 match = false;
4606 break;
4607 }
4608 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4609 bool v = j->getBool();
4610 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4611 //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
4612 if ( static_cast<bool>(gtk_toggle_action_get_active(toggle)) != v ) {
4613 match = false;
4614 break;
4615 }
4616 }
4617 }
4618 }
4620 if (match) {
4621 // newly added item is at the same index as the
4622 // save command, so we need to change twice for it to take effect
4623 ege_select_one_action_set_active(sel, 0);
4624 ege_select_one_action_set_active(sel, ege_index); // one-based index
4625 return;
4626 }
4627 }
4629 // no match found
4630 ege_select_one_action_set_active(sel, 0);
4631 }
4633 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
4634 {
4635 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4636 prefs->setDouble( "/tools/calligraphic/mass", adj->value );
4637 update_presets_list(tbl);
4638 }
4640 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
4641 {
4642 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4643 prefs->setDouble( "/tools/calligraphic/wiggle", adj->value );
4644 update_presets_list(tbl);
4645 }
4647 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
4648 {
4649 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4650 prefs->setDouble( "/tools/calligraphic/angle", adj->value );
4651 update_presets_list(tbl);
4652 }
4654 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
4655 {
4656 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4657 prefs->setDouble( "/tools/calligraphic/width", adj->value );
4658 update_presets_list(tbl);
4659 }
4661 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
4662 {
4663 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4664 prefs->setDouble("/tools/calligraphic/thinning", adj->value );
4665 update_presets_list(tbl);
4666 }
4668 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
4669 {
4670 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4671 prefs->setDouble( "/tools/calligraphic/flatness", adj->value );
4672 update_presets_list(tbl);
4673 }
4675 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
4676 {
4677 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4678 prefs->setDouble( "/tools/calligraphic/tremor", adj->value );
4679 update_presets_list(tbl);
4680 }
4682 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
4683 {
4684 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4685 prefs->setDouble( "/tools/calligraphic/cap_rounding", adj->value );
4686 update_presets_list(tbl);
4687 }
4689 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject* tbl )
4690 {
4691 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4692 prefs->setBool("/tools/calligraphic/usepressure", gtk_toggle_action_get_active( act ));
4693 update_presets_list(tbl);
4694 }
4696 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject* tbl )
4697 {
4698 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4699 prefs->setBool("/tools/calligraphic/tracebackground", gtk_toggle_action_get_active( act ));
4700 update_presets_list(tbl);
4701 }
4703 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject* tbl )
4704 {
4705 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4706 GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
4707 prefs->setBool("/tools/calligraphic/usetilt", gtk_toggle_action_get_active( act ));
4708 update_presets_list(tbl);
4709 if (calligraphy_angle )
4710 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
4711 }
4714 static gchar const *const widget_names[] = {
4715 "width",
4716 "mass",
4717 "wiggle",
4718 "angle",
4719 "thinning",
4720 "tremor",
4721 "flatness",
4722 "cap_rounding",
4723 "usepressure",
4724 "tracebackground",
4725 "usetilt"
4726 };
4729 static void sp_dcc_build_presets_list(GObject *tbl)
4730 {
4731 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4733 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4734 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4735 gtk_list_store_clear (model);
4737 {
4738 GtkTreeIter iter;
4739 gtk_list_store_append( model, &iter );
4740 gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4741 }
4743 // iterate over all presets to populate the list
4744 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4745 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4746 int ii=1;
4748 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i) {
4749 GtkTreeIter iter;
4750 Glib::ustring preset_name = prefs->getString(*i + "/name");
4751 gtk_list_store_append( model, &iter );
4752 gtk_list_store_set( model, &iter, 0, _(preset_name.data()), 1, ii++, -1 );
4753 }
4755 {
4756 GtkTreeIter iter;
4757 gtk_list_store_append( model, &iter );
4758 gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4759 g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4760 }
4762 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4764 update_presets_list (tbl);
4765 }
4767 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4768 {
4769 using Inkscape::UI::Dialog::CalligraphicProfileRename;
4770 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4771 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4772 if (! desktop) return;
4774 if (g_object_get_data(tbl, "presets_blocked"))
4775 return;
4777 CalligraphicProfileRename::show(desktop);
4778 if ( !CalligraphicProfileRename::applied()) {
4779 // dialog cancelled
4780 update_presets_list (tbl);
4781 return;
4782 }
4783 Glib::ustring profile_name = CalligraphicProfileRename::getProfileName();
4785 if (profile_name.empty()) {
4786 // empty name entered
4787 update_presets_list (tbl);
4788 return;
4789 }
4791 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4793 // If there's a preset with the given name, find it and set save_path appropriately
4794 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4795 int total_presets = presets.size();
4796 int new_index = -1;
4797 Glib::ustring save_path; // profile pref path without a trailing slash
4799 int temp_index = 0;
4800 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++temp_index) {
4801 Glib::ustring name = prefs->getString(*i + "/name");
4802 if (!name.empty() && profile_name == name) {
4803 new_index = temp_index;
4804 save_path = *i;
4805 break;
4806 }
4807 }
4809 if (new_index == -1) {
4810 // no preset with this name, create
4811 new_index = total_presets + 1;
4812 gchar *profile_id = g_strdup_printf("/dcc%d", new_index);
4813 save_path = Glib::ustring("/tools/calligraphic/preset") + profile_id;
4814 g_free(profile_id);
4815 }
4817 for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4818 gchar const *const widget_name = widget_names[i];
4819 void *widget = g_object_get_data(tbl, widget_name);
4820 if (widget) {
4821 if (GTK_IS_ADJUSTMENT(widget)) {
4822 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4823 prefs->setDouble(save_path + "/" + widget_name, gtk_adjustment_get_value(adj));
4824 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4825 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4826 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4827 prefs->setBool(save_path + "/" + widget_name, gtk_toggle_action_get_active(toggle));
4828 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4829 } else {
4830 g_warning("Unknown widget type for preset: %s\n", widget_name);
4831 }
4832 } else {
4833 g_warning("Bad key when writing preset: %s\n", widget_name);
4834 }
4835 }
4836 prefs->setString(save_path + "/name", profile_name);
4838 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4839 sp_dcc_build_presets_list (tbl);
4840 }
4843 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4845 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4847 gint preset_index = ege_select_one_action_get_active( act );
4848 // This is necessary because EgeSelectOneAction spams us with GObject "changed" signal calls
4849 // even when the preset is not changed. It would be good to replace it with something more
4850 // modern. Index 0 means "No preset", so we don't do anything.
4851 if (preset_index == 0) return;
4853 gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4855 if (preset_index == save_presets_index) {
4856 // this is the Save command
4857 sp_dcc_save_profile(NULL, tbl);
4858 return;
4859 }
4861 if (g_object_get_data(tbl, "presets_blocked"))
4862 return;
4864 // preset_index is one-based so we subtract 1
4865 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4866 Glib::ustring preset_path = presets.at(preset_index - 1);
4868 if (!preset_path.empty()) {
4869 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
4871 std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(preset_path);
4873 // Shouldn't this be std::map?
4874 for (std::vector<Inkscape::Preferences::Entry>::iterator i = preset.begin(); i != preset.end(); ++i) {
4875 Glib::ustring entry_name = i->getEntryName();
4876 if (entry_name == "id" || entry_name == "name") continue;
4877 void *widget = g_object_get_data(tbl, entry_name.data());
4878 if (widget) {
4879 if (GTK_IS_ADJUSTMENT(widget)) {
4880 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4881 gtk_adjustment_set_value(adj, i->getDouble());
4882 //std::cout << "set adj " << attr_name << " to " << v << "\n";
4883 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4884 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4885 gtk_toggle_action_set_active(toggle, i->getBool());
4886 //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4887 } else {
4888 g_warning("Unknown widget type for preset: %s\n", entry_name.data());
4889 }
4890 } else {
4891 g_warning("Bad key found in a preset record: %s\n", entry_name.data());
4892 }
4893 }
4894 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4895 }
4896 }
4899 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4900 {
4901 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4902 {
4903 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4905 EgeAdjustmentAction* calligraphy_angle = 0;
4907 {
4908 /* Width */
4909 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4910 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4911 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4912 _("Pen Width"), _("Width:"),
4913 _("The width of the calligraphic pen (relative to the visible canvas area)"),
4914 "/tools/calligraphic/width", 15,
4915 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4916 1, 100, 1.0, 10.0,
4917 labels, values, G_N_ELEMENTS(labels),
4918 sp_ddc_width_value_changed, 1, 0 );
4919 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4920 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4921 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4922 }
4924 {
4925 /* Thinning */
4926 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4927 gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4928 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4929 _("Stroke Thinning"), _("Thinning:"),
4930 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4931 "/tools/calligraphic/thinning", 10,
4932 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4933 -100, 100, 1, 10.0,
4934 labels, values, G_N_ELEMENTS(labels),
4935 sp_ddc_velthin_value_changed, 1, 0);
4936 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4937 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4938 }
4940 {
4941 /* Angle */
4942 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4943 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4944 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4945 _("Pen Angle"), _("Angle:"),
4946 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4947 "/tools/calligraphic/angle", 30,
4948 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4949 -90.0, 90.0, 1.0, 10.0,
4950 labels, values, G_N_ELEMENTS(labels),
4951 sp_ddc_angle_value_changed, 1, 0 );
4952 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4953 g_object_set_data( holder, "angle_action", eact );
4954 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4955 calligraphy_angle = eact;
4956 }
4958 {
4959 /* Fixation */
4960 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4961 gdouble values[] = {0, 20, 40, 60, 90, 100};
4962 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4963 _("Fixation"), _("Fixation:"),
4964 _("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"),
4965 "/tools/calligraphic/flatness", 90,
4966 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4967 0.0, 100, 1.0, 10.0,
4968 labels, values, G_N_ELEMENTS(labels),
4969 sp_ddc_flatness_value_changed, 1, 0);
4970 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4971 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4972 }
4974 {
4975 /* Cap Rounding */
4976 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4977 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4978 // TRANSLATORS: "cap" means "end" (both start and finish) here
4979 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4980 _("Cap rounding"), _("Caps:"),
4981 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4982 "/tools/calligraphic/cap_rounding", 0.0,
4983 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4984 0.0, 5.0, 0.01, 0.1,
4985 labels, values, G_N_ELEMENTS(labels),
4986 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4987 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4988 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4989 }
4991 {
4992 /* Tremor */
4993 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4994 gdouble values[] = {0, 10, 20, 40, 60, 100};
4995 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4996 _("Stroke Tremor"), _("Tremor:"),
4997 _("Increase to make strokes rugged and trembling"),
4998 "/tools/calligraphic/tremor", 0.0,
4999 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5000 0.0, 100, 1, 10.0,
5001 labels, values, G_N_ELEMENTS(labels),
5002 sp_ddc_tremor_value_changed, 1, 0);
5004 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5005 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5006 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5007 }
5009 {
5010 /* Wiggle */
5011 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
5012 gdouble values[] = {0, 20, 40, 60, 100};
5013 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
5014 _("Pen Wiggle"), _("Wiggle:"),
5015 _("Increase to make the pen waver and wiggle"),
5016 "/tools/calligraphic/wiggle", 0.0,
5017 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5018 0.0, 100, 1, 10.0,
5019 labels, values, G_N_ELEMENTS(labels),
5020 sp_ddc_wiggle_value_changed, 1, 0);
5021 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5022 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5023 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5024 }
5026 {
5027 /* Mass */
5028 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
5029 gdouble values[] = {0.0, 2, 10, 20, 50, 100};
5030 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
5031 _("Pen Mass"), _("Mass:"),
5032 _("Increase to make the pen drag behind, as if slowed by inertia"),
5033 "/tools/calligraphic/mass", 2.0,
5034 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5035 0.0, 100, 1, 10.0,
5036 labels, values, G_N_ELEMENTS(labels),
5037 sp_ddc_mass_value_changed, 1, 0);
5038 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5039 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5040 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5041 }
5044 /* Trace Background button */
5045 {
5046 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
5047 _("Trace Background"),
5048 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
5049 INKSCAPE_ICON_DRAW_TRACE_BACKGROUND,
5050 Inkscape::ICON_SIZE_DECORATION );
5051 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5052 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
5053 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/tracebackground", false) );
5054 g_object_set_data( holder, "tracebackground", act );
5055 }
5057 /* Use Pressure button */
5058 {
5059 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
5060 _("Pressure"),
5061 _("Use the pressure of the input device to alter the width of the pen"),
5062 INKSCAPE_ICON_DRAW_USE_PRESSURE,
5063 Inkscape::ICON_SIZE_DECORATION );
5064 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5065 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
5066 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usepressure", true) );
5067 g_object_set_data( holder, "usepressure", act );
5068 }
5070 /* Use Tilt button */
5071 {
5072 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
5073 _("Tilt"),
5074 _("Use the tilt of the input device to alter the angle of the pen's nib"),
5075 INKSCAPE_ICON_DRAW_USE_TILT,
5076 Inkscape::ICON_SIZE_DECORATION );
5077 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5078 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
5079 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs->getBool("/tools/calligraphic/usetilt", true) );
5080 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usetilt", true) );
5081 g_object_set_data( holder, "usetilt", act );
5082 }
5084 /*calligraphic profile */
5085 {
5086 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5087 EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
5088 ege_select_one_action_set_appearance (act1, "compact");
5089 g_object_set_data (holder, "profile_selector", act1 );
5091 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
5093 sp_dcc_build_presets_list (holder);
5095 g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
5096 gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
5097 }
5098 }
5099 }
5102 //########################
5103 //## Circle / Arc ##
5104 //########################
5106 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
5107 {
5108 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
5109 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
5111 if (v1 == 0 && v2 == 0) {
5112 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
5113 gtk_action_set_sensitive( ocb, FALSE );
5114 gtk_action_set_sensitive( make_whole, FALSE );
5115 }
5116 } else {
5117 gtk_action_set_sensitive( ocb, TRUE );
5118 gtk_action_set_sensitive( make_whole, TRUE );
5119 }
5120 }
5122 static void
5123 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
5124 {
5125 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5127 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5128 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5129 prefs->setDouble(Glib::ustring("/tools/shapes/arc/") + value_name, adj->value);
5130 }
5132 // quit if run by the attr_changed listener
5133 if (g_object_get_data( tbl, "freeze" )) {
5134 return;
5135 }
5137 // in turn, prevent listener from responding
5138 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5140 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
5142 bool modmade = false;
5143 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5144 items != NULL;
5145 items = items->next)
5146 {
5147 SPItem *item = SP_ITEM(items->data);
5149 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
5151 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
5152 SPArc *arc = SP_ARC(item);
5154 if (!strcmp(value_name, "start"))
5155 ge->start = (adj->value * M_PI)/ 180;
5156 else
5157 ge->end = (adj->value * M_PI)/ 180;
5159 sp_genericellipse_normalize(ge);
5160 ((SPObject *)arc)->updateRepr();
5161 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
5163 modmade = true;
5164 }
5165 }
5167 g_free(namespaced_name);
5169 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
5171 sp_arctb_sensitivize( tbl, adj->value, other->value );
5173 if (modmade) {
5174 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
5175 _("Arc: Change start/end"));
5176 }
5178 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5179 }
5182 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
5183 {
5184 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
5185 }
5187 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
5188 {
5189 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
5190 }
5193 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
5194 {
5195 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5196 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5197 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5198 prefs->setBool("/tools/shapes/arc/open", ege_select_one_action_get_active(act) != 0);
5199 }
5201 // quit if run by the attr_changed listener
5202 if (g_object_get_data( tbl, "freeze" )) {
5203 return;
5204 }
5206 // in turn, prevent listener from responding
5207 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5209 bool modmade = false;
5211 if ( ege_select_one_action_get_active(act) != 0 ) {
5212 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5213 items != NULL;
5214 items = items->next)
5215 {
5216 if (SP_IS_ARC((SPItem *) items->data)) {
5217 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5218 repr->setAttribute("sodipodi:open", "true");
5219 SP_OBJECT((SPItem *) items->data)->updateRepr();
5220 modmade = true;
5221 }
5222 }
5223 } else {
5224 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5225 items != NULL;
5226 items = items->next)
5227 {
5228 if (SP_IS_ARC((SPItem *) items->data)) {
5229 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5230 repr->setAttribute("sodipodi:open", NULL);
5231 SP_OBJECT((SPItem *) items->data)->updateRepr();
5232 modmade = true;
5233 }
5234 }
5235 }
5237 if (modmade) {
5238 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
5239 _("Arc: Change open/closed"));
5240 }
5242 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5243 }
5245 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
5246 {
5247 GtkAdjustment *adj;
5248 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
5249 gtk_adjustment_set_value(adj, 0.0);
5250 gtk_adjustment_value_changed(adj);
5252 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
5253 gtk_adjustment_set_value(adj, 0.0);
5254 gtk_adjustment_value_changed(adj);
5256 spinbutton_defocus( GTK_OBJECT(obj) );
5257 }
5259 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
5260 gchar const */*old_value*/, gchar const */*new_value*/,
5261 bool /*is_interactive*/, gpointer data)
5262 {
5263 GObject *tbl = G_OBJECT(data);
5265 // quit if run by the _changed callbacks
5266 if (g_object_get_data( tbl, "freeze" )) {
5267 return;
5268 }
5270 // in turn, prevent callbacks from responding
5271 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5273 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
5274 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
5276 GtkAdjustment *adj1,*adj2;
5277 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
5278 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
5279 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
5280 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
5282 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
5284 char const *openstr = NULL;
5285 openstr = repr->attribute("sodipodi:open");
5286 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
5288 if (openstr) {
5289 ege_select_one_action_set_active( ocb, 1 );
5290 } else {
5291 ege_select_one_action_set_active( ocb, 0 );
5292 }
5294 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5295 }
5297 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
5298 NULL, /* child_added */
5299 NULL, /* child_removed */
5300 arc_tb_event_attr_changed,
5301 NULL, /* content_changed */
5302 NULL /* order_changed */
5303 };
5306 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
5307 {
5308 int n_selected = 0;
5309 Inkscape::XML::Node *repr = NULL;
5311 purge_repr_listener( tbl, tbl );
5313 for (GSList const *items = selection->itemList();
5314 items != NULL;
5315 items = items->next)
5316 {
5317 if (SP_IS_ARC((SPItem *) items->data)) {
5318 n_selected++;
5319 repr = SP_OBJECT_REPR((SPItem *) items->data);
5320 }
5321 }
5323 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
5325 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
5326 if (n_selected == 0) {
5327 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
5328 } else if (n_selected == 1) {
5329 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
5330 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5332 if (repr) {
5333 g_object_set_data( tbl, "repr", repr );
5334 Inkscape::GC::anchor(repr);
5335 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
5336 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
5337 }
5338 } else {
5339 // FIXME: implement averaging of all parameters for multiple selected
5340 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
5341 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5342 sp_arctb_sensitivize( tbl, 1, 0 );
5343 }
5344 }
5347 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5348 {
5349 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5351 EgeAdjustmentAction* eact = 0;
5352 Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
5355 {
5356 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
5357 ege_output_action_set_use_markup( act, TRUE );
5358 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5359 g_object_set_data( holder, "mode_action", act );
5360 }
5362 /* Start */
5363 {
5364 eact = create_adjustment_action( "ArcStartAction",
5365 _("Start"), _("Start:"),
5366 _("The angle (in degrees) from the horizontal to the arc's start point"),
5367 "/tools/shapes/arc/start", 0.0,
5368 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
5369 -360.0, 360.0, 1.0, 10.0,
5370 0, 0, 0,
5371 sp_arctb_start_value_changed);
5372 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5373 }
5375 /* End */
5376 {
5377 eact = create_adjustment_action( "ArcEndAction",
5378 _("End"), _("End:"),
5379 _("The angle (in degrees) from the horizontal to the arc's end point"),
5380 "/tools/shapes/arc/end", 0.0,
5381 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
5382 -360.0, 360.0, 1.0, 10.0,
5383 0, 0, 0,
5384 sp_arctb_end_value_changed);
5385 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5386 }
5388 /* Segments / Pie checkbox */
5389 {
5390 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5392 GtkTreeIter iter;
5393 gtk_list_store_append( model, &iter );
5394 gtk_list_store_set( model, &iter,
5395 0, _("Closed arc"),
5396 1, _("Switch to segment (closed shape with two radii)"),
5397 2, INKSCAPE_ICON_DRAW_ELLIPSE_SEGMENT,
5398 -1 );
5400 gtk_list_store_append( model, &iter );
5401 gtk_list_store_set( model, &iter,
5402 0, _("Open Arc"),
5403 1, _("Switch to arc (unclosed shape)"),
5404 2, INKSCAPE_ICON_DRAW_ELLIPSE_ARC,
5405 -1 );
5407 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5408 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5409 g_object_set_data( holder, "open_action", act );
5411 ege_select_one_action_set_appearance( act, "full" );
5412 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5413 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5414 ege_select_one_action_set_icon_column( act, 2 );
5415 ege_select_one_action_set_icon_size( act, secondarySize );
5416 ege_select_one_action_set_tooltip_column( act, 1 );
5418 bool isClosed = !prefs->getBool("/tools/shapes/arc/open", false);
5419 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
5420 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
5421 }
5423 /* Make Whole */
5424 {
5425 InkAction* inky = ink_action_new( "ArcResetAction",
5426 _("Make whole"),
5427 _("Make the shape a whole ellipse, not arc or segment"),
5428 INKSCAPE_ICON_DRAW_ELLIPSE_WHOLE,
5429 secondarySize );
5430 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
5431 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5432 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
5433 g_object_set_data( holder, "make_whole", inky );
5434 }
5436 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
5437 // sensitivize make whole and open checkbox
5438 {
5439 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
5440 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
5441 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
5442 }
5445 sigc::connection *connection = new sigc::connection(
5446 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
5447 );
5448 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
5449 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
5450 }
5455 // toggle button callbacks and updaters
5457 //########################
5458 //## Dropper ##
5459 //########################
5461 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
5462 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5463 prefs->setInt( "/tools/dropper/pick", gtk_toggle_action_get_active( act ) );
5464 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
5465 if ( set_action ) {
5466 if ( gtk_toggle_action_get_active( act ) ) {
5467 gtk_action_set_sensitive( set_action, TRUE );
5468 } else {
5469 gtk_action_set_sensitive( set_action, FALSE );
5470 }
5471 }
5473 spinbutton_defocus(GTK_OBJECT(tbl));
5474 }
5476 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
5477 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5478 prefs->setBool( "/tools/dropper/setalpha", gtk_toggle_action_get_active( act ) );
5479 spinbutton_defocus(GTK_OBJECT(tbl));
5480 }
5483 /**
5484 * Dropper auxiliary toolbar construction and setup.
5485 *
5486 * TODO: Would like to add swatch of current color.
5487 * TODO: Add queue of last 5 or so colors selected with new swatches so that
5488 * can drag and drop places. Will provide a nice mixing palette.
5489 */
5490 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
5491 {
5492 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5493 gint pickAlpha = prefs->getInt( "/tools/dropper/pick", 1 );
5495 {
5496 EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
5497 ege_output_action_set_use_markup( act, TRUE );
5498 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5499 }
5501 {
5502 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
5503 _("Pick opacity"),
5504 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
5505 NULL,
5506 Inkscape::ICON_SIZE_DECORATION );
5507 g_object_set( act, "short_label", _("Pick"), NULL );
5508 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5509 g_object_set_data( holder, "pick_action", act );
5510 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
5511 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
5512 }
5514 {
5515 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
5516 _("Assign opacity"),
5517 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
5518 NULL,
5519 Inkscape::ICON_SIZE_DECORATION );
5520 g_object_set( act, "short_label", _("Assign"), NULL );
5521 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5522 g_object_set_data( holder, "set_action", act );
5523 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/dropper/setalpha", true) );
5524 // make sure it's disabled if we're not picking alpha
5525 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
5526 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
5527 }
5528 }
5531 //########################
5532 //## LPETool ##
5533 //########################
5535 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
5537 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
5538 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
5539 {
5540 using namespace Inkscape::LivePathEffect;
5542 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
5543 SPEventContext *ec = desktop->event_context;
5544 if (!SP_IS_LPETOOL_CONTEXT(ec)) {
5545 return;
5546 }
5548 // only take action if run by the attr_changed listener
5549 if (!g_object_get_data(tbl, "freeze")) {
5550 // in turn, prevent listener from responding
5551 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5553 gint mode = ege_select_one_action_get_active(act);
5554 EffectType type = lpesubtools[mode].type;
5556 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5557 bool success = lpetool_try_construction(lc, type);
5558 if (success) {
5559 // since the construction was already performed, we set the state back to inactive
5560 ege_select_one_action_set_active(act, 0);
5561 mode = 0;
5562 } else {
5563 // switch to the chosen subtool
5564 SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
5565 }
5567 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5568 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5569 prefs->setInt( "/tools/lpetool/mode", mode );
5570 }
5572 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
5573 }
5574 }
5576 void sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject */*tbl*/)
5577 {
5578 SPEventContext *ec = selection->desktop()->event_context;
5579 if (!SP_IS_LPETOOL_CONTEXT(ec))
5580 return;
5582 lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
5583 }
5585 void
5586 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
5587 {
5588 using namespace Inkscape::LivePathEffect;
5589 SPEventContext *ec = selection->desktop()->event_context;
5590 if (!SP_IS_LPETOOL_CONTEXT(ec))
5591 return;
5592 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
5594 lpetool_delete_measuring_items(lc);
5595 lpetool_create_measuring_items(lc, selection);
5597 // activate line segment combo box if a single item with LPELineSegment is selected
5598 GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
5599 SPItem *item = selection->singleItem();
5600 if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
5601 SPLPEItem *lpeitem = SP_LPE_ITEM(item);
5602 Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
5603 if (lpe && lpe->effectType() == LINE_SEGMENT) {
5604 LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
5605 g_object_set_data(tbl, "currentlpe", lpe);
5606 g_object_set_data(tbl, "currentlpeitem", lpeitem);
5607 gtk_action_set_sensitive(w, TRUE);
5608 ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
5609 } else {
5610 g_object_set_data(tbl, "currentlpe", NULL);
5611 g_object_set_data(tbl, "currentlpeitem", NULL);
5612 gtk_action_set_sensitive(w, FALSE);
5613 }
5614 } else {
5615 g_object_set_data(tbl, "currentlpe", NULL);
5616 g_object_set_data(tbl, "currentlpeitem", NULL);
5617 gtk_action_set_sensitive(w, FALSE);
5618 }
5619 }
5621 static void
5622 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
5623 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5624 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5626 bool show = gtk_toggle_action_get_active( act );
5627 prefs->setBool("/tools/lpetool/show_bbox", show);
5629 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5630 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5631 lpetool_context_reset_limiting_bbox(lc);
5632 }
5633 }
5635 static void
5636 lpetool_toggle_show_measuring_info (GtkToggleAction *act, GObject *tbl) {
5637 SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
5638 if (!tools_isactive(desktop, TOOLS_LPETOOL))
5639 return;
5641 GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
5642 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5643 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5644 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5645 bool show = gtk_toggle_action_get_active( act );
5646 prefs->setBool("/tools/lpetool/show_measuring_info", show);
5647 lpetool_show_measuring_info(lc, show);
5648 gtk_action_set_sensitive(GTK_ACTION(unitact), show);
5649 }
5650 }
5652 static void lpetool_unit_changed(GtkAction* /*act*/, GObject* tbl) {
5653 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
5654 SPUnit const *unit = tracker->getActiveUnit();
5655 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5656 prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5658 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5659 if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
5660 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5661 lpetool_delete_measuring_items(lc);
5662 lpetool_create_measuring_items(lc);
5663 }
5664 }
5666 static void
5667 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
5668 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5669 Inkscape::Selection *selection = desktop->selection;
5671 Geom::OptRect bbox = selection->bounds();
5673 if (bbox) {
5674 Geom::Point A(bbox->min());
5675 Geom::Point B(bbox->max());
5677 A *= desktop->doc2dt();
5678 B *= desktop->doc2dt();
5680 // TODO: should we provide a way to store points in prefs?
5681 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5682 prefs->setDouble("/tools/lpetool/bbox_upperleftx", A[Geom::X]);
5683 prefs->setDouble("/tools/lpetool/bbox_upperlefty", A[Geom::Y]);
5684 prefs->setDouble("/tools/lpetool/bbox_lowerrightx", B[Geom::X]);
5685 prefs->setDouble("/tools/lpetool/bbox_lowerrighty", B[Geom::Y]);
5687 lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
5688 }
5690 gtk_toggle_action_set_active(act, false);
5691 }
5693 static void
5694 sp_line_segment_build_list(GObject *tbl)
5695 {
5696 g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
5698 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
5699 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
5700 gtk_list_store_clear (model);
5702 // TODO: we add the entries of rht combo box manually; later this should be done automatically
5703 {
5704 GtkTreeIter iter;
5705 gtk_list_store_append( model, &iter );
5706 gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
5707 gtk_list_store_append( model, &iter );
5708 gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
5709 gtk_list_store_append( model, &iter );
5710 gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
5711 gtk_list_store_append( model, &iter );
5712 gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
5713 }
5715 g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5716 }
5718 static void
5719 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
5720 using namespace Inkscape::LivePathEffect;
5722 // quit if run by the attr_changed listener
5723 if (g_object_get_data(tbl, "freeze")) {
5724 return;
5725 }
5727 // in turn, prevent listener from responding
5728 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5730 LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
5731 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5732 if (lpeitem) {
5733 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5734 lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
5735 sp_lpe_item_update_patheffect(lpeitem, true, true);
5736 }
5738 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5739 }
5741 static void
5742 lpetool_open_lpe_dialog (GtkToggleAction *act, gpointer data) {
5743 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5745 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5746 sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5747 }
5748 gtk_toggle_action_set_active(act, false);
5749 }
5751 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5752 {
5753 UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5754 tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5755 g_object_set_data(holder, "tracker", tracker);
5756 SPUnit const *unit = tracker->getActiveUnit();
5758 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5759 prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5761 /** Automatically create a list of LPEs that get added to the toolbar **/
5762 {
5763 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5765 GtkTreeIter iter;
5767 // the first toggle button represents the state that no subtool is active (remove this when
5768 // this can be modeled by EgeSelectOneAction or some other action)
5769 gtk_list_store_append( model, &iter );
5770 gtk_list_store_set( model, &iter,
5771 0, _("All inactive"),
5772 1, _("No geometric tool is active"),
5773 2, "draw-geometry-inactive",
5774 -1 );
5776 Inkscape::LivePathEffect::EffectType type;
5777 for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5778 type = lpesubtools[i].type;
5779 gtk_list_store_append( model, &iter );
5780 gtk_list_store_set( model, &iter,
5781 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5782 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5783 2, lpesubtools[i].icon_name,
5784 -1 );
5785 }
5787 EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5788 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5789 g_object_set_data( holder, "lpetool_mode_action", act );
5791 ege_select_one_action_set_appearance( act, "full" );
5792 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5793 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5794 ege_select_one_action_set_icon_column( act, 2 );
5795 ege_select_one_action_set_tooltip_column( act, 1 );
5797 gint lpeToolMode = prefs->getInt("/tools/lpetool/mode", 0);
5798 ege_select_one_action_set_active( act, lpeToolMode );
5799 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5800 }
5802 /* Show limiting bounding box */
5803 {
5804 InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5805 _("Show limiting bounding box"),
5806 _("Show bounding box (used to cut infinite lines)"),
5807 "show-bounding-box",
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_bbox), desktop );
5811 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_bbox", true ) );
5812 }
5814 /* Set limiting bounding box to bbox of current selection */
5815 {
5816 InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5817 _("Get limiting bounding box from selection"),
5818 _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5819 "draw-geometry-set-bounding-box",
5820 Inkscape::ICON_SIZE_DECORATION );
5821 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5822 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5823 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5824 }
5827 /* Combo box to choose line segment type */
5828 {
5829 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5830 EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5831 ege_select_one_action_set_appearance (act, "compact");
5832 g_object_set_data (holder, "lpetool_line_segment_action", act );
5834 g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5836 sp_line_segment_build_list (holder);
5838 g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5839 gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5840 gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5841 }
5843 /* Display measuring info for selected items */
5844 {
5845 InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5846 _("Display measuring info"),
5847 _("Display measuring info for selected items"),
5848 "draw-geometry-show-measuring-info",
5849 Inkscape::ICON_SIZE_DECORATION );
5850 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5851 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
5852 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_measuring_info", true ) );
5853 }
5855 // add the units menu
5856 {
5857 GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5858 gtk_action_group_add_action( mainActions, act );
5859 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5860 g_object_set_data(holder, "lpetool_units_action", act);
5861 gtk_action_set_sensitive(act, prefs->getBool("/tools/lpetool/show_measuring_info", true));
5862 }
5864 /* Open LPE dialog (to adapt parameters numerically) */
5865 {
5866 InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
5867 _("Open LPE dialog"),
5868 _("Open LPE dialog (to adapt parameters numerically)"),
5869 "dialog-geometry",
5870 Inkscape::ICON_SIZE_DECORATION );
5871 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5872 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
5873 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5874 }
5876 //watch selection
5877 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5879 sigc::connection *c_selection_modified =
5880 new sigc::connection (sp_desktop_selection (desktop)->connectModified
5881 (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
5882 pool->add_connection ("selection-modified", c_selection_modified);
5884 sigc::connection *c_selection_changed =
5885 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5886 (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5887 pool->add_connection ("selection-changed", c_selection_changed);
5888 }
5890 //########################
5891 //## Eraser ##
5892 //########################
5894 static void sp_erc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
5895 {
5896 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5897 prefs->setDouble( "/tools/eraser/width", adj->value );
5898 update_presets_list(tbl);
5899 }
5901 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5902 {
5903 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5904 bool eraserMode = ege_select_one_action_get_active( act ) != 0;
5905 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5906 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5907 prefs->setBool( "/tools/eraser/mode", eraserMode );
5908 }
5910 // only take action if run by the attr_changed listener
5911 if (!g_object_get_data( tbl, "freeze" )) {
5912 // in turn, prevent listener from responding
5913 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5915 if ( eraserMode != 0 ) {
5916 } else {
5917 }
5918 // TODO finish implementation
5920 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5921 }
5922 }
5924 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5925 {
5926 {
5927 /* Width */
5928 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5929 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5930 EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5931 _("Pen Width"), _("Width:"),
5932 _("The width of the eraser pen (relative to the visible canvas area)"),
5933 "/tools/eraser/width", 15,
5934 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5935 1, 100, 1.0, 10.0,
5936 labels, values, G_N_ELEMENTS(labels),
5937 sp_erc_width_value_changed, 1, 0);
5938 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5939 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5940 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5941 }
5943 {
5944 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5946 GtkTreeIter iter;
5947 gtk_list_store_append( model, &iter );
5948 gtk_list_store_set( model, &iter,
5949 0, _("Delete"),
5950 1, _("Delete objects touched by the eraser"),
5951 2, INKSCAPE_ICON_DRAW_ERASER_DELETE_OBJECTS,
5952 -1 );
5954 gtk_list_store_append( model, &iter );
5955 gtk_list_store_set( model, &iter,
5956 0, _("Cut"),
5957 1, _("Cut out from objects"),
5958 2, INKSCAPE_ICON_PATH_DIFFERENCE,
5959 -1 );
5961 EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5962 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5963 g_object_set_data( holder, "eraser_mode_action", act );
5965 ege_select_one_action_set_appearance( act, "full" );
5966 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5967 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5968 ege_select_one_action_set_icon_column( act, 2 );
5969 ege_select_one_action_set_tooltip_column( act, 1 );
5971 /// @todo Convert to boolean?
5972 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5973 gint eraserMode = prefs->getBool("/tools/eraser/mode") ? 1 : 0;
5974 ege_select_one_action_set_active( act, eraserMode );
5975 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
5976 }
5978 }
5980 //########################
5981 //## Text Toolbox ##
5982 //########################
5983 /*
5984 static void
5985 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
5986 {
5987 //Call back for letter sizing spinbutton
5988 }
5990 static void
5991 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
5992 {
5993 //Call back for line height spinbutton
5994 }
5996 static void
5997 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5998 {
5999 //Call back for horizontal kerning spinbutton
6000 }
6002 static void
6003 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
6004 {
6005 //Call back for vertical kerning spinbutton
6006 }
6008 static void
6009 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
6010 {
6011 //Call back for letter rotation spinbutton
6012 }*/
6014 namespace {
6016 void
6017 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
6018 {
6019 // quit if run by the _changed callbacks
6020 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6021 return;
6022 }
6024 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6026 SPStyle *query =
6027 sp_style_new (SP_ACTIVE_DOCUMENT);
6029 int result_family =
6030 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6032 int result_style =
6033 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6035 int result_numbers =
6036 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6038 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6040 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6041 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
6042 // there are no texts in selection, read from prefs
6044 sp_style_read_from_prefs(query, "/tools/text");
6046 if (g_object_get_data(tbl, "text_style_from_prefs")) {
6047 // do not reset the toolbar style from prefs if we already did it last time
6048 sp_style_unref(query);
6049 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6050 return;
6051 }
6052 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
6053 } else {
6054 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
6055 }
6057 if (query->text)
6058 {
6059 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
6060 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6061 gtk_entry_set_text (GTK_ENTRY (entry), "");
6063 } else if (query->text->font_specification.value || query->text->font_family.value) {
6065 Gtk::ComboBoxEntry *combo = (Gtk::ComboBoxEntry *) (g_object_get_data (G_OBJECT (tbl), "family-entry-combo"));
6066 GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6068 // Get the font that corresponds
6069 Glib::ustring familyName;
6071 font_instance * font = font_factory::Default()->FaceFromStyle(query);
6072 if (font) {
6073 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
6074 font->Unref();
6075 font = NULL;
6076 }
6078 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
6080 Gtk::TreeIter iter;
6081 try {
6082 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
6083 Glib::RefPtr<Gtk::TreeModel> model = combo->get_model();
6084 iter = model->get_iter(path);
6085 } catch (...) {
6086 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
6087 sp_style_unref(query);
6088 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6089 return;
6090 }
6092 combo->set_active (iter);
6093 }
6095 //Size
6096 {
6097 GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
6098 gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
6099 gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
6100 g_free(str);
6101 }
6103 //Anchor
6104 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
6105 {
6106 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
6107 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6108 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6109 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6110 }
6111 else
6112 {
6113 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
6114 {
6115 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
6116 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6117 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6118 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6119 }
6120 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
6121 {
6122 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
6123 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6124 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6125 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6126 }
6127 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
6128 {
6129 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
6130 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6131 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6132 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6133 }
6134 }
6136 //Style
6137 {
6138 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
6140 gboolean active = gtk_toggle_button_get_active (button);
6141 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));
6143 if (active != check)
6144 {
6145 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6146 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6147 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6148 }
6149 }
6151 {
6152 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
6154 gboolean active = gtk_toggle_button_get_active (button);
6155 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
6157 if (active != check)
6158 {
6159 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6160 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6161 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6162 }
6163 }
6165 //Orientation
6166 //locking both buttons, changing one affect all group (both)
6167 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
6168 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6170 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
6171 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
6173 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
6174 {
6175 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6176 }
6177 else
6178 {
6179 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
6180 }
6181 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6182 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
6183 }
6185 sp_style_unref(query);
6187 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6188 }
6190 void
6191 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
6192 {
6193 sp_text_toolbox_selection_changed (selection, tbl);
6194 }
6196 void
6197 sp_text_toolbox_subselection_changed (gpointer /*tc*/, GObject *tbl)
6198 {
6199 sp_text_toolbox_selection_changed (NULL, tbl);
6200 }
6202 void
6203 sp_text_toolbox_family_changed (GtkComboBoxEntry *,
6204 GObject *tbl)
6205 {
6206 // quit if run by the _changed callbacks
6207 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6208 return;
6209 }
6211 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6213 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6214 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6215 const gchar* family = gtk_entry_get_text (GTK_ENTRY (entry));
6217 //g_print ("family changed to: %s\n", family);
6219 SPStyle *query =
6220 sp_style_new (SP_ACTIVE_DOCUMENT);
6222 int result_fontspec =
6223 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6225 SPCSSAttr *css = sp_repr_css_attr_new ();
6227 // First try to get the font spec from the stored value
6228 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
6230 if (fontSpec.empty()) {
6231 // Construct a new font specification if it does not yet exist
6232 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6233 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6234 fontFromStyle->Unref();
6235 }
6237 if (!fontSpec.empty()) {
6239 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
6241 if (!newFontSpec.empty()) {
6243 if (fontSpec != newFontSpec) {
6245 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
6247 if (font) {
6248 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6250 // Set all the these just in case they were altered when finding the best
6251 // match for the new family and old style...
6253 gchar c[256];
6255 font->Family(c, 256);
6257 sp_repr_css_set_property (css, "font-family", c);
6259 font->Attribute( "weight", c, 256);
6260 sp_repr_css_set_property (css, "font-weight", c);
6262 font->Attribute("style", c, 256);
6263 sp_repr_css_set_property (css, "font-style", c);
6265 font->Attribute("stretch", c, 256);
6266 sp_repr_css_set_property (css, "font-stretch", c);
6268 font->Attribute("variant", c, 256);
6269 sp_repr_css_set_property (css, "font-variant", c);
6271 font->Unref();
6272 }
6273 }
6275 } else {
6276 // If the old font on selection (or default) was not existing on the system,
6277 // ReplaceFontSpecificationFamily does not work. In that case we fall back to blindly
6278 // setting the family reported by the family chooser.
6280 //g_print ("fallback setting family: %s\n", family);
6281 sp_repr_css_set_property (css, "-inkscape-font-specification", family);
6282 sp_repr_css_set_property (css, "font-family", family);
6283 }
6284 }
6286 // If querying returned nothing, set the default style of the tool (for new texts)
6287 if (result_fontspec == QUERY_STYLE_NOTHING)
6288 {
6289 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6290 prefs->mergeStyle("/tools/text/style", css);
6291 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
6292 }
6293 else
6294 {
6295 sp_desktop_set_style (desktop, css, true, true);
6296 }
6298 sp_style_unref(query);
6300 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6301 _("Text: Change font family"));
6302 sp_repr_css_attr_unref (css);
6304 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6306 // unfreeze
6307 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6309 // focus to canvas
6310 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6311 }
6314 void
6315 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
6316 gpointer data)
6317 {
6318 if (g_object_get_data (G_OBJECT (button), "block")) return;
6319 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
6320 int prop = GPOINTER_TO_INT(data);
6322 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6324 // move the x of all texts to preserve the same bbox
6325 Inkscape::Selection *selection = sp_desktop_selection(desktop);
6326 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
6327 if (SP_IS_TEXT((SPItem *) items->data)) {
6328 SPItem *item = SP_ITEM(items->data);
6330 unsigned writing_mode = SP_OBJECT_STYLE(item)->writing_mode.value;
6331 // below, variable names suggest horizontal move, but we check the writing direction
6332 // and move in the corresponding axis
6333 int axis;
6334 if (writing_mode == SP_CSS_WRITING_MODE_LR_TB || writing_mode == SP_CSS_WRITING_MODE_RL_TB) {
6335 axis = NR::X;
6336 } else {
6337 axis = NR::Y;
6338 }
6340 Geom::OptRect bbox
6341 = item->getBounds(Geom::identity(), SPItem::GEOMETRIC_BBOX);
6342 if (!bbox)
6343 continue;
6344 double width = bbox->dimensions()[axis];
6345 // If you want to align within some frame, other than the text's own bbox, calculate
6346 // the left and right (or top and bottom for tb text) slacks of the text inside that
6347 // frame (currently unused)
6348 double left_slack = 0;
6349 double right_slack = 0;
6350 unsigned old_align = SP_OBJECT_STYLE(item)->text_align.value;
6351 double move = 0;
6352 if (old_align == SP_CSS_TEXT_ALIGN_START || old_align == SP_CSS_TEXT_ALIGN_LEFT) {
6353 switch (prop) {
6354 case 0:
6355 move = -left_slack;
6356 break;
6357 case 1:
6358 move = width/2 + (right_slack - left_slack)/2;
6359 break;
6360 case 2:
6361 move = width + right_slack;
6362 break;
6363 }
6364 } else if (old_align == SP_CSS_TEXT_ALIGN_CENTER) {
6365 switch (prop) {
6366 case 0:
6367 move = -width/2 - left_slack;
6368 break;
6369 case 1:
6370 move = (right_slack - left_slack)/2;
6371 break;
6372 case 2:
6373 move = width/2 + right_slack;
6374 break;
6375 }
6376 } else if (old_align == SP_CSS_TEXT_ALIGN_END || old_align == SP_CSS_TEXT_ALIGN_RIGHT) {
6377 switch (prop) {
6378 case 0:
6379 move = -width - left_slack;
6380 break;
6381 case 1:
6382 move = -width/2 + (right_slack - left_slack)/2;
6383 break;
6384 case 2:
6385 move = right_slack;
6386 break;
6387 }
6388 }
6389 Geom::Point XY = SP_TEXT(item)->attributes.firstXY();
6390 if (axis == NR::X) {
6391 XY = XY + Geom::Point (move, 0);
6392 } else {
6393 XY = XY + Geom::Point (0, move);
6394 }
6395 SP_TEXT(item)->attributes.setFirstXY(XY);
6396 SP_OBJECT(item)->updateRepr();
6397 SP_OBJECT(item)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
6398 }
6399 }
6401 SPCSSAttr *css = sp_repr_css_attr_new ();
6402 switch (prop)
6403 {
6404 case 0:
6405 {
6406 sp_repr_css_set_property (css, "text-anchor", "start");
6407 sp_repr_css_set_property (css, "text-align", "start");
6408 break;
6409 }
6410 case 1:
6411 {
6412 sp_repr_css_set_property (css, "text-anchor", "middle");
6413 sp_repr_css_set_property (css, "text-align", "center");
6414 break;
6415 }
6417 case 2:
6418 {
6419 sp_repr_css_set_property (css, "text-anchor", "end");
6420 sp_repr_css_set_property (css, "text-align", "end");
6421 break;
6422 }
6424 case 3:
6425 {
6426 sp_repr_css_set_property (css, "text-anchor", "start");
6427 sp_repr_css_set_property (css, "text-align", "justify");
6428 break;
6429 }
6430 }
6432 SPStyle *query =
6433 sp_style_new (SP_ACTIVE_DOCUMENT);
6434 int result_numbers =
6435 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6437 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6438 if (result_numbers == QUERY_STYLE_NOTHING)
6439 {
6440 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6441 prefs->mergeStyle("/tools/text/style", css);
6442 }
6444 sp_style_unref(query);
6446 sp_desktop_set_style (desktop, css, true, true);
6447 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6448 _("Text: Change alignment"));
6449 sp_repr_css_attr_unref (css);
6451 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6452 }
6454 void
6455 sp_text_toolbox_style_toggled (GtkToggleButton *button,
6456 gpointer data)
6457 {
6458 if (g_object_get_data (G_OBJECT (button), "block")) return;
6460 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6461 SPCSSAttr *css = sp_repr_css_attr_new ();
6462 int prop = GPOINTER_TO_INT(data);
6463 bool active = gtk_toggle_button_get_active (button);
6465 SPStyle *query =
6466 sp_style_new (SP_ACTIVE_DOCUMENT);
6468 int result_fontspec =
6469 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6471 //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6472 //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6473 //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6475 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
6476 Glib::ustring newFontSpec = "";
6478 if (fontSpec.empty()) {
6479 // Construct a new font specification if it does not yet exist
6480 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6481 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6482 fontFromStyle->Unref();
6483 }
6485 bool nochange = true;
6486 switch (prop)
6487 {
6488 case 0:
6489 {
6490 if (!fontSpec.empty()) {
6491 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
6492 if (!newFontSpec.empty()) {
6493 // Don't even set the bold if the font didn't exist on the system
6494 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
6495 nochange = false;
6496 }
6497 }
6498 // set or reset the button according
6499 if(nochange) {
6500 gboolean check = gtk_toggle_button_get_active (button);
6502 if (active != check)
6503 {
6504 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6505 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6506 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6507 }
6508 }
6510 break;
6511 }
6513 case 1:
6514 {
6515 if (!fontSpec.empty()) {
6516 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
6517 if (!newFontSpec.empty()) {
6518 // Don't even set the italic if the font didn't exist on the system
6519 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
6520 nochange = false;
6521 }
6522 }
6523 if(nochange) {
6524 gboolean check = gtk_toggle_button_get_active (button);
6526 if (active != check)
6527 {
6528 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6529 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6530 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6531 }
6532 }
6533 break;
6534 }
6535 }
6537 if (!newFontSpec.empty()) {
6538 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6539 }
6541 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6542 if (result_fontspec == QUERY_STYLE_NOTHING)
6543 {
6544 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6545 prefs->mergeStyle("/tools/text/style", css);
6546 }
6548 sp_style_unref(query);
6550 sp_desktop_set_style (desktop, css, true, true);
6551 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6552 _("Text: Change font style"));
6553 sp_repr_css_attr_unref (css);
6555 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6556 }
6558 void
6559 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
6560 gpointer data)
6561 {
6562 if (g_object_get_data (G_OBJECT (button), "block")) {
6563 return;
6564 }
6566 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6567 SPCSSAttr *css = sp_repr_css_attr_new ();
6568 int prop = GPOINTER_TO_INT(data);
6570 switch (prop)
6571 {
6572 case 0:
6573 {
6574 sp_repr_css_set_property (css, "writing-mode", "lr");
6575 break;
6576 }
6578 case 1:
6579 {
6580 sp_repr_css_set_property (css, "writing-mode", "tb");
6581 break;
6582 }
6583 }
6585 SPStyle *query =
6586 sp_style_new (SP_ACTIVE_DOCUMENT);
6587 int result_numbers =
6588 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6590 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6591 if (result_numbers == QUERY_STYLE_NOTHING)
6592 {
6593 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6594 prefs->mergeStyle("/tools/text/style", css);
6595 }
6597 sp_desktop_set_style (desktop, css, true, true);
6598 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6599 _("Text: Change orientation"));
6600 sp_repr_css_attr_unref (css);
6602 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6603 }
6605 gboolean
6606 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6607 {
6608 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6609 if (!desktop) return FALSE;
6611 switch (get_group0_keyval (event)) {
6612 case GDK_KP_Enter: // chosen
6613 case GDK_Return:
6614 // unfreeze and update, which will defocus
6615 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6616 sp_text_toolbox_family_changed (NULL, tbl);
6617 return TRUE; // I consumed the event
6618 break;
6619 case GDK_Escape:
6620 // defocus
6621 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6622 return TRUE; // I consumed the event
6623 break;
6624 }
6625 return FALSE;
6626 }
6628 gboolean
6629 sp_text_toolbox_family_list_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject */*tbl*/)
6630 {
6631 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6632 if (!desktop) return FALSE;
6634 switch (get_group0_keyval (event)) {
6635 case GDK_KP_Enter:
6636 case GDK_Return:
6637 case GDK_Escape: // defocus
6638 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6639 return TRUE; // I consumed the event
6640 break;
6641 case GDK_w:
6642 case GDK_W:
6643 if (event->state & GDK_CONTROL_MASK) {
6644 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6645 return TRUE; // I consumed the event
6646 }
6647 break;
6648 }
6649 return FALSE;
6650 }
6653 void
6654 sp_text_toolbox_size_changed (GtkComboBox *cbox,
6655 GObject *tbl)
6656 {
6657 // quit if run by the _changed callbacks
6658 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6659 return;
6660 }
6662 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6664 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6666 // If this is not from selecting a size in the list (in which case get_active will give the
6667 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
6668 // process this event. This fixes GTK's stupid insistence on sending an activate change every
6669 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
6670 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed")) {
6671 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6672 return;
6673 }
6675 gdouble value = -1;
6676 {
6677 gchar *endptr;
6678 gchar *const text = gtk_combo_box_get_active_text(cbox);
6679 if (text) {
6680 value = g_strtod(text, &endptr);
6681 if (endptr == text) { // Conversion failed, non-numeric input.
6682 value = -1;
6683 }
6684 g_free(text);
6685 }
6686 }
6687 if (value <= 0) {
6688 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6689 return; // could not parse value
6690 }
6692 SPCSSAttr *css = sp_repr_css_attr_new ();
6693 Inkscape::CSSOStringStream osfs;
6694 osfs << value;
6695 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
6697 SPStyle *query =
6698 sp_style_new (SP_ACTIVE_DOCUMENT);
6699 int result_numbers =
6700 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6702 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6703 if (result_numbers == QUERY_STYLE_NOTHING)
6704 {
6705 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6706 prefs->mergeStyle("/tools/text/style", css);
6707 }
6709 sp_style_unref(query);
6711 sp_desktop_set_style (desktop, css, true, true);
6712 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
6713 _("Text: Change font size"));
6714 sp_repr_css_attr_unref (css);
6716 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6718 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6719 }
6721 gboolean
6722 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
6723 {
6724 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6725 if (!desktop) return FALSE;
6727 if (!g_object_get_data (tbl, "esc-pressed")) {
6728 g_object_set_data (tbl, "enter-pressed", gpointer(1));
6729 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6730 sp_text_toolbox_size_changed (cbox, tbl);
6731 g_object_set_data (tbl, "enter-pressed", gpointer(0));
6732 }
6733 return FALSE; // I consumed the event
6734 }
6737 gboolean
6738 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6739 {
6740 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6741 if (!desktop) return FALSE;
6743 switch (get_group0_keyval (event)) {
6744 case GDK_Escape: // defocus
6745 g_object_set_data (tbl, "esc-pressed", gpointer(1));
6746 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6747 g_object_set_data (tbl, "esc-pressed", gpointer(0));
6748 return TRUE; // I consumed the event
6749 break;
6750 case GDK_Return: // defocus
6751 case GDK_KP_Enter:
6752 g_object_set_data (tbl, "enter-pressed", gpointer(1));
6753 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6754 sp_text_toolbox_size_changed (cbox, tbl);
6755 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6756 g_object_set_data (tbl, "enter-pressed", gpointer(0));
6757 return TRUE; // I consumed the event
6758 break;
6759 }
6760 return FALSE;
6761 }
6763 // While editing font name in the entry, disable family_changed by freezing, otherwise completion
6764 // does not work!
6765 gboolean
6766 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
6767 GdkEventFocus */*event*/,
6768 GObject *tbl)
6769 {
6770 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6771 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1); // select all
6772 return FALSE;
6773 }
6775 gboolean
6776 sp_text_toolbox_entry_focus_out (GtkWidget *entry,
6777 GdkEventFocus */*event*/,
6778 GObject *tbl)
6779 {
6780 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6781 gtk_entry_select_region (GTK_ENTRY (entry), 0, 0); // deselect
6782 return FALSE;
6783 }
6785 void
6786 cell_data_func (GtkCellLayout */*cell_layout*/,
6787 GtkCellRenderer *cell,
6788 GtkTreeModel *tree_model,
6789 GtkTreeIter *iter,
6790 gpointer /*data*/)
6791 {
6792 gchar *family;
6793 gtk_tree_model_get(tree_model, iter, 0, &family, -1);
6794 gchar *const family_escaped = g_markup_escape_text(family, -1);
6796 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6797 int show_sample = prefs->getInt("/tools/text/show_sample_in_list", 1);
6798 if (show_sample) {
6800 Glib::ustring sample = prefs->getString("/tools/text/font_sample");
6801 gchar *const sample_escaped = g_markup_escape_text(sample.data(), -1);
6803 std::stringstream markup;
6804 markup << family_escaped << " <span foreground='darkgray' font_family='"
6805 << family_escaped << "'>" << sample_escaped << "</span>";
6806 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6808 g_free(sample_escaped);
6809 } else {
6810 g_object_set (G_OBJECT (cell), "markup", family_escaped, NULL);
6811 }
6813 g_free(family);
6814 g_free(family_escaped);
6815 }
6817 gboolean text_toolbox_completion_match_selected(GtkEntryCompletion */*widget*/,
6818 GtkTreeModel *model,
6819 GtkTreeIter *iter,
6820 GObject *tbl)
6821 {
6822 // We intercept this signal so as to fire family_changed at once (without it, you'd have to
6823 // press Enter again after choosing a completion)
6824 gchar *family = 0;
6825 gtk_tree_model_get(model, iter, 0, &family, -1);
6827 GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6828 gtk_entry_set_text (GTK_ENTRY (entry), family);
6830 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6831 sp_text_toolbox_family_changed (NULL, tbl);
6832 return TRUE;
6833 }
6836 static void
6837 cbe_add_completion (GtkComboBoxEntry *cbe, GObject *tbl){
6838 GtkEntry *entry;
6839 GtkEntryCompletion *completion;
6840 GtkTreeModel *model;
6842 entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(cbe)));
6843 completion = gtk_entry_completion_new();
6844 model = gtk_combo_box_get_model(GTK_COMBO_BOX(cbe));
6845 gtk_entry_completion_set_model(completion, model);
6846 gtk_entry_completion_set_text_column(completion, 0);
6847 gtk_entry_completion_set_inline_completion(completion, FALSE);
6848 gtk_entry_completion_set_inline_selection(completion, FALSE);
6849 gtk_entry_completion_set_popup_completion(completion, TRUE);
6850 gtk_entry_set_completion(entry, completion);
6852 g_signal_connect (G_OBJECT (completion), "match-selected", G_CALLBACK (text_toolbox_completion_match_selected), tbl);
6854 g_object_unref(completion);
6855 }
6857 void sp_text_toolbox_family_popnotify(GtkComboBox *widget,
6858 void */*property*/,
6859 GObject *tbl)
6860 {
6861 // while the drop-down is open, we disable font family changing, reenabling it only when it closes
6863 gboolean shown;
6864 g_object_get (G_OBJECT(widget), "popup-shown", &shown, NULL);
6865 if (shown) {
6866 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6867 //g_print("POP: notify: SHOWN\n");
6868 } else {
6869 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6871 // stupid GTK doesn't let us attach to events in the drop-down window, so we peek here to
6872 // find out if the drop down was closed by Enter and if so, manually update (only
6873 // necessary on Windows, on Linux it updates itself - what a mess, but we'll manage)
6874 GdkEvent *ev = gtk_get_current_event();
6875 if (ev) {
6876 //g_print ("ev type: %d\n", ev->type);
6877 if (ev->type == GDK_KEY_PRESS) {
6878 switch (get_group0_keyval ((GdkEventKey *) ev)) {
6879 case GDK_KP_Enter: // chosen
6880 case GDK_Return:
6881 {
6882 // make sure the chosen one is inserted into the entry
6883 GtkComboBox *combo = GTK_COMBO_BOX (((Gtk::ComboBox *) (g_object_get_data (tbl, "family-entry-combo")))->gobj());
6884 GtkTreeModel *model = gtk_combo_box_get_model(combo);
6885 GtkTreeIter iter;
6886 gboolean has_active = gtk_combo_box_get_active_iter (combo, &iter);
6887 if (has_active) {
6888 gchar *family;
6889 gtk_tree_model_get(model, &iter, 0, &family, -1);
6890 GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6891 gtk_entry_set_text (GTK_ENTRY (entry), family);
6892 }
6894 // update
6895 sp_text_toolbox_family_changed (NULL, tbl);
6896 break;
6897 }
6898 }
6899 }
6900 }
6902 // regardless of whether we updated, defocus the widget
6903 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6904 if (desktop)
6905 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6906 //g_print("POP: notify: HIDDEN\n");
6907 }
6908 }
6910 GtkWidget *sp_text_toolbox_new (SPDesktop *desktop)
6911 {
6912 GtkToolbar *tbl = GTK_TOOLBAR(gtk_toolbar_new());
6913 GtkIconSize secondarySize = static_cast<GtkIconSize>(ToolboxFactory::prefToSize("/toolbox/secondary", 1));
6915 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
6916 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
6918 GtkTooltips *tt = gtk_tooltips_new();
6920 ////////////Family
6921 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
6922 Gtk::ComboBoxEntry *font_sel = Gtk::manage(new Gtk::ComboBoxEntry(store));
6924 gtk_rc_parse_string (
6925 "style \"dropdown-as-list-style\"\n"
6926 "{\n"
6927 " GtkComboBox::appears-as-list = 1\n"
6928 "}\n"
6929 "widget \"*.toolbox-fontfamily-list\" style \"dropdown-as-list-style\"");
6930 gtk_widget_set_name(GTK_WIDGET (font_sel->gobj()), "toolbox-fontfamily-list");
6931 gtk_tooltips_set_tip (tt, GTK_WIDGET (font_sel->gobj()), _("Select font family (Alt+X to access)"), "");
6933 g_signal_connect (G_OBJECT (font_sel->gobj()), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
6935 cbe_add_completion(font_sel->gobj(), G_OBJECT(tbl));
6937 gtk_toolbar_append_widget( tbl, (GtkWidget*) font_sel->gobj(), "", "");
6938 g_object_set_data (G_OBJECT (tbl), "family-entry-combo", font_sel);
6940 // expand the field a bit so as to view more of the previews in the drop-down
6941 GtkRequisition req;
6942 gtk_widget_size_request (GTK_WIDGET (font_sel->gobj()), &req);
6943 gtk_widget_set_size_request (GTK_WIDGET (font_sel->gobj()), MIN(req.width + 50, 500), -1);
6945 GtkWidget* entry = (GtkWidget*) font_sel->get_entry()->gobj();
6946 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6948 g_signal_connect (G_OBJECT (font_sel->gobj()), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6949 g_signal_connect (G_OBJECT (font_sel->gobj()), "notify::popup-shown",
6950 G_CALLBACK (sp_text_toolbox_family_popnotify), tbl);
6951 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
6952 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
6953 g_signal_connect (G_OBJECT (entry), "focus-out-event", G_CALLBACK (sp_text_toolbox_entry_focus_out), tbl);
6955 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
6956 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
6958 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
6959 gtk_cell_layout_clear( GTK_CELL_LAYOUT(font_sel->gobj()) );
6960 gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(font_sel->gobj()) , cell , TRUE );
6961 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT(font_sel->gobj()), cell, GtkCellLayoutDataFunc (cell_data_func), NULL, NULL);
6963 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
6964 GtkWidget *box = gtk_event_box_new ();
6965 gtk_container_add (GTK_CONTAINER (box), image);
6966 gtk_toolbar_append_widget( tbl, box, "", "");
6967 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
6968 gtk_tooltips_set_tip (tt, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
6969 gtk_widget_hide (GTK_WIDGET (box));
6970 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
6972 ////////////Size
6973 gchar const *const sizes[] = {
6974 "4", "6", "8", "9", "10", "11", "12", "13", "14",
6975 "16", "18", "20", "22", "24", "28",
6976 "32", "36", "40", "48", "56", "64", "72", "144"
6977 };
6979 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
6980 for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
6981 gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
6982 }
6983 gtk_widget_set_size_request (cbox, 80, -1);
6984 gtk_toolbar_append_widget( tbl, cbox, "", "");
6985 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
6986 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
6987 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
6988 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
6990 ////////////Text anchor
6991 GtkWidget *group = gtk_radio_button_new (NULL);
6992 GtkWidget *row = gtk_hbox_new (FALSE, 4);
6993 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
6995 // left
6996 GtkWidget *rbutton = group;
6997 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6998 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
6999 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7001 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7002 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
7003 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
7004 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
7006 // center
7007 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
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_JUSTIFY_CENTER, secondarySize));
7010 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7012 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7013 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
7014 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
7015 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
7017 // right
7018 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
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_JUSTIFY_RIGHT, secondarySize));
7021 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7023 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7024 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
7025 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
7026 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
7028 // fill
7029 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7030 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7031 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
7032 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7034 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7035 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
7036 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
7037 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
7039 gtk_toolbar_append_widget( tbl, row, "", "");
7041 //spacer
7042 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
7044 ////////////Text style
7045 row = gtk_hbox_new (FALSE, 4);
7047 // bold
7048 rbutton = gtk_toggle_button_new ();
7049 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7050 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
7051 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7052 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
7054 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7055 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
7056 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
7058 // italic
7059 rbutton = gtk_toggle_button_new ();
7060 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7061 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
7062 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7063 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
7065 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7066 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
7067 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
7069 gtk_toolbar_append_widget( tbl, row, "", "");
7071 //spacer
7072 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
7074 // Text orientation
7075 group = gtk_radio_button_new (NULL);
7076 row = gtk_hbox_new (FALSE, 4);
7077 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
7079 // horizontal
7080 rbutton = group;
7081 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7082 gtk_container_add (GTK_CONTAINER (rbutton),
7083 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_HORIZONTAL));
7084 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7085 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
7087 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7088 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
7089 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
7091 // vertical
7092 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7093 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7094 gtk_container_add (GTK_CONTAINER (rbutton),
7095 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_VERTICAL));
7096 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7097 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
7099 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7100 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
7101 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
7102 gtk_toolbar_append_widget( tbl, row, "", "" );
7105 //watch selection
7106 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
7108 sigc::connection *c_selection_changed =
7109 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
7110 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
7111 pool->add_connection ("selection-changed", c_selection_changed);
7113 sigc::connection *c_selection_modified =
7114 new sigc::connection (sp_desktop_selection (desktop)->connectModified
7115 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
7116 pool->add_connection ("selection-modified", c_selection_modified);
7118 sigc::connection *c_subselection_changed =
7119 new sigc::connection (desktop->connectToolSubselectionChanged
7120 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
7121 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
7123 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
7126 gtk_widget_show_all( GTK_WIDGET(tbl) );
7128 return GTK_WIDGET(tbl);
7129 } // end of sp_text_toolbox_new()
7131 }//<unnamed> namespace
7134 //#########################
7135 //## Connector ##
7136 //#########################
7138 static void sp_connector_mode_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7139 {
7140 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7141 prefs->setBool("/tools/connector/mode",
7142 gtk_toggle_action_get_active( act ));
7143 }
7145 static void sp_connector_path_set_avoid(void)
7146 {
7147 cc_selection_set_avoid(true);
7148 }
7151 static void sp_connector_path_set_ignore(void)
7152 {
7153 cc_selection_set_avoid(false);
7154 }
7156 static void sp_connector_orthogonal_toggled( GtkToggleAction* act, GObject *tbl )
7157 {
7158 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7159 Inkscape::Selection * selection = sp_desktop_selection(desktop);
7160 SPDocument *doc = sp_desktop_document(desktop);
7162 if (!sp_document_get_undo_sensitive(doc))
7163 {
7164 return;
7165 }
7168 // quit if run by the _changed callbacks
7169 if (g_object_get_data( tbl, "freeze" )) {
7170 return;
7171 }
7173 // in turn, prevent callbacks from responding
7174 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7176 bool is_orthog = gtk_toggle_action_get_active( act );
7177 gchar orthog_str[] = "orthogonal";
7178 gchar polyline_str[] = "polyline";
7179 gchar *value = is_orthog ? orthog_str : polyline_str ;
7181 bool modmade = false;
7182 GSList *l = (GSList *) selection->itemList();
7183 while (l) {
7184 SPItem *item = (SPItem *) l->data;
7186 if (cc_item_is_connector(item)) {
7187 sp_object_setAttribute(item, "inkscape:connector-type",
7188 value, false);
7189 item->avoidRef->handleSettingChange();
7190 modmade = true;
7191 }
7192 l = l->next;
7193 }
7195 if (!modmade)
7196 {
7197 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7198 prefs->setBool("/tools/connector/orthogonal", is_orthog);
7199 }
7201 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7202 is_orthog ? _("Set connector type: orthogonal"): _("Set connector type: polyline"));
7204 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7205 }
7207 static void connector_curvature_changed(GtkAdjustment *adj, GObject* tbl)
7208 {
7209 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7210 Inkscape::Selection * selection = sp_desktop_selection(desktop);
7211 SPDocument *doc = sp_desktop_document(desktop);
7213 if (!sp_document_get_undo_sensitive(doc))
7214 {
7215 return;
7216 }
7219 // quit if run by the _changed callbacks
7220 if (g_object_get_data( tbl, "freeze" )) {
7221 return;
7222 }
7224 // in turn, prevent callbacks from responding
7225 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7227 gdouble newValue = gtk_adjustment_get_value(adj);
7228 gchar value[G_ASCII_DTOSTR_BUF_SIZE];
7229 g_ascii_dtostr(value, G_ASCII_DTOSTR_BUF_SIZE, newValue);
7231 bool modmade = false;
7232 GSList *l = (GSList *) selection->itemList();
7233 while (l) {
7234 SPItem *item = (SPItem *) l->data;
7236 if (cc_item_is_connector(item)) {
7237 sp_object_setAttribute(item, "inkscape:connector-curvature",
7238 value, false);
7239 item->avoidRef->handleSettingChange();
7240 modmade = true;
7241 }
7242 l = l->next;
7243 }
7245 if (!modmade)
7246 {
7247 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7248 prefs->setDouble(Glib::ustring("/tools/connector/curvature"), newValue);
7249 }
7251 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7252 _("Change connector curvature"));
7254 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7255 }
7258 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
7259 {
7260 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7261 SPDocument *doc = sp_desktop_document(desktop);
7263 if (!sp_document_get_undo_sensitive(doc))
7264 {
7265 return;
7266 }
7268 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7270 if ( !repr->attribute("inkscape:connector-spacing") &&
7271 ( adj->value == defaultConnSpacing )) {
7272 // Don't need to update the repr if the attribute doesn't
7273 // exist and it is being set to the default value -- as will
7274 // happen at startup.
7275 return;
7276 }
7278 // quit if run by the attr_changed listener
7279 if (g_object_get_data( tbl, "freeze" )) {
7280 return;
7281 }
7283 // in turn, prevent listener from responding
7284 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
7286 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
7287 SP_OBJECT(desktop->namedview)->updateRepr();
7289 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
7290 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
7291 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
7292 Geom::Matrix m = Geom::identity();
7293 avoid_item_move(&m, item);
7294 }
7296 if (items) {
7297 g_slist_free(items);
7298 }
7300 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7301 _("Change connector spacing"));
7303 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7304 }
7306 static void sp_connector_graph_layout(void)
7307 {
7308 if (!SP_ACTIVE_DESKTOP) return;
7309 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7311 // hack for clones, see comment in align-and-distribute.cpp
7312 int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7313 prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7315 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
7317 prefs->setInt("/options/clonecompensation/value", saved_compensation);
7319 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
7320 }
7322 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7323 {
7324 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7325 prefs->setBool("/tools/connector/directedlayout",
7326 gtk_toggle_action_get_active( act ));
7327 }
7329 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7330 {
7331 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7332 prefs->setBool("/tools/connector/avoidoverlaplayout",
7333 gtk_toggle_action_get_active( act ));
7334 }
7337 static void connector_length_changed(GtkAdjustment *adj, GObject* /*tbl*/)
7338 {
7339 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7340 prefs->setDouble("/tools/connector/length", adj->value);
7341 }
7343 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
7344 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
7345 bool /*is_interactive*/, gpointer data)
7346 {
7347 GtkWidget *tbl = GTK_WIDGET(data);
7349 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
7350 return;
7351 }
7352 if (strcmp(name, "inkscape:connector-spacing") == 0)
7353 {
7354 GtkAdjustment *adj = (GtkAdjustment*)
7355 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
7356 gdouble spacing = defaultConnSpacing;
7357 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
7359 gtk_adjustment_set_value(adj, spacing);
7360 gtk_adjustment_value_changed(adj);
7361 }
7363 spinbutton_defocus(GTK_OBJECT(tbl));
7364 }
7366 static void sp_connector_new_connection_point(GtkWidget *, GObject *tbl)
7367 {
7368 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7369 SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7371 if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE)
7372 cc_create_connection_point(cc);
7373 }
7375 static void sp_connector_remove_connection_point(GtkWidget *, GObject *tbl)
7376 {
7377 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7378 SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7380 if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE)
7381 cc_remove_connection_point(cc);
7382 }
7384 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
7385 NULL, /* child_added */
7386 NULL, /* child_removed */
7387 connector_tb_event_attr_changed,
7388 NULL, /* content_changed */
7389 NULL /* order_changed */
7390 };
7392 static void sp_connector_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
7393 {
7394 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "curvature" ) );
7395 GtkToggleAction *act = GTK_TOGGLE_ACTION( g_object_get_data( tbl, "orthogonal" ) );
7396 SPItem *item = selection->singleItem();
7397 if (SP_IS_PATH(item))
7398 {
7399 gdouble curvature = SP_PATH(item)->connEndPair.getCurvature();
7400 bool is_orthog = SP_PATH(item)->connEndPair.isOrthogonal();
7401 gtk_toggle_action_set_active(act, is_orthog);
7402 gtk_adjustment_set_value(adj, curvature);
7403 }
7405 }
7407 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
7408 {
7409 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7410 Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
7412 // Editing mode toggle button
7413 {
7414 InkToggleAction* act = ink_toggle_action_new( "ConnectorEditModeAction",
7415 _("EditMode"),
7416 _("Switch between connection point editing and connector drawing mode"),
7417 INKSCAPE_ICON_CONNECTOR_EDIT,
7418 Inkscape::ICON_SIZE_DECORATION );
7419 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7421 bool tbuttonstate = prefs->getBool("/tools/connector/mode");
7422 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7423 g_object_set_data( holder, "mode", act );
7424 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_mode_toggled), holder );
7425 }
7428 {
7429 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
7430 _("Avoid"),
7431 _("Make connectors avoid selected objects"),
7432 INKSCAPE_ICON_CONNECTOR_AVOID,
7433 secondarySize );
7434 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
7435 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7436 }
7438 {
7439 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
7440 _("Ignore"),
7441 _("Make connectors ignore selected objects"),
7442 INKSCAPE_ICON_CONNECTOR_IGNORE,
7443 secondarySize );
7444 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
7445 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7446 }
7448 // Orthogonal connectors toggle button
7449 {
7450 InkToggleAction* act = ink_toggle_action_new( "ConnectorOrthogonalAction",
7451 _("Orthogonal"),
7452 _("Make connector orthogonal or polyline"),
7453 INKSCAPE_ICON_CONNECTOR_ORTHOGONAL,
7454 Inkscape::ICON_SIZE_DECORATION );
7455 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7457 bool tbuttonstate = prefs->getBool("/tools/connector/orthogonal");
7458 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7459 g_object_set_data( holder, "orthogonal", act );
7460 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_orthogonal_toggled), holder );
7461 }
7463 EgeAdjustmentAction* eact = 0;
7464 // Curvature spinbox
7465 eact = create_adjustment_action( "ConnectorCurvatureAction",
7466 _("Connector Curvature"), _("Curvature:"),
7467 _("The amount of connectors curvature"),
7468 "/tools/connector/curvature", defaultConnCurvature,
7469 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-curvature",
7470 0, 100, 1.0, 10.0,
7471 0, 0, 0,
7472 connector_curvature_changed, 1, 0 );
7473 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7475 // Spacing spinbox
7476 eact = create_adjustment_action( "ConnectorSpacingAction",
7477 _("Connector Spacing"), _("Spacing:"),
7478 _("The amount of space left around objects by auto-routing connectors"),
7479 "/tools/connector/spacing", defaultConnSpacing,
7480 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
7481 0, 100, 1.0, 10.0,
7482 0, 0, 0,
7483 connector_spacing_changed, 1, 0 );
7484 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7486 // Graph (connector network) layout
7487 {
7488 InkAction* inky = ink_action_new( "ConnectorGraphAction",
7489 _("Graph"),
7490 _("Nicely arrange selected connector network"),
7491 INKSCAPE_ICON_DISTRIBUTE_GRAPH,
7492 secondarySize );
7493 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
7494 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7495 }
7497 // Default connector length spinbox
7498 eact = create_adjustment_action( "ConnectorLengthAction",
7499 _("Connector Length"), _("Length:"),
7500 _("Ideal length for connectors when layout is applied"),
7501 "/tools/connector/length", 100,
7502 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
7503 10, 1000, 10.0, 100.0,
7504 0, 0, 0,
7505 connector_length_changed, 1, 0 );
7506 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7509 // Directed edges toggle button
7510 {
7511 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
7512 _("Downwards"),
7513 _("Make connectors with end-markers (arrows) point downwards"),
7514 INKSCAPE_ICON_DISTRIBUTE_GRAPH_DIRECTED,
7515 Inkscape::ICON_SIZE_DECORATION );
7516 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7518 bool tbuttonstate = prefs->getBool("/tools/connector/directedlayout");
7519 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7521 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
7522 sigc::connection *connection = new sigc::connection(sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_connector_toolbox_selection_changed), (GObject *)holder))
7523 );
7524 }
7526 // Avoid overlaps toggle button
7527 {
7528 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
7529 _("Remove overlaps"),
7530 _("Do not allow overlapping shapes"),
7531 INKSCAPE_ICON_DISTRIBUTE_REMOVE_OVERLAPS,
7532 Inkscape::ICON_SIZE_DECORATION );
7533 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7535 bool tbuttonstate = prefs->getBool("/tools/connector/avoidoverlaplayout");
7536 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), (tbuttonstate ? TRUE : FALSE ));
7538 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
7539 }
7542 // New connection point button
7543 {
7544 InkAction* inky = ink_action_new( "ConnectorNewConnPointAction",
7545 _("New connection point"),
7546 _("Add a new connection point to the currently selected item"),
7547 INKSCAPE_ICON_CONNECTOR_NEW_CONNPOINT,
7548 secondarySize );
7549 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_new_connection_point), holder );
7550 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7551 }
7553 // Remove selected connection point button
7555 {
7556 InkAction* inky = ink_action_new( "ConnectorRemoveConnPointAction",
7557 _("Remove connection point"),
7558 _("Remove the currently selected connection point"),
7559 INKSCAPE_ICON_CONNECTOR_REMOVE_CONNPOINT,
7560 secondarySize );
7561 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_remove_connection_point), holder );
7562 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7563 }
7566 // Code to watch for changes to the connector-spacing attribute in
7567 // the XML.
7568 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7569 g_assert(repr != NULL);
7571 purge_repr_listener( holder, holder );
7573 if (repr) {
7574 g_object_set_data( holder, "repr", repr );
7575 Inkscape::GC::anchor(repr);
7576 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
7577 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
7578 }
7579 } // end of sp_connector_toolbox_prep()
7582 //#########################
7583 //## Paintbucket ##
7584 //#########################
7586 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
7587 {
7588 gint channels = ege_select_one_action_get_active( act );
7589 flood_channels_set_channels( channels );
7590 }
7592 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
7593 {
7594 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7595 prefs->setInt("/tools/paintbucket/threshold", (gint)adj->value);
7596 }
7598 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
7599 {
7600 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7601 prefs->setBool("/tools/paintbucket/autogap", ege_select_one_action_get_active( act ));
7602 }
7604 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
7605 {
7606 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
7607 SPUnit const *unit = tracker->getActiveUnit();
7608 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7610 prefs->setDouble("/tools/paintbucket/offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
7611 prefs->setString("/tools/paintbucket/offsetunits", sp_unit_get_abbreviation(unit));
7612 }
7614 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
7615 {
7616 // FIXME: make defaults settable via Inkscape Options
7617 struct KeyValue {
7618 char const *key;
7619 double value;
7620 } const key_values[] = {
7621 {"threshold", 15},
7622 {"offset", 0.0}
7623 };
7625 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
7626 KeyValue const &kv = key_values[i];
7627 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
7628 if ( adj ) {
7629 gtk_adjustment_set_value(adj, kv.value);
7630 }
7631 }
7633 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
7634 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
7635 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
7636 ege_select_one_action_set_active( autogap_action, 0 );
7637 }
7639 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
7640 {
7641 EgeAdjustmentAction* eact = 0;
7642 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7644 {
7645 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7647 GList* items = 0;
7648 gint count = 0;
7649 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
7650 {
7651 GtkTreeIter iter;
7652 gtk_list_store_append( model, &iter );
7653 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7654 count++;
7655 }
7656 g_list_free( items );
7657 items = 0;
7658 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
7659 g_object_set( act1, "short_label", _("Fill by:"), NULL );
7660 ege_select_one_action_set_appearance( act1, "compact" );
7661 ege_select_one_action_set_active( act1, prefs->getInt("/tools/paintbucket/channels", 0) );
7662 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
7663 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
7664 g_object_set_data( holder, "channels_action", act1 );
7665 }
7667 // Spacing spinbox
7668 {
7669 eact = create_adjustment_action(
7670 "ThresholdAction",
7671 _("Fill Threshold"), _("Threshold:"),
7672 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
7673 "/tools/paintbucket/threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
7674 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
7675 0, 0, 0,
7676 paintbucket_threshold_changed, 1, 0 );
7678 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
7679 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7680 }
7682 // Create the units menu.
7683 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
7684 Glib::ustring stored_unit = prefs->getString("/tools/paintbucket/offsetunits");
7685 if (!stored_unit.empty())
7686 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit.data()));
7687 g_object_set_data( holder, "tracker", tracker );
7688 {
7689 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
7690 gtk_action_group_add_action( mainActions, act );
7691 }
7693 // Offset spinbox
7694 {
7695 eact = create_adjustment_action(
7696 "OffsetAction",
7697 _("Grow/shrink by"), _("Grow/shrink by:"),
7698 _("The amount to grow (positive) or shrink (negative) the created fill path"),
7699 "/tools/paintbucket/offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
7700 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
7701 0, 0, 0,
7702 paintbucket_offset_changed, 1, 2);
7703 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
7705 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7706 }
7708 /* Auto Gap */
7709 {
7710 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7712 GList* items = 0;
7713 gint count = 0;
7714 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
7715 {
7716 GtkTreeIter iter;
7717 gtk_list_store_append( model, &iter );
7718 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7719 count++;
7720 }
7721 g_list_free( items );
7722 items = 0;
7723 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
7724 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
7725 ege_select_one_action_set_appearance( act2, "compact" );
7726 ege_select_one_action_set_active( act2, prefs->getBool("/tools/paintbucket/autogap") );
7727 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
7728 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
7729 g_object_set_data( holder, "autogap_action", act2 );
7730 }
7732 /* Reset */
7733 {
7734 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
7735 _("Defaults"),
7736 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
7737 GTK_STOCK_CLEAR );
7738 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
7739 gtk_action_group_add_action( mainActions, act );
7740 gtk_action_set_sensitive( act, TRUE );
7741 }
7743 }
7745 /*
7746 Local Variables:
7747 mode:c++
7748 c-file-style:"stroustrup"
7749 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
7750 indent-tabs-mode:nil
7751 fill-column:99
7752 End:
7753 */
7754 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :