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