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( 3, G_TYPE_STRING, 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 2, 0,
849 -1 );
851 gtk_list_store_append( model, &iter );
852 gtk_list_store_set( model, &iter,
853 0, _("Custom"),
854 1, _("Set the custom task"),
855 2, 0,
856 -1 );
858 EgeSelectOneAction* act = ege_select_one_action_new( "TaskSetAction", _("Task"), (""), NULL, GTK_TREE_MODEL(model) );
859 g_object_set( act, "short_label", _("Task:"), NULL );
860 mainActions->add(Glib::wrap(GTK_ACTION(act)));
861 //g_object_set_data( holder, "mode_action", act );
863 ege_select_one_action_set_appearance( act, "minimal" );
864 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
865 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
866 ege_select_one_action_set_icon_column( act, 2 );
867 //ege_select_one_action_set_icon_size( act, secondarySize );
868 ege_select_one_action_set_tooltip_column( act, 1 );
870 //ege_select_one_action_set_active( act, mode );
871 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(fireTaskChange), desktop );
872 }
874 return mainActions;
875 }
878 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
879 {
880 gtk_widget_set_size_request( widget,
881 widget->allocation.width,
882 widget->allocation.height );
883 }
885 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
886 {
887 gtk_widget_set_size_request( widget, -1, -1 );
888 }
890 static GtkWidget* toolboxNewCommon( GtkWidget* tb, BarId id, GtkPositionType handlePos )
891 {
892 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
894 gtk_widget_set_sensitive(tb, FALSE);
896 GtkWidget *hb = gtk_handle_box_new();
897 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), handlePos);
898 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
899 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
901 gtk_container_add(GTK_CONTAINER(hb), tb);
902 gtk_widget_show(GTK_WIDGET(tb));
904 sigc::connection* conn = new sigc::connection;
905 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
907 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
908 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
910 gpointer val = GINT_TO_POINTER(id);
911 g_object_set_data(G_OBJECT(hb), BAR_ID_KEY, val);
913 return hb;
914 }
916 GtkWidget *ToolboxFactory::createToolToolbox()
917 {
918 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
920 return toolboxNewCommon( tb, BAR_TOOL, GTK_POS_TOP );
921 }
923 GtkWidget *ToolboxFactory::createAuxToolbox()
924 {
925 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
927 return toolboxNewCommon( tb, BAR_AUX, GTK_POS_LEFT );
928 }
930 //####################################
931 //# Commands Bar
932 //####################################
934 GtkWidget *ToolboxFactory::createCommandsToolbox()
935 {
936 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
938 return toolboxNewCommon( tb, BAR_COMMANDS, GTK_POS_LEFT );
939 }
941 GtkWidget *ToolboxFactory::createSnapToolbox()
942 {
943 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
945 return toolboxNewCommon( tb, BAR_SNAP, GTK_POS_LEFT );
946 }
948 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
949 gchar const *label, gchar const *shortLabel, gchar const *tooltip,
950 Glib::ustring const &path, gdouble def,
951 GtkWidget *focusTarget,
952 GtkWidget *us,
953 GObject *dataKludge,
954 gboolean altx, gchar const *altx_mark,
955 gdouble lower, gdouble upper, gdouble step, gdouble page,
956 gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
957 void (*callback)(GtkAdjustment *, GObject *),
958 gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
959 {
960 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
961 GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs->getDouble(path, def) * factor,
962 lower, upper, step, page, 0 ) );
963 if (us) {
964 sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
965 }
967 gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
969 EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
970 if ( shortLabel ) {
971 g_object_set( act, "short_label", shortLabel, NULL );
972 }
974 if ( (descrCount > 0) && descrLabels && descrValues ) {
975 ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
976 }
978 if ( focusTarget ) {
979 ege_adjustment_action_set_focuswidget( act, focusTarget );
980 }
982 if ( altx && altx_mark ) {
983 g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
984 }
986 if ( dataKludge ) {
987 // Rather lame, but it's the only place where we need to get the entry name
988 // but we don't have an Entry
989 g_object_set_data( dataKludge, prefs->getEntry(path).getEntryName().data(), adj );
990 }
992 // Using a cast just to make sure we pass in the right kind of function pointer
993 g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
995 return act;
996 }
999 //####################################
1000 //# node editing callbacks
1001 //####################################
1003 /**
1004 * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
1005 */
1006 static ShapeEditor *get_current_shape_editor()
1007 {
1008 if (!SP_ACTIVE_DESKTOP) {
1009 return NULL;
1010 }
1012 SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
1014 if (!SP_IS_NODE_CONTEXT(event_context)) {
1015 return NULL;
1016 }
1018 return event_context->shape_editor;
1019 }
1022 void
1023 sp_node_path_edit_add(void)
1024 {
1025 ShapeEditor *shape_editor = get_current_shape_editor();
1026 if (shape_editor) shape_editor->add_node();
1027 }
1029 void
1030 sp_node_path_edit_delete(void)
1031 {
1032 ShapeEditor *shape_editor = get_current_shape_editor();
1033 if (shape_editor) shape_editor->delete_nodes_preserving_shape();
1034 }
1036 void
1037 sp_node_path_edit_delete_segment(void)
1038 {
1039 ShapeEditor *shape_editor = get_current_shape_editor();
1040 if (shape_editor) shape_editor->delete_segment();
1041 }
1043 void
1044 sp_node_path_edit_break(void)
1045 {
1046 ShapeEditor *shape_editor = get_current_shape_editor();
1047 if (shape_editor) shape_editor->break_at_nodes();
1048 }
1050 void
1051 sp_node_path_edit_join(void)
1052 {
1053 ShapeEditor *shape_editor = get_current_shape_editor();
1054 if (shape_editor) shape_editor->join_nodes();
1055 }
1057 void
1058 sp_node_path_edit_join_segment(void)
1059 {
1060 ShapeEditor *shape_editor = get_current_shape_editor();
1061 if (shape_editor) shape_editor->join_segments();
1062 }
1064 void
1065 sp_node_path_edit_toline(void)
1066 {
1067 ShapeEditor *shape_editor = get_current_shape_editor();
1068 if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1069 }
1071 void
1072 sp_node_path_edit_tocurve(void)
1073 {
1074 ShapeEditor *shape_editor = get_current_shape_editor();
1075 if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1076 }
1078 void
1079 sp_node_path_edit_cusp(void)
1080 {
1081 ShapeEditor *shape_editor = get_current_shape_editor();
1082 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1083 }
1085 void
1086 sp_node_path_edit_smooth(void)
1087 {
1088 ShapeEditor *shape_editor = get_current_shape_editor();
1089 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1090 }
1092 void
1093 sp_node_path_edit_symmetrical(void)
1094 {
1095 ShapeEditor *shape_editor = get_current_shape_editor();
1096 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1097 }
1099 void
1100 sp_node_path_edit_auto(void)
1101 {
1102 ShapeEditor *shape_editor = get_current_shape_editor();
1103 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_AUTO);
1104 }
1106 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1107 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1108 bool show = gtk_toggle_action_get_active( act );
1109 prefs->setBool("/tools/nodes/show_handles", show);
1110 ShapeEditor *shape_editor = get_current_shape_editor();
1111 if (shape_editor) shape_editor->show_handles(show);
1112 }
1114 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1115 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1116 bool show = gtk_toggle_action_get_active( act );
1117 prefs->setBool("/tools/nodes/show_helperpath", show);
1118 ShapeEditor *shape_editor = get_current_shape_editor();
1119 if (shape_editor) shape_editor->show_helperpath(show);
1120 }
1122 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1123 sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1124 }
1126 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1127 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1128 }
1130 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1131 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1132 }
1134 /* is called when the node selection is modified */
1135 static void
1136 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1137 {
1138 GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1139 GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1140 GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1141 GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1143 // quit if run by the attr_changed listener
1144 if (g_object_get_data( tbl, "freeze" )) {
1145 return;
1146 }
1148 // in turn, prevent listener from responding
1149 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1151 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1152 SPUnit const *unit = tracker->getActiveUnit();
1154 ShapeEditor *shape_editor = get_current_shape_editor();
1155 if (shape_editor && shape_editor->has_nodepath()) {
1156 Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1157 int n_selected = 0;
1158 if (nodepath) {
1159 n_selected = nodepath->numSelected();
1160 }
1162 if (n_selected == 0) {
1163 gtk_action_set_sensitive(xact, FALSE);
1164 gtk_action_set_sensitive(yact, FALSE);
1165 } else {
1166 gtk_action_set_sensitive(xact, TRUE);
1167 gtk_action_set_sensitive(yact, TRUE);
1168 Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1169 Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1171 if (n_selected == 1) {
1172 Geom::Point sel_node = nodepath->singleSelectedCoords();
1173 if (oldx != sel_node[Geom::X] || oldy != sel_node[Geom::Y]) {
1174 gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[Geom::X], *unit));
1175 gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[Geom::Y], *unit));
1176 }
1177 } else {
1178 boost::optional<Geom::Coord> x = sp_node_selected_common_coord(nodepath, Geom::X);
1179 boost::optional<Geom::Coord> y = sp_node_selected_common_coord(nodepath, Geom::Y);
1180 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1181 /* Note: Currently x and y will always have a value, even if the coordinates of the
1182 selected nodes don't coincide (in this case we use the coordinates of the center
1183 of the bounding box). So the entries are never set to zero. */
1184 // FIXME: Maybe we should clear the entry if several nodes are selected
1185 // instead of providing a kind of average value
1186 gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1187 gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1188 }
1189 }
1190 }
1191 } else {
1192 // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1193 gtk_action_set_sensitive(xact, FALSE);
1194 gtk_action_set_sensitive(yact, FALSE);
1195 }
1197 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1198 }
1200 static void
1201 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1202 {
1203 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1204 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1206 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1207 SPUnit const *unit = tracker->getActiveUnit();
1209 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1210 prefs->setDouble(Glib::ustring("/tools/nodes/") + value_name, sp_units_get_pixels(adj->value, *unit));
1211 }
1213 // quit if run by the attr_changed listener
1214 if (g_object_get_data( tbl, "freeze" )) {
1215 return;
1216 }
1218 // in turn, prevent listener from responding
1219 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1221 ShapeEditor *shape_editor = get_current_shape_editor();
1222 if (shape_editor && shape_editor->has_nodepath()) {
1223 double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1224 if (!strcmp(value_name, "x")) {
1225 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::X);
1226 }
1227 if (!strcmp(value_name, "y")) {
1228 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::Y);
1229 }
1230 }
1232 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1233 }
1235 static void
1236 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1237 {
1238 sp_node_path_value_changed(adj, tbl, "x");
1239 }
1241 static void
1242 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1243 {
1244 sp_node_path_value_changed(adj, tbl, "y");
1245 }
1247 void
1248 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1249 {
1250 {
1251 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1252 SPItem *item = selection->singleItem();
1253 if (item && SP_IS_LPE_ITEM(item)) {
1254 if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1255 gtk_action_set_sensitive(w, TRUE);
1256 } else {
1257 gtk_action_set_sensitive(w, FALSE);
1258 }
1259 } else {
1260 gtk_action_set_sensitive(w, FALSE);
1261 }
1262 }
1264 {
1265 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1266 SPItem *item = selection->singleItem();
1267 if (item && item->clip_ref && item->clip_ref->getObject()) {
1268 gtk_action_set_sensitive(w, TRUE);
1269 } else {
1270 gtk_action_set_sensitive(w, FALSE);
1271 }
1272 }
1274 {
1275 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1276 SPItem *item = selection->singleItem();
1277 if (item && item->mask_ref && item->mask_ref->getObject()) {
1278 gtk_action_set_sensitive(w, TRUE);
1279 } else {
1280 gtk_action_set_sensitive(w, FALSE);
1281 }
1282 }
1283 }
1285 void
1286 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1287 {
1288 sp_node_toolbox_sel_changed (selection, tbl);
1289 }
1293 //################################
1294 //## Node Editing Toolbox ##
1295 //################################
1297 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1298 {
1299 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1300 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1301 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1302 g_object_set_data( holder, "tracker", tracker );
1304 Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
1306 {
1307 InkAction* inky = ink_action_new( "NodeInsertAction",
1308 _("Insert node"),
1309 _("Insert new nodes into selected segments"),
1310 INKSCAPE_ICON_NODE_ADD,
1311 secondarySize );
1312 g_object_set( inky, "short_label", _("Insert"), NULL );
1313 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1314 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1315 }
1317 {
1318 InkAction* inky = ink_action_new( "NodeDeleteAction",
1319 _("Delete node"),
1320 _("Delete selected nodes"),
1321 INKSCAPE_ICON_NODE_DELETE,
1322 secondarySize );
1323 g_object_set( inky, "short_label", _("Delete"), NULL );
1324 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1325 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1326 }
1328 {
1329 InkAction* inky = ink_action_new( "NodeJoinAction",
1330 _("Join endnodes"),
1331 _("Join selected endnodes"),
1332 INKSCAPE_ICON_NODE_JOIN,
1333 secondarySize );
1334 g_object_set( inky, "short_label", _("Join"), NULL );
1335 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1336 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1337 }
1339 {
1340 InkAction* inky = ink_action_new( "NodeBreakAction",
1341 _("Break nodes"),
1342 _("Break path at selected nodes"),
1343 INKSCAPE_ICON_NODE_BREAK,
1344 secondarySize );
1345 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1346 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1347 }
1350 {
1351 InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1352 _("Join with segment"),
1353 _("Join selected endnodes with a new segment"),
1354 INKSCAPE_ICON_NODE_JOIN_SEGMENT,
1355 secondarySize );
1356 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1357 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1358 }
1360 {
1361 InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1362 _("Delete segment"),
1363 _("Delete segment between two non-endpoint nodes"),
1364 INKSCAPE_ICON_NODE_DELETE_SEGMENT,
1365 secondarySize );
1366 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1367 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1368 }
1370 {
1371 InkAction* inky = ink_action_new( "NodeCuspAction",
1372 _("Node Cusp"),
1373 _("Make selected nodes corner"),
1374 INKSCAPE_ICON_NODE_TYPE_CUSP,
1375 secondarySize );
1376 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1377 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1378 }
1380 {
1381 InkAction* inky = ink_action_new( "NodeSmoothAction",
1382 _("Node Smooth"),
1383 _("Make selected nodes smooth"),
1384 INKSCAPE_ICON_NODE_TYPE_SMOOTH,
1385 secondarySize );
1386 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1387 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1388 }
1390 {
1391 InkAction* inky = ink_action_new( "NodeSymmetricAction",
1392 _("Node Symmetric"),
1393 _("Make selected nodes symmetric"),
1394 INKSCAPE_ICON_NODE_TYPE_SYMMETRIC,
1395 secondarySize );
1396 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1397 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1398 }
1400 {
1401 InkAction* inky = ink_action_new( "NodeAutoAction",
1402 _("Node Auto"),
1403 _("Make selected nodes auto-smooth"),
1404 INKSCAPE_ICON_NODE_TYPE_AUTO_SMOOTH,
1405 secondarySize );
1406 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_auto), 0 );
1407 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1408 }
1410 {
1411 InkAction* inky = ink_action_new( "NodeLineAction",
1412 _("Node Line"),
1413 _("Make selected segments lines"),
1414 INKSCAPE_ICON_NODE_SEGMENT_LINE,
1415 secondarySize );
1416 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1417 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1418 }
1420 {
1421 InkAction* inky = ink_action_new( "NodeCurveAction",
1422 _("Node Curve"),
1423 _("Make selected segments curves"),
1424 INKSCAPE_ICON_NODE_SEGMENT_CURVE,
1425 secondarySize );
1426 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1427 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1428 }
1430 {
1431 InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1432 _("Show Handles"),
1433 _("Show the Bezier handles of selected nodes"),
1434 INKSCAPE_ICON_SHOW_NODE_HANDLES,
1435 Inkscape::ICON_SIZE_DECORATION );
1436 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1437 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1438 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_handles", true) );
1439 }
1441 {
1442 InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1443 _("Show Outline"),
1444 _("Show the outline of the path"),
1445 INKSCAPE_ICON_SHOW_PATH_OUTLINE,
1446 Inkscape::ICON_SIZE_DECORATION );
1447 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1448 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1449 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_helperpath", false) );
1450 }
1452 {
1453 InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1454 _("Next path effect parameter"),
1455 _("Show next path effect parameter for editing"),
1456 INKSCAPE_ICON_PATH_EFFECT_PARAMETER_NEXT,
1457 Inkscape::ICON_SIZE_DECORATION );
1458 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1459 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1460 g_object_set_data( holder, "nodes_lpeedit", inky);
1461 }
1463 {
1464 InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1465 _("Edit clipping path"),
1466 _("Edit the clipping path of the object"),
1467 INKSCAPE_ICON_PATH_CLIP_EDIT,
1468 Inkscape::ICON_SIZE_DECORATION );
1469 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1470 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1471 g_object_set_data( holder, "nodes_clippathedit", inky);
1472 }
1474 {
1475 InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1476 _("Edit mask path"),
1477 _("Edit the mask of the object"),
1478 INKSCAPE_ICON_PATH_MASK_EDIT,
1479 Inkscape::ICON_SIZE_DECORATION );
1480 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1481 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1482 g_object_set_data( holder, "nodes_maskedit", inky);
1483 }
1485 /* X coord of selected node(s) */
1486 {
1487 EgeAdjustmentAction* eact = 0;
1488 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1489 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1490 eact = create_adjustment_action( "NodeXAction",
1491 _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1492 "/tools/nodes/Xcoord", 0,
1493 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1494 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1495 labels, values, G_N_ELEMENTS(labels),
1496 sp_node_path_x_value_changed );
1497 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1498 g_object_set_data( holder, "nodes_x_action", eact );
1499 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1500 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1501 }
1503 /* Y coord of selected node(s) */
1504 {
1505 EgeAdjustmentAction* eact = 0;
1506 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1507 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1508 eact = create_adjustment_action( "NodeYAction",
1509 _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1510 "/tools/nodes/Ycoord", 0,
1511 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1512 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1513 labels, values, G_N_ELEMENTS(labels),
1514 sp_node_path_y_value_changed );
1515 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1516 g_object_set_data( holder, "nodes_y_action", eact );
1517 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1518 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1519 }
1521 // add the units menu
1522 {
1523 GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1524 gtk_action_group_add_action( mainActions, act );
1525 }
1528 sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1530 //watch selection
1531 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1533 sigc::connection *c_selection_changed =
1534 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1535 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1536 pool->add_connection ("selection-changed", c_selection_changed);
1538 sigc::connection *c_selection_modified =
1539 new sigc::connection (sp_desktop_selection (desktop)->connectModified
1540 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1541 pool->add_connection ("selection-modified", c_selection_modified);
1543 sigc::connection *c_subselection_changed =
1544 new sigc::connection (desktop->connectToolSubselectionChanged
1545 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1546 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1548 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1550 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1551 } // end of sp_node_toolbox_prep()
1554 //########################
1555 //## Zoom Toolbox ##
1556 //########################
1558 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1559 {
1560 // no custom GtkAction setup needed
1561 } // end of sp_zoom_toolbox_prep()
1563 void ToolboxFactory::setToolboxDesktop(GtkWidget *toolbox, SPDesktop *desktop)
1564 {
1565 sigc::connection *conn = static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1566 "event_context_connection"));
1568 BarId id = static_cast<BarId>( GPOINTER_TO_INT(g_object_get_data(G_OBJECT(toolbox), BAR_ID_KEY)) );
1570 SetupFunction setup_func = 0;
1571 UpdateFunction update_func = 0;
1573 switch (id) {
1574 case BAR_TOOL:
1575 setup_func = setup_tool_toolbox;
1576 update_func = update_tool_toolbox;
1577 break;
1579 case BAR_AUX:
1580 toolbox = gtk_bin_get_child(GTK_BIN(toolbox));
1581 setup_func = setup_aux_toolbox;
1582 update_func = update_aux_toolbox;
1583 break;
1585 case BAR_COMMANDS:
1586 setup_func = setup_commands_toolbox;
1587 update_func = update_commands_toolbox;
1588 break;
1590 case BAR_SNAP:
1591 setup_func = setup_snap_toolbox;
1592 update_func = updateSnapToolbox;
1593 break;
1594 default:
1595 g_warning("Unexpected toolbox id encountered.");
1596 }
1598 gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1599 SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1601 if (old_desktop) {
1602 GList *children, *iter;
1604 children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1605 for ( iter = children ; iter ; iter = iter->next ) {
1606 gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1607 }
1608 g_list_free(children);
1609 }
1611 g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1613 if (desktop && setup_func && update_func) {
1614 gtk_widget_set_sensitive(toolbox, TRUE);
1615 setup_func(toolbox, desktop);
1616 update_func(desktop, desktop->event_context, toolbox);
1617 *conn = desktop->connectEventContextChanged(sigc::bind (sigc::ptr_fun(update_func), toolbox));
1618 } else {
1619 gtk_widget_set_sensitive(toolbox, FALSE);
1620 }
1622 } // end of sp_toolbox_set_desktop()
1625 static void setupToolboxCommon( GtkWidget *toolbox,
1626 SPDesktop *desktop,
1627 gchar const *descr,
1628 gchar const* toolbarName,
1629 gchar const* sizePref )
1630 {
1631 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1632 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1634 GtkUIManager* mgr = gtk_ui_manager_new();
1635 GError* errVal = 0;
1637 GtkOrientation orientation = GTK_ORIENTATION_HORIZONTAL;
1639 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1640 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1642 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, toolbarName );
1643 if ( prefs->getBool("/toolbox/icononly", true) ) {
1644 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1645 }
1647 Inkscape::IconSize toolboxSize = ToolboxFactory::prefToSize(sizePref);
1648 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1650 if (GTK_IS_HANDLE_BOX(toolbox)) {
1651 // g_message("GRABBING ORIENTATION [%s]", toolbarName);
1652 GtkPositionType pos = gtk_handle_box_get_handle_position(GTK_HANDLE_BOX(toolbox));
1653 orientation = ((pos == GTK_POS_LEFT) || (pos == GTK_POS_RIGHT)) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
1654 }
1655 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), orientation);
1656 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1658 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1660 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1661 if ( child ) {
1662 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1663 }
1665 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1666 }
1668 void ToolboxFactory::setOrientation(GtkWidget* toolbox, GtkOrientation orientation)
1669 {
1670 //g_message("Set orientation for %p to be %d", toolbox, orientation);
1671 //GType type = GTK_WIDGET_TYPE(toolbox);
1672 //g_message(" [%s]", g_type_name(type));
1673 //g_message(" %p", g_object_get_data(G_OBJECT(toolbox), BAR_ID_KEY));
1675 GtkPositionType pos = (orientation == GTK_ORIENTATION_HORIZONTAL) ? GTK_POS_LEFT : GTK_POS_TOP;
1676 GtkHandleBox* handleBox = 0;
1678 if (GTK_IS_BIN(toolbox)) {
1679 //g_message(" is a BIN");
1680 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1681 if (child) {
1682 //GType type2 = GTK_WIDGET_TYPE(child);
1683 //g_message(" child [%s]", g_type_name(type2));
1685 if (GTK_IS_BOX(child)) {
1686 //g_message(" is a BOX");
1688 GList* children = gtk_container_get_children(GTK_CONTAINER(child));
1689 if (children && children->data) {
1690 //GtkWidget* child2 = GTK_WIDGET(children->data);
1691 //GType type3 = GTK_WIDGET_TYPE(child2);
1692 //g_message(" child [%s]", g_type_name(type3));
1693 g_message("need to add dynamic switch");
1694 for (GList* curr = children; curr; curr = g_list_next(curr)) {
1695 GtkWidget* child2 = GTK_WIDGET(curr->data);
1697 if (GTK_IS_TOOLBAR(child2)) {
1698 GtkToolbar* childBar = GTK_TOOLBAR(child2);
1699 gtk_toolbar_set_orientation(childBar, orientation);
1700 if (GTK_IS_HANDLE_BOX(toolbox)) {
1701 handleBox = GTK_HANDLE_BOX(toolbox);
1702 }
1703 }
1704 }
1705 g_list_free(children);
1706 } else {
1707 if (GTK_IS_HANDLE_BOX(toolbox)) {
1708 handleBox = GTK_HANDLE_BOX(toolbox);
1709 }
1710 // The call is being made before the toolbox proper has been setup.
1711 }
1712 } else if (GTK_IS_TOOLBAR(child)) {
1713 GtkToolbar* toolbar = GTK_TOOLBAR(child);
1714 gtk_toolbar_set_orientation( toolbar, orientation );
1715 if (GTK_IS_HANDLE_BOX(toolbox)) {
1716 handleBox = GTK_HANDLE_BOX(toolbox);
1717 }
1718 }
1719 }
1720 }
1722 if (handleBox) {
1723 gtk_handle_box_set_handle_position(handleBox, pos);
1724 }
1725 }
1727 static void
1728 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1729 {
1730 gchar const * descr =
1731 "<ui>"
1732 " <toolbar name='ToolToolbar'>"
1733 " <toolitem action='ToolSelector' />"
1734 " <toolitem action='ToolNode' />"
1735 " <toolitem action='ToolTweak' />"
1736 " <toolitem action='ToolSpray' />"
1737 " <toolitem action='ToolZoom' />"
1738 " <toolitem action='ToolRect' />"
1739 " <toolitem action='Tool3DBox' />"
1740 " <toolitem action='ToolArc' />"
1741 " <toolitem action='ToolStar' />"
1742 " <toolitem action='ToolSpiral' />"
1743 " <toolitem action='ToolPencil' />"
1744 " <toolitem action='ToolPen' />"
1745 " <toolitem action='ToolCalligraphic' />"
1746 " <toolitem action='ToolEraser' />"
1747 // " <toolitem action='ToolLPETool' />"
1748 " <toolitem action='ToolPaintBucket' />"
1749 " <toolitem action='ToolText' />"
1750 " <toolitem action='ToolConnector' />"
1751 " <toolitem action='ToolGradient' />"
1752 " <toolitem action='ToolDropper' />"
1753 " </toolbar>"
1754 "</ui>";
1756 setupToolboxCommon( toolbox, desktop, descr,
1757 "/ui/ToolToolbar",
1758 "/toolbox/tools/small");
1759 }
1761 static void
1762 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1763 {
1764 gchar const *const tname = ( eventcontext
1765 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1766 : NULL );
1767 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1769 for (int i = 0 ; tools[i].type_name ; i++ ) {
1770 Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1771 if ( act ) {
1772 bool setActive = tname && !strcmp(tname, tools[i].type_name);
1773 Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1774 if ( verbAct ) {
1775 verbAct->set_active(setActive);
1776 }
1777 }
1778 }
1779 }
1781 static void
1782 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1783 {
1784 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1785 GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1786 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1787 GtkUIManager* mgr = gtk_ui_manager_new();
1788 GError* errVal = 0;
1789 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1790 gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1792 std::map<std::string, GtkWidget*> dataHolders;
1794 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1795 if ( aux_toolboxes[i].prep_func ) {
1796 // converted to GtkActions and UIManager
1798 GtkWidget* kludge = gtk_toolbar_new();
1799 g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1800 g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1801 dataHolders[aux_toolboxes[i].type_name] = kludge;
1802 aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1803 } else {
1805 GtkWidget *sub_toolbox = 0;
1806 if (aux_toolboxes[i].create_func == NULL) {
1807 sub_toolbox = sp_empty_toolbox_new(desktop);
1808 } else {
1809 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1810 }
1812 gtk_size_group_add_widget( grouper, sub_toolbox );
1814 gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1815 g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1817 }
1818 }
1820 // Second pass to create toolbars *after* all GtkActions are created
1821 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1822 if ( aux_toolboxes[i].prep_func ) {
1823 // converted to GtkActions and UIManager
1825 GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1827 GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1828 gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1830 gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1831 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1832 g_free( tmp );
1833 tmp = 0;
1835 if ( prefs->getBool( "/toolbox/icononly", true) ) {
1836 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1837 }
1839 Inkscape::IconSize toolboxSize = ToolboxFactory::prefToSize("/toolbox/small");
1840 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1842 gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1844 if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1845 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1846 swatch->setDesktop( desktop );
1847 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1848 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1849 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1850 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 );
1851 }
1853 gtk_widget_show_all( holder );
1854 sp_set_font_size_smaller( holder );
1856 gtk_size_group_add_widget( grouper, holder );
1858 gtk_container_add( GTK_CONTAINER(toolbox), holder );
1859 g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1860 }
1861 }
1863 g_object_unref( G_OBJECT(grouper) );
1864 }
1866 static void
1867 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1868 {
1869 gchar const *tname = ( eventcontext
1870 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1871 : NULL );
1872 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1873 GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1874 if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1875 gtk_widget_show_all(sub_toolbox);
1876 g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1877 } else {
1878 gtk_widget_hide(sub_toolbox);
1879 }
1880 }
1881 }
1883 static void
1884 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1885 {
1886 gchar const * descr =
1887 "<ui>"
1888 " <toolbar name='CommandsToolbar'>"
1889 " <toolitem action='FileNew' />"
1890 " <toolitem action='FileOpen' />"
1891 " <toolitem action='FileSave' />"
1892 " <toolitem action='FilePrint' />"
1893 " <separator />"
1894 " <toolitem action='FileImport' />"
1895 " <toolitem action='FileExport' />"
1896 " <separator />"
1897 " <toolitem action='EditUndo' />"
1898 " <toolitem action='EditRedo' />"
1899 " <separator />"
1900 " <toolitem action='EditCopy' />"
1901 " <toolitem action='EditCut' />"
1902 " <toolitem action='EditPaste' />"
1903 " <separator />"
1904 " <toolitem action='ZoomSelection' />"
1905 " <toolitem action='ZoomDrawing' />"
1906 " <toolitem action='ZoomPage' />"
1907 " <separator />"
1908 " <toolitem action='EditDuplicate' />"
1909 " <toolitem action='EditClone' />"
1910 " <toolitem action='EditUnlinkClone' />"
1911 " <separator />"
1912 " <toolitem action='SelectionGroup' />"
1913 " <toolitem action='SelectionUnGroup' />"
1914 " <separator />"
1915 " <toolitem action='DialogFillStroke' />"
1916 " <toolitem action='DialogText' />"
1917 " <toolitem action='DialogLayers' />"
1918 " <toolitem action='DialogXMLEditor' />"
1919 " <toolitem action='DialogAlignDistribute' />"
1920 " <separator />"
1921 " <toolitem action='DialogPreferences' />"
1922 " <toolitem action='DialogDocumentProperties' />"
1923 " <separator />"
1924 " <toolitem action='TaskSetAction' />"
1925 " </toolbar>"
1926 "</ui>";
1928 setupToolboxCommon( toolbox, desktop, descr,
1929 "/ui/CommandsToolbar",
1930 "/toolbox/small" );
1931 }
1933 static void
1934 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1935 {
1936 }
1938 void toggle_snap_callback (GtkToggleAction *act, gpointer data) { //data points to the toolbox
1940 if (g_object_get_data(G_OBJECT(data), "freeze" )) {
1941 return;
1942 }
1944 gpointer ptr = g_object_get_data(G_OBJECT(data), "desktop");
1945 g_assert(ptr != NULL);
1947 SPDesktop *dt = reinterpret_cast<SPDesktop*>(ptr);
1948 SPNamedView *nv = sp_desktop_namedview(dt);
1949 SPDocument *doc = SP_OBJECT_DOCUMENT(nv);
1951 if (dt == NULL || nv == NULL) {
1952 g_warning("No desktop or namedview specified (in toggle_snap_callback)!");
1953 return;
1954 }
1956 Inkscape::XML::Node *repr = SP_OBJECT_REPR(nv);
1958 if (repr == NULL) {
1959 g_warning("This namedview doesn't have a xml representation attached!");
1960 return;
1961 }
1963 bool saved = sp_document_get_undo_sensitive(doc);
1964 sp_document_set_undo_sensitive(doc, false);
1966 bool v = false;
1967 SPAttributeEnum attr = (SPAttributeEnum) GPOINTER_TO_INT(g_object_get_data(G_OBJECT(act), "SP_ATTR_INKSCAPE"));
1969 switch (attr) {
1970 case SP_ATTR_INKSCAPE_SNAP_GLOBAL:
1971 dt->toggleSnapGlobal();
1972 break;
1973 case SP_ATTR_INKSCAPE_SNAP_BBOX:
1974 v = nv->snap_manager.snapprefs.getSnapModeBBox();
1975 sp_repr_set_boolean(repr, "inkscape:snap-bbox", !v);
1976 break;
1977 case SP_ATTR_INKSCAPE_BBOX_PATHS:
1978 v = nv->snap_manager.snapprefs.getSnapToBBoxPath();
1979 sp_repr_set_boolean(repr, "inkscape:bbox-paths", !v);
1980 break;
1981 case SP_ATTR_INKSCAPE_BBOX_NODES:
1982 v = nv->snap_manager.snapprefs.getSnapToBBoxNode();
1983 sp_repr_set_boolean(repr, "inkscape:bbox-nodes", !v);
1984 break;
1985 case SP_ATTR_INKSCAPE_SNAP_NODES:
1986 v = nv->snap_manager.snapprefs.getSnapModeNode();
1987 sp_repr_set_boolean(repr, "inkscape:snap-nodes", !v);
1988 break;
1989 case SP_ATTR_INKSCAPE_OBJECT_PATHS:
1990 v = nv->snap_manager.snapprefs.getSnapToItemPath();
1991 sp_repr_set_boolean(repr, "inkscape:object-paths", !v);
1992 break;
1993 case SP_ATTR_INKSCAPE_OBJECT_NODES:
1994 v = nv->snap_manager.snapprefs.getSnapToItemNode();
1995 sp_repr_set_boolean(repr, "inkscape:object-nodes", !v);
1996 break;
1997 case SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES:
1998 v = nv->snap_manager.snapprefs.getSnapSmoothNodes();
1999 sp_repr_set_boolean(repr, "inkscape:snap-smooth-nodes", !v);
2000 break;
2001 case SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS:
2002 v = nv->snap_manager.snapprefs.getSnapIntersectionCS();
2003 sp_repr_set_boolean(repr, "inkscape:snap-intersection-paths", !v);
2004 break;
2005 case SP_ATTR_INKSCAPE_SNAP_CENTER:
2006 v = nv->snap_manager.snapprefs.getIncludeItemCenter();
2007 sp_repr_set_boolean(repr, "inkscape:snap-center", !v);
2008 break;
2009 case SP_ATTR_INKSCAPE_SNAP_GRIDS:
2010 v = nv->snap_manager.snapprefs.getSnapToGrids();
2011 sp_repr_set_boolean(repr, "inkscape:snap-grids", !v);
2012 break;
2013 case SP_ATTR_INKSCAPE_SNAP_TO_GUIDES:
2014 v = nv->snap_manager.snapprefs.getSnapToGuides();
2015 sp_repr_set_boolean(repr, "inkscape:snap-to-guides", !v);
2016 break;
2017 case SP_ATTR_INKSCAPE_SNAP_PAGE:
2018 v = nv->snap_manager.snapprefs.getSnapToPageBorder();
2019 sp_repr_set_boolean(repr, "inkscape:snap-page", !v);
2020 break;
2021 /*case SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE:
2022 v = nv->snap_manager.snapprefs.getSnapIntersectionGG();
2023 sp_repr_set_boolean(repr, "inkscape:snap-intersection-grid-guide", !v);
2024 break;*/
2025 case SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS:
2026 v = nv->snap_manager.snapprefs.getSnapLineMidpoints();
2027 sp_repr_set_boolean(repr, "inkscape:snap-midpoints", !v);
2028 break;
2029 case SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS:
2030 v = nv->snap_manager.snapprefs.getSnapObjectMidpoints();
2031 sp_repr_set_boolean(repr, "inkscape:snap-object-midpoints", !v);
2032 break;
2033 case SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS:
2034 v = nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints();
2035 sp_repr_set_boolean(repr, "inkscape:snap-bbox-edge-midpoints", !v);
2036 break;
2037 case SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS:
2038 v = nv->snap_manager.snapprefs.getSnapBBoxMidpoints();
2039 sp_repr_set_boolean(repr, "inkscape:snap-bbox-midpoints", !v);
2040 break;
2041 default:
2042 g_warning("toggle_snap_callback has been called with an ID for which no action has been defined");
2043 break;
2044 }
2046 // The snapping preferences are stored in the document, and therefore toggling makes the document dirty
2047 doc->setModifiedSinceSave();
2049 sp_document_set_undo_sensitive(doc, saved);
2050 }
2052 void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
2053 {
2054 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2056 gchar const * descr =
2057 "<ui>"
2058 " <toolbar name='SnapToolbar'>"
2059 " <toolitem action='ToggleSnapGlobal' />"
2060 " <separator />"
2061 " <toolitem action='ToggleSnapFromBBoxCorner' />"
2062 " <toolitem action='ToggleSnapToBBoxPath' />"
2063 " <toolitem action='ToggleSnapToBBoxNode' />"
2064 " <toolitem action='ToggleSnapToFromBBoxEdgeMidpoints' />"
2065 " <toolitem action='ToggleSnapToFromBBoxCenters' />"
2066 " <separator />"
2067 " <toolitem action='ToggleSnapFromNode' />"
2068 " <toolitem action='ToggleSnapToItemPath' />"
2069 " <toolitem action='ToggleSnapToPathIntersections' />"
2070 " <toolitem action='ToggleSnapToItemNode' />"
2071 " <toolitem action='ToggleSnapToSmoothNodes' />"
2072 " <toolitem action='ToggleSnapToFromLineMidpoints' />"
2073 " <toolitem action='ToggleSnapToFromObjectCenters' />"
2074 " <toolitem action='ToggleSnapToFromRotationCenter' />"
2075 " <separator />"
2076 " <toolitem action='ToggleSnapToPageBorder' />"
2077 " <toolitem action='ToggleSnapToGrids' />"
2078 " <toolitem action='ToggleSnapToGuides' />"
2079 //" <toolitem action='ToggleSnapToGridGuideIntersections' />"
2080 " </toolbar>"
2081 "</ui>";
2083 Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
2085 {
2086 InkToggleAction* act = ink_toggle_action_new("ToggleSnapGlobal",
2087 _("Snap"), _("Enable snapping"), INKSCAPE_ICON_SNAP, secondarySize,
2088 SP_ATTR_INKSCAPE_SNAP_GLOBAL);
2090 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2091 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2092 }
2094 {
2095 InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromBBoxCorner",
2096 _("Bounding box"), _("Snap bounding box corners"), INKSCAPE_ICON_SNAP_BOUNDING_BOX,
2097 secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX);
2099 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2100 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2101 }
2103 {
2104 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxPath",
2105 _("Bounding box edges"), _("Snap to edges of a bounding box"),
2106 INKSCAPE_ICON_SNAP_BOUNDING_BOX_EDGES, secondarySize, SP_ATTR_INKSCAPE_BBOX_PATHS);
2108 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2109 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2110 }
2112 {
2113 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxNode",
2114 _("Bounding box corners"), _("Snap to bounding box corners"),
2115 INKSCAPE_ICON_SNAP_BOUNDING_BOX_CORNERS, secondarySize, SP_ATTR_INKSCAPE_BBOX_NODES);
2117 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2118 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2119 }
2121 {
2122 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxEdgeMidpoints",
2123 _("BBox Edge Midpoints"), _("Snap from and to midpoints of bounding box edges"),
2124 INKSCAPE_ICON_SNAP_BOUNDING_BOX_MIDPOINTS, secondarySize,
2125 SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS);
2127 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2128 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2129 }
2131 {
2132 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxCenters",
2133 _("BBox Centers"), _("Snapping from and to centers of bounding boxes"),
2134 INKSCAPE_ICON_SNAP_BOUNDING_BOX_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS);
2136 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2137 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2138 }
2140 {
2141 InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromNode",
2142 _("Nodes"), _("Snap nodes or handles"), INKSCAPE_ICON_SNAP_NODES, secondarySize, SP_ATTR_INKSCAPE_SNAP_NODES);
2144 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2145 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2146 }
2148 {
2149 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemPath",
2150 _("Paths"), _("Snap to paths"), INKSCAPE_ICON_SNAP_NODES_PATH, secondarySize,
2151 SP_ATTR_INKSCAPE_OBJECT_PATHS);
2153 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2154 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2155 }
2157 {
2158 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPathIntersections",
2159 _("Path intersections"), _("Snap to path intersections"),
2160 INKSCAPE_ICON_SNAP_NODES_INTERSECTION, secondarySize, SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS);
2162 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2163 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2164 }
2166 {
2167 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemNode",
2168 _("To nodes"), _("Snap to cusp nodes"), INKSCAPE_ICON_SNAP_NODES_CUSP, secondarySize,
2169 SP_ATTR_INKSCAPE_OBJECT_NODES);
2171 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2172 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2173 }
2175 {
2176 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToSmoothNodes",
2177 _("Smooth nodes"), _("Snap to smooth nodes"), INKSCAPE_ICON_SNAP_NODES_SMOOTH,
2178 secondarySize, SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES);
2180 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2181 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2182 }
2184 {
2185 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromLineMidpoints",
2186 _("Line Midpoints"), _("Snap from and to midpoints of line segments"),
2187 INKSCAPE_ICON_SNAP_NODES_MIDPOINT, secondarySize, SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS);
2189 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2190 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2191 }
2193 {
2194 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromObjectCenters",
2195 _("Object Centers"), _("Snap from and to centers of objects"),
2196 INKSCAPE_ICON_SNAP_NODES_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS);
2198 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2199 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2200 }
2202 {
2203 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromRotationCenter",
2204 _("Rotation Centers"), _("Snap from and to an item's rotation center"),
2205 INKSCAPE_ICON_SNAP_NODES_ROTATION_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_CENTER);
2207 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2208 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2209 }
2211 {
2212 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPageBorder",
2213 _("Page border"), _("Snap to the page border"), INKSCAPE_ICON_SNAP_PAGE,
2214 secondarySize, SP_ATTR_INKSCAPE_SNAP_PAGE);
2216 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2217 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2218 }
2220 {
2221 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGrids",
2222 _("Grids"), _("Snap to grids"), INKSCAPE_ICON_GRID_RECTANGULAR, secondarySize,
2223 SP_ATTR_INKSCAPE_SNAP_GRIDS);
2225 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2226 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2227 }
2229 {
2230 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGuides",
2231 _("Guides"), _("Snap to guides"), INKSCAPE_ICON_GUIDES, secondarySize,
2232 SP_ATTR_INKSCAPE_SNAP_TO_GUIDES);
2234 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2235 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2236 }
2238 /*{
2239 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGridGuideIntersections",
2240 _("Grid/guide intersections"), _("Snap to intersections of a grid with a guide"),
2241 INKSCAPE_ICON_SNAP_GRID_GUIDE_INTERSECTIONS, secondarySize,
2242 SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE);
2244 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2245 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2246 }*/
2248 setupToolboxCommon( toolbox, desktop, descr,
2249 "/ui/SnapToolbar",
2250 "/toolbox/secondary" );
2251 }
2253 void ToolboxFactory::updateSnapToolbox(SPDesktop *desktop, SPEventContext */*eventcontext*/, GtkWidget *toolbox)
2254 {
2255 g_assert(desktop != NULL);
2256 g_assert(toolbox != NULL);
2258 SPNamedView *nv = sp_desktop_namedview(desktop);
2259 if (nv == NULL) {
2260 g_warning("Namedview cannot be retrieved (in updateSnapToolbox)!");
2261 return;
2262 }
2264 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2266 Glib::RefPtr<Gtk::Action> act1 = mainActions->get_action("ToggleSnapGlobal");
2267 Glib::RefPtr<Gtk::Action> act2 = mainActions->get_action("ToggleSnapFromBBoxCorner");
2268 Glib::RefPtr<Gtk::Action> act3 = mainActions->get_action("ToggleSnapToBBoxPath");
2269 Glib::RefPtr<Gtk::Action> act4 = mainActions->get_action("ToggleSnapToBBoxNode");
2270 Glib::RefPtr<Gtk::Action> act4b = mainActions->get_action("ToggleSnapToFromBBoxEdgeMidpoints");
2271 Glib::RefPtr<Gtk::Action> act4c = mainActions->get_action("ToggleSnapToFromBBoxCenters");
2272 Glib::RefPtr<Gtk::Action> act5 = mainActions->get_action("ToggleSnapFromNode");
2273 Glib::RefPtr<Gtk::Action> act6 = mainActions->get_action("ToggleSnapToItemPath");
2274 Glib::RefPtr<Gtk::Action> act6b = mainActions->get_action("ToggleSnapToPathIntersections");
2275 Glib::RefPtr<Gtk::Action> act7 = mainActions->get_action("ToggleSnapToItemNode");
2276 Glib::RefPtr<Gtk::Action> act8 = mainActions->get_action("ToggleSnapToSmoothNodes");
2277 Glib::RefPtr<Gtk::Action> act9 = mainActions->get_action("ToggleSnapToFromLineMidpoints");
2278 Glib::RefPtr<Gtk::Action> act10 = mainActions->get_action("ToggleSnapToFromObjectCenters");
2279 Glib::RefPtr<Gtk::Action> act11 = mainActions->get_action("ToggleSnapToFromRotationCenter");
2280 Glib::RefPtr<Gtk::Action> act12 = mainActions->get_action("ToggleSnapToPageBorder");
2281 //Glib::RefPtr<Gtk::Action> act13 = mainActions->get_action("ToggleSnapToGridGuideIntersections");
2282 Glib::RefPtr<Gtk::Action> act14 = mainActions->get_action("ToggleSnapToGrids");
2283 Glib::RefPtr<Gtk::Action> act15 = mainActions->get_action("ToggleSnapToGuides");
2286 if (!act1) {
2287 return; // The snap actions haven't been defined yet (might be the case during startup)
2288 }
2290 // The ..._set_active calls below will toggle the buttons, but this shouldn't lead to
2291 // changes in our document because we're only updating the UI;
2292 // Setting the "freeze" parameter to true will block the code in toggle_snap_callback()
2293 g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(TRUE));
2295 bool const c1 = nv->snap_manager.snapprefs.getSnapEnabledGlobally();
2296 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act1->gobj()), c1);
2298 bool const c2 = nv->snap_manager.snapprefs.getSnapModeBBox();
2299 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act2->gobj()), c2);
2300 gtk_action_set_sensitive(GTK_ACTION(act2->gobj()), c1);
2302 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act3->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxPath());
2303 gtk_action_set_sensitive(GTK_ACTION(act3->gobj()), c1 && c2);
2304 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxNode());
2305 gtk_action_set_sensitive(GTK_ACTION(act4->gobj()), c1 && c2);
2306 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4b->gobj()), nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints());
2307 gtk_action_set_sensitive(GTK_ACTION(act4b->gobj()), c1 && c2);
2308 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4c->gobj()), nv->snap_manager.snapprefs.getSnapBBoxMidpoints());
2309 gtk_action_set_sensitive(GTK_ACTION(act4c->gobj()), c1 && c2);
2311 bool const c3 = nv->snap_manager.snapprefs.getSnapModeNode();
2312 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act5->gobj()), c3);
2313 gtk_action_set_sensitive(GTK_ACTION(act5->gobj()), c1);
2315 bool const c4 = nv->snap_manager.snapprefs.getSnapToItemPath();
2316 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6->gobj()), c4);
2317 gtk_action_set_sensitive(GTK_ACTION(act6->gobj()), c1 && c3);
2318 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6b->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionCS());
2319 gtk_action_set_sensitive(GTK_ACTION(act6b->gobj()), c1 && c3 && c4);
2320 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act7->gobj()), nv->snap_manager.snapprefs.getSnapToItemNode());
2321 gtk_action_set_sensitive(GTK_ACTION(act7->gobj()), c1 && c3);
2322 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act8->gobj()), nv->snap_manager.snapprefs.getSnapSmoothNodes());
2323 gtk_action_set_sensitive(GTK_ACTION(act8->gobj()), c1 && c3);
2324 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act9->gobj()), nv->snap_manager.snapprefs.getSnapLineMidpoints());
2325 gtk_action_set_sensitive(GTK_ACTION(act9->gobj()), c1 && c3);
2326 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act10->gobj()), nv->snap_manager.snapprefs.getSnapObjectMidpoints());
2327 gtk_action_set_sensitive(GTK_ACTION(act10->gobj()), c1 && c3);
2328 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act11->gobj()), nv->snap_manager.snapprefs.getIncludeItemCenter());
2329 gtk_action_set_sensitive(GTK_ACTION(act11->gobj()), c1 && c3);
2331 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act12->gobj()), nv->snap_manager.snapprefs.getSnapToPageBorder());
2332 gtk_action_set_sensitive(GTK_ACTION(act12->gobj()), c1);
2333 //gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act13->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionGG());
2334 //gtk_action_set_sensitive(GTK_ACTION(act13->gobj()), c1);
2336 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act14->gobj()), nv->snap_manager.snapprefs.getSnapToGrids());
2337 gtk_action_set_sensitive(GTK_ACTION(act14->gobj()), c1);
2338 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act15->gobj()), nv->snap_manager.snapprefs.getSnapToGuides());
2339 gtk_action_set_sensitive(GTK_ACTION(act15->gobj()), c1);
2342 g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(FALSE)); // unfreeze (see above)
2343 }
2345 void ToolboxFactory::showAuxToolbox(GtkWidget *toolbox_toplevel)
2346 {
2347 gtk_widget_show(toolbox_toplevel);
2348 GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
2350 GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
2351 if (!shown_toolbox) {
2352 return;
2353 }
2354 gtk_widget_show(toolbox);
2356 gtk_widget_show_all(shown_toolbox);
2357 }
2359 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop)
2360 {
2361 GtkWidget *tbl = gtk_toolbar_new();
2362 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
2363 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
2365 gtk_widget_show_all(tbl);
2366 sp_set_font_size_smaller (tbl);
2368 return tbl;
2369 }
2371 #define MODE_LABEL_WIDTH 70
2373 //########################
2374 //## Star ##
2375 //########################
2377 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2378 {
2379 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2381 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2382 // do not remember prefs if this call is initiated by an undo change, because undoing object
2383 // creation sets bogus values to its attributes before it is deleted
2384 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2385 prefs->setInt("/tools/shapes/star/magnitude", (gint)adj->value);
2386 }
2388 // quit if run by the attr_changed listener
2389 if (g_object_get_data( dataKludge, "freeze" )) {
2390 return;
2391 }
2393 // in turn, prevent listener from responding
2394 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2396 bool modmade = false;
2398 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2399 GSList const *items = selection->itemList();
2400 for (; items != NULL; items = items->next) {
2401 if (SP_IS_STAR((SPItem *) items->data)) {
2402 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2403 sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
2404 sp_repr_set_svg_double(repr, "sodipodi:arg2",
2405 (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
2406 + M_PI / (gint)adj->value));
2407 SP_OBJECT((SPItem *) items->data)->updateRepr();
2408 modmade = true;
2409 }
2410 }
2411 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2412 _("Star: Change number of corners"));
2414 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2415 }
2417 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2418 {
2419 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2421 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2422 if (!IS_NAN(adj->value)) {
2423 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2424 prefs->setDouble("/tools/shapes/star/proportion", adj->value);
2425 }
2426 }
2428 // quit if run by the attr_changed listener
2429 if (g_object_get_data( dataKludge, "freeze" )) {
2430 return;
2431 }
2433 // in turn, prevent listener from responding
2434 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2436 bool modmade = false;
2437 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2438 GSList const *items = selection->itemList();
2439 for (; items != NULL; items = items->next) {
2440 if (SP_IS_STAR((SPItem *) items->data)) {
2441 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2443 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2444 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2445 if (r2 < r1) {
2446 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
2447 } else {
2448 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
2449 }
2451 SP_OBJECT((SPItem *) items->data)->updateRepr();
2452 modmade = true;
2453 }
2454 }
2456 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2457 _("Star: Change spoke ratio"));
2459 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2460 }
2462 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
2463 {
2464 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2465 bool flat = ege_select_one_action_get_active( act ) == 0;
2467 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2468 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2469 prefs->setBool( "/tools/shapes/star/isflatsided", flat);
2470 }
2472 // quit if run by the attr_changed listener
2473 if (g_object_get_data( dataKludge, "freeze" )) {
2474 return;
2475 }
2477 // in turn, prevent listener from responding
2478 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2480 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2481 GSList const *items = selection->itemList();
2482 GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2483 bool modmade = false;
2485 if ( prop_action ) {
2486 gtk_action_set_sensitive( prop_action, !flat );
2487 }
2489 for (; items != NULL; items = items->next) {
2490 if (SP_IS_STAR((SPItem *) items->data)) {
2491 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2492 repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
2493 SP_OBJECT((SPItem *) items->data)->updateRepr();
2494 modmade = true;
2495 }
2496 }
2498 if (modmade) {
2499 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2500 flat ? _("Make polygon") : _("Make star"));
2501 }
2503 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2504 }
2506 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2507 {
2508 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2510 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2511 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2512 prefs->setDouble("/tools/shapes/star/rounded", (gdouble) adj->value);
2513 }
2515 // quit if run by the attr_changed listener
2516 if (g_object_get_data( dataKludge, "freeze" )) {
2517 return;
2518 }
2520 // in turn, prevent listener from responding
2521 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2523 bool modmade = false;
2525 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2526 GSList const *items = selection->itemList();
2527 for (; items != NULL; items = items->next) {
2528 if (SP_IS_STAR((SPItem *) items->data)) {
2529 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2530 sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2531 SP_OBJECT(items->data)->updateRepr();
2532 modmade = true;
2533 }
2534 }
2535 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2536 _("Star: Change rounding"));
2538 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2539 }
2541 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2542 {
2543 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2545 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2546 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2547 prefs->setDouble("/tools/shapes/star/randomized", (gdouble) adj->value);
2548 }
2550 // quit if run by the attr_changed listener
2551 if (g_object_get_data( dataKludge, "freeze" )) {
2552 return;
2553 }
2555 // in turn, prevent listener from responding
2556 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2558 bool modmade = false;
2560 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2561 GSList const *items = selection->itemList();
2562 for (; items != NULL; items = items->next) {
2563 if (SP_IS_STAR((SPItem *) items->data)) {
2564 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2565 sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2566 SP_OBJECT(items->data)->updateRepr();
2567 modmade = true;
2568 }
2569 }
2570 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2571 _("Star: Change randomization"));
2573 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2574 }
2577 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2578 gchar const */*old_value*/, gchar const */*new_value*/,
2579 bool /*is_interactive*/, gpointer data)
2580 {
2581 GtkWidget *tbl = GTK_WIDGET(data);
2583 // quit if run by the _changed callbacks
2584 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2585 return;
2586 }
2588 // in turn, prevent callbacks from responding
2589 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2591 GtkAdjustment *adj = 0;
2593 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2594 bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2596 if (!strcmp(name, "inkscape:randomized")) {
2597 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2598 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2599 } else if (!strcmp(name, "inkscape:rounded")) {
2600 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2601 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2602 } else if (!strcmp(name, "inkscape:flatsided")) {
2603 GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2604 char const *flatsides = repr->attribute("inkscape:flatsided");
2605 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2606 if ( flatsides && !strcmp(flatsides,"false") ) {
2607 ege_select_one_action_set_active( flat_action, 1 );
2608 gtk_action_set_sensitive( prop_action, TRUE );
2609 } else {
2610 ege_select_one_action_set_active( flat_action, 0 );
2611 gtk_action_set_sensitive( prop_action, FALSE );
2612 }
2613 } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2614 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2615 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2616 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2617 if (r2 < r1) {
2618 gtk_adjustment_set_value(adj, r2/r1);
2619 } else {
2620 gtk_adjustment_set_value(adj, r1/r2);
2621 }
2622 } else if (!strcmp(name, "sodipodi:sides")) {
2623 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2624 gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2625 }
2627 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2628 }
2631 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2632 {
2633 NULL, /* child_added */
2634 NULL, /* child_removed */
2635 star_tb_event_attr_changed,
2636 NULL, /* content_changed */
2637 NULL /* order_changed */
2638 };
2641 /**
2642 * \param selection Should not be NULL.
2643 */
2644 static void
2645 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2646 {
2647 int n_selected = 0;
2648 Inkscape::XML::Node *repr = NULL;
2650 purge_repr_listener( tbl, tbl );
2652 for (GSList const *items = selection->itemList();
2653 items != NULL;
2654 items = items->next)
2655 {
2656 if (SP_IS_STAR((SPItem *) items->data)) {
2657 n_selected++;
2658 repr = SP_OBJECT_REPR((SPItem *) items->data);
2659 }
2660 }
2662 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2664 if (n_selected == 0) {
2665 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2666 } else if (n_selected == 1) {
2667 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2669 if (repr) {
2670 g_object_set_data( tbl, "repr", repr );
2671 Inkscape::GC::anchor(repr);
2672 sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2673 sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2674 }
2675 } else {
2676 // FIXME: implement averaging of all parameters for multiple selected stars
2677 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2678 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2679 }
2680 }
2683 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2684 {
2685 // FIXME: in this and all other _default functions, set some flag telling the value_changed
2686 // callbacks to lump all the changes for all selected objects in one undo step
2688 GtkAdjustment *adj = 0;
2690 // fixme: make settable in prefs!
2691 gint mag = 5;
2692 gdouble prop = 0.5;
2693 gboolean flat = FALSE;
2694 gdouble randomized = 0;
2695 gdouble rounded = 0;
2697 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2698 ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2700 GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2701 gtk_action_set_sensitive( sb2, !flat );
2703 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2704 gtk_adjustment_set_value(adj, mag);
2705 gtk_adjustment_value_changed(adj);
2707 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2708 gtk_adjustment_set_value(adj, prop);
2709 gtk_adjustment_value_changed(adj);
2711 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2712 gtk_adjustment_set_value(adj, rounded);
2713 gtk_adjustment_value_changed(adj);
2715 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2716 gtk_adjustment_set_value(adj, randomized);
2717 gtk_adjustment_value_changed(adj);
2718 }
2721 void
2722 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2723 {
2724 GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2725 if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2726 GtkWidget *l = gtk_label_new(NULL);
2727 gtk_label_set_markup(GTK_LABEL(l), title);
2728 gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2729 if ( GTK_IS_TOOLBAR(tbl) ) {
2730 gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2731 } else {
2732 gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2733 }
2734 gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2735 }
2738 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2739 {
2740 Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
2742 {
2743 EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2744 ege_output_action_set_use_markup( act, TRUE );
2745 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2746 g_object_set_data( holder, "mode_action", act );
2747 }
2749 {
2750 EgeAdjustmentAction* eact = 0;
2751 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2752 bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2754 /* Flatsided checkbox */
2755 {
2756 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2758 GtkTreeIter iter;
2759 gtk_list_store_append( model, &iter );
2760 gtk_list_store_set( model, &iter,
2761 0, _("Polygon"),
2762 1, _("Regular polygon (with one handle) instead of a star"),
2763 2, INKSCAPE_ICON_DRAW_POLYGON,
2764 -1 );
2766 gtk_list_store_append( model, &iter );
2767 gtk_list_store_set( model, &iter,
2768 0, _("Star"),
2769 1, _("Star instead of a regular polygon (with one handle)"),
2770 2, INKSCAPE_ICON_DRAW_STAR,
2771 -1 );
2773 EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2774 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2775 g_object_set_data( holder, "flat_action", act );
2777 ege_select_one_action_set_appearance( act, "full" );
2778 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2779 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2780 ege_select_one_action_set_icon_column( act, 2 );
2781 ege_select_one_action_set_icon_size( act, secondarySize );
2782 ege_select_one_action_set_tooltip_column( act, 1 );
2784 ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2785 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2786 }
2788 /* Magnitude */
2789 {
2790 gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2791 gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2792 eact = create_adjustment_action( "MagnitudeAction",
2793 _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2794 "/tools/shapes/star/magnitude", 3,
2795 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2796 3, 1024, 1, 5,
2797 labels, values, G_N_ELEMENTS(labels),
2798 sp_stb_magnitude_value_changed,
2799 1.0, 0 );
2800 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2801 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2802 }
2804 /* Spoke ratio */
2805 {
2806 gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2807 gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2808 eact = create_adjustment_action( "SpokeAction",
2809 _("Spoke ratio"), _("Spoke ratio:"),
2810 // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2811 // Base radius is the same for the closest handle.
2812 _("Base radius to tip radius ratio"),
2813 "/tools/shapes/star/proportion", 0.5,
2814 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2815 0.01, 1.0, 0.01, 0.1,
2816 labels, values, G_N_ELEMENTS(labels),
2817 sp_stb_proportion_value_changed );
2818 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2819 g_object_set_data( holder, "prop_action", eact );
2820 }
2822 if ( !isFlatSided ) {
2823 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2824 } else {
2825 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2826 }
2828 /* Roundedness */
2829 {
2830 gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2831 gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2832 eact = create_adjustment_action( "RoundednessAction",
2833 _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2834 "/tools/shapes/star/rounded", 0.0,
2835 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2836 -10.0, 10.0, 0.01, 0.1,
2837 labels, values, G_N_ELEMENTS(labels),
2838 sp_stb_rounded_value_changed );
2839 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2840 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2841 }
2843 /* Randomization */
2844 {
2845 gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2846 gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2847 eact = create_adjustment_action( "RandomizationAction",
2848 _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2849 "/tools/shapes/star/randomized", 0.0,
2850 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2851 -10.0, 10.0, 0.001, 0.01,
2852 labels, values, G_N_ELEMENTS(labels),
2853 sp_stb_randomized_value_changed, 0.1, 3 );
2854 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2855 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2856 }
2857 }
2859 {
2860 /* Reset */
2861 {
2862 GtkAction* act = gtk_action_new( "StarResetAction",
2863 _("Defaults"),
2864 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2865 GTK_STOCK_CLEAR );
2866 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2867 gtk_action_group_add_action( mainActions, act );
2868 gtk_action_set_sensitive( act, TRUE );
2869 }
2870 }
2872 sigc::connection *connection = new sigc::connection(
2873 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2874 );
2875 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2876 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2877 }
2880 //########################
2881 //## Rect ##
2882 //########################
2884 static void sp_rtb_sensitivize( GObject *tbl )
2885 {
2886 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2887 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2888 GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2890 if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2891 gtk_action_set_sensitive( not_rounded, FALSE );
2892 } else {
2893 gtk_action_set_sensitive( not_rounded, TRUE );
2894 }
2895 }
2898 static void
2899 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2900 void (*setter)(SPRect *, gdouble))
2901 {
2902 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2904 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2905 SPUnit const *unit = tracker->getActiveUnit();
2907 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2908 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2909 prefs->setDouble(Glib::ustring("/tools/shapes/rect/") + value_name, sp_units_get_pixels(adj->value, *unit));
2910 }
2912 // quit if run by the attr_changed listener
2913 if (g_object_get_data( tbl, "freeze" )) {
2914 return;
2915 }
2917 // in turn, prevent listener from responding
2918 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2920 bool modmade = false;
2921 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2922 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2923 if (SP_IS_RECT(items->data)) {
2924 if (adj->value != 0) {
2925 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2926 } else {
2927 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2928 }
2929 modmade = true;
2930 }
2931 }
2933 sp_rtb_sensitivize( tbl );
2935 if (modmade) {
2936 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2937 _("Change rectangle"));
2938 }
2940 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2941 }
2943 static void
2944 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2945 {
2946 sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2947 }
2949 static void
2950 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2951 {
2952 sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2953 }
2955 static void
2956 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2957 {
2958 sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2959 }
2961 static void
2962 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2963 {
2964 sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2965 }
2969 static void
2970 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2971 {
2972 GtkAdjustment *adj = 0;
2974 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2975 gtk_adjustment_set_value(adj, 0.0);
2976 // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2977 gtk_adjustment_value_changed(adj);
2979 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2980 gtk_adjustment_set_value(adj, 0.0);
2981 gtk_adjustment_value_changed(adj);
2983 sp_rtb_sensitivize( obj );
2984 }
2986 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2987 gchar const */*old_value*/, gchar const */*new_value*/,
2988 bool /*is_interactive*/, gpointer data)
2989 {
2990 GObject *tbl = G_OBJECT(data);
2992 // quit if run by the _changed callbacks
2993 if (g_object_get_data( tbl, "freeze" )) {
2994 return;
2995 }
2997 // in turn, prevent callbacks from responding
2998 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3000 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
3001 SPUnit const *unit = tracker->getActiveUnit();
3003 gpointer item = g_object_get_data( tbl, "item" );
3004 if (item && SP_IS_RECT(item)) {
3005 {
3006 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
3007 gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
3008 gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
3009 }
3011 {
3012 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
3013 gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
3014 gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
3015 }
3017 {
3018 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
3019 gdouble width = sp_rect_get_visible_width (SP_RECT(item));
3020 gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
3021 }
3023 {
3024 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
3025 gdouble height = sp_rect_get_visible_height (SP_RECT(item));
3026 gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
3027 }
3028 }
3030 sp_rtb_sensitivize( tbl );
3032 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3033 }
3036 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
3037 NULL, /* child_added */
3038 NULL, /* child_removed */
3039 rect_tb_event_attr_changed,
3040 NULL, /* content_changed */
3041 NULL /* order_changed */
3042 };
3044 /**
3045 * \param selection should not be NULL.
3046 */
3047 static void
3048 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3049 {
3050 int n_selected = 0;
3051 Inkscape::XML::Node *repr = NULL;
3052 SPItem *item = NULL;
3054 if ( g_object_get_data( tbl, "repr" ) ) {
3055 g_object_set_data( tbl, "item", NULL );
3056 }
3057 purge_repr_listener( tbl, tbl );
3059 for (GSList const *items = selection->itemList();
3060 items != NULL;
3061 items = items->next) {
3062 if (SP_IS_RECT((SPItem *) items->data)) {
3063 n_selected++;
3064 item = (SPItem *) items->data;
3065 repr = SP_OBJECT_REPR(item);
3066 }
3067 }
3069 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3071 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3073 if (n_selected == 0) {
3074 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3076 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3077 gtk_action_set_sensitive(w, FALSE);
3078 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3079 gtk_action_set_sensitive(h, FALSE);
3081 } else if (n_selected == 1) {
3082 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3083 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3085 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3086 gtk_action_set_sensitive(w, TRUE);
3087 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3088 gtk_action_set_sensitive(h, TRUE);
3090 if (repr) {
3091 g_object_set_data( tbl, "repr", repr );
3092 g_object_set_data( tbl, "item", item );
3093 Inkscape::GC::anchor(repr);
3094 sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
3095 sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
3096 }
3097 } else {
3098 // FIXME: implement averaging of all parameters for multiple selected
3099 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3100 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3101 sp_rtb_sensitivize( tbl );
3102 }
3103 }
3106 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3107 {
3108 EgeAdjustmentAction* eact = 0;
3109 Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
3111 {
3112 EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
3113 ege_output_action_set_use_markup( act, TRUE );
3114 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3115 g_object_set_data( holder, "mode_action", act );
3116 }
3118 // rx/ry units menu: create
3119 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
3120 //tracker->addUnit( SP_UNIT_PERCENT, 0 );
3121 // fixme: add % meaning per cent of the width/height
3122 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
3123 g_object_set_data( holder, "tracker", tracker );
3125 /* W */
3126 {
3127 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3128 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3129 eact = create_adjustment_action( "RectWidthAction",
3130 _("Width"), _("W:"), _("Width of rectangle"),
3131 "/tools/shapes/rect/width", 0,
3132 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
3133 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3134 labels, values, G_N_ELEMENTS(labels),
3135 sp_rtb_width_value_changed );
3136 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3137 g_object_set_data( holder, "width_action", eact );
3138 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3139 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3140 }
3142 /* H */
3143 {
3144 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3145 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3146 eact = create_adjustment_action( "RectHeightAction",
3147 _("Height"), _("H:"), _("Height of rectangle"),
3148 "/tools/shapes/rect/height", 0,
3149 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3150 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3151 labels, values, G_N_ELEMENTS(labels),
3152 sp_rtb_height_value_changed );
3153 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3154 g_object_set_data( holder, "height_action", eact );
3155 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3156 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3157 }
3159 /* rx */
3160 {
3161 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3162 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3163 eact = create_adjustment_action( "RadiusXAction",
3164 _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
3165 "/tools/shapes/rect/rx", 0,
3166 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3167 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3168 labels, values, G_N_ELEMENTS(labels),
3169 sp_rtb_rx_value_changed);
3170 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3171 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3172 }
3174 /* ry */
3175 {
3176 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3177 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3178 eact = create_adjustment_action( "RadiusYAction",
3179 _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
3180 "/tools/shapes/rect/ry", 0,
3181 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3182 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3183 labels, values, G_N_ELEMENTS(labels),
3184 sp_rtb_ry_value_changed);
3185 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3186 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3187 }
3189 // add the units menu
3190 {
3191 GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
3192 gtk_action_group_add_action( mainActions, act );
3193 }
3195 /* Reset */
3196 {
3197 InkAction* inky = ink_action_new( "RectResetAction",
3198 _("Not rounded"),
3199 _("Make corners sharp"),
3200 INKSCAPE_ICON_RECTANGLE_MAKE_CORNERS_SHARP,
3201 secondarySize );
3202 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
3203 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3204 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3205 g_object_set_data( holder, "not_rounded", inky );
3206 }
3208 g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
3209 sp_rtb_sensitivize( holder );
3211 sigc::connection *connection = new sigc::connection(
3212 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
3213 );
3214 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3215 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3216 }
3218 //########################
3219 //## 3D Box ##
3220 //########################
3222 // normalize angle so that it lies in the interval [0,360]
3223 static double box3d_normalize_angle (double a) {
3224 double angle = a + ((int) (a/360.0))*360;
3225 if (angle < 0) {
3226 angle += 360.0;
3227 }
3228 return angle;
3229 }
3231 static void
3232 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
3233 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
3234 // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
3235 // have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
3236 // are reset).
3237 bool is_infinite = !persp3d_VP_is_finite(persp->perspective_impl, axis);
3239 if (is_infinite) {
3240 gtk_toggle_action_set_active(tact, TRUE);
3241 gtk_action_set_sensitive(act, TRUE);
3243 double angle = persp3d_get_infinite_angle(persp, axis);
3244 if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
3245 gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
3246 }
3247 } else {
3248 gtk_toggle_action_set_active(tact, FALSE);
3249 gtk_action_set_sensitive(act, FALSE);
3250 }
3251 }
3253 static void
3254 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
3255 if (!persp_repr) {
3256 g_print ("No perspective given to box3d_resync_toolbar().\n");
3257 return;
3258 }
3260 GtkWidget *tbl = GTK_WIDGET(data);
3261 GtkAdjustment *adj = 0;
3262 GtkAction *act = 0;
3263 GtkToggleAction *tact = 0;
3264 Persp3D *persp = persp3d_get_from_repr(persp_repr);
3265 if (!persp) {
3266 // Hmm, is it an error if this happens?
3267 return;
3268 }
3269 {
3270 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
3271 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
3272 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
3274 box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
3275 }
3276 {
3277 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
3278 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
3279 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
3281 box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
3282 }
3283 {
3284 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
3285 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
3286 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
3288 box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
3289 }
3290 }
3292 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3293 gchar const */*old_value*/, gchar const */*new_value*/,
3294 bool /*is_interactive*/, gpointer data)
3295 {
3296 GtkWidget *tbl = GTK_WIDGET(data);
3298 // quit if run by the attr_changed or selection changed listener
3299 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3300 return;
3301 }
3303 // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
3304 // sp_document_maybe_done() when the document is undo insensitive)
3305 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3307 // TODO: Only update the appropriate part of the toolbar
3308 // if (!strcmp(name, "inkscape:vp_z")) {
3309 box3d_resync_toolbar(repr, G_OBJECT(tbl));
3310 // }
3312 Persp3D *persp = persp3d_get_from_repr(repr);
3313 persp3d_update_box_reprs(persp);
3315 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3316 }
3318 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
3319 {
3320 NULL, /* child_added */
3321 NULL, /* child_removed */
3322 box3d_persp_tb_event_attr_changed,
3323 NULL, /* content_changed */
3324 NULL /* order_changed */
3325 };
3327 /**
3328 * \param selection Should not be NULL.
3329 */
3330 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
3331 // Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
3332 static void
3333 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3334 {
3335 // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
3336 // disable the angle entry fields for this direction (otherwise entering a value in them should only
3337 // update the perspectives with infinite VPs and leave the other ones untouched).
3339 Inkscape::XML::Node *persp_repr = NULL;
3340 purge_repr_listener(tbl, tbl);
3342 SPItem *item = selection->singleItem();
3343 if (item && SP_IS_BOX3D(item)) {
3344 // FIXME: Also deal with multiple selected boxes
3345 SPBox3D *box = SP_BOX3D(item);
3346 Persp3D *persp = box3d_get_perspective(box);
3347 persp_repr = SP_OBJECT_REPR(persp);
3348 if (persp_repr) {
3349 g_object_set_data(tbl, "repr", persp_repr);
3350 Inkscape::GC::anchor(persp_repr);
3351 sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
3352 sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
3353 }
3355 inkscape_active_document()->setCurrentPersp3D(persp3d_get_from_repr(persp_repr));
3356 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3357 prefs->setString("/tools/shapes/3dbox/persp", persp_repr->attribute("id"));
3359 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
3360 box3d_resync_toolbar(persp_repr, tbl);
3361 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
3362 }
3363 }
3365 static void
3366 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
3367 {
3368 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3369 SPDocument *document = sp_desktop_document(desktop);
3371 // quit if run by the attr_changed or selection changed listener
3372 if (g_object_get_data( dataKludge, "freeze" )) {
3373 return;
3374 }
3376 // in turn, prevent listener from responding
3377 g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
3379 std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
3380 if (sel_persps.empty()) {
3381 // this can happen when the document is created; we silently ignore it
3382 return;
3383 }
3384 Persp3D *persp = sel_persps.front();
3386 persp->perspective_impl->tmat.set_infinite_direction (axis, adj->value);
3387 SP_OBJECT(persp)->updateRepr();
3389 // TODO: use the correct axis here, too
3390 sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
3392 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
3393 }
3396 static void
3397 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3398 {
3399 box3d_angle_value_changed(adj, dataKludge, Proj::X);
3400 }
3402 static void
3403 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3404 {
3405 box3d_angle_value_changed(adj, dataKludge, Proj::Y);
3406 }
3408 static void
3409 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3410 {
3411 box3d_angle_value_changed(adj, dataKludge, Proj::Z);
3412 }
3415 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
3416 {
3417 // TODO: Take all selected perspectives into account
3418 std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
3419 if (sel_persps.empty()) {
3420 // this can happen when the document is created; we silently ignore it
3421 return;
3422 }
3423 Persp3D *persp = sel_persps.front();
3425 bool set_infinite = gtk_toggle_action_get_active(act);
3426 persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
3427 }
3429 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3430 {
3431 box3d_vp_state_changed(act, box3d_angle, Proj::X);
3432 }
3434 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3435 {
3436 box3d_vp_state_changed(act, box3d_angle, Proj::Y);
3437 }
3439 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3440 {
3441 box3d_vp_state_changed(act, box3d_angle, Proj::Z);
3442 }
3444 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3445 {
3446 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3447 EgeAdjustmentAction* eact = 0;
3448 SPDocument *document = sp_desktop_document (desktop);
3449 Persp3DImpl *persp_impl = document->getCurrentPersp3DImpl();
3451 EgeAdjustmentAction* box3d_angle_x = 0;
3452 EgeAdjustmentAction* box3d_angle_y = 0;
3453 EgeAdjustmentAction* box3d_angle_z = 0;
3455 /* Angle X */
3456 {
3457 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3458 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3459 eact = create_adjustment_action( "3DBoxAngleXAction",
3460 _("Angle in X direction"), _("Angle X:"),
3461 // Translators: PL is short for 'perspective line'
3462 _("Angle of PLs in X direction"),
3463 "/tools/shapes/3dbox/box3d_angle_x", 30,
3464 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
3465 -360.0, 360.0, 1.0, 10.0,
3466 labels, values, G_N_ELEMENTS(labels),
3467 box3d_angle_x_value_changed );
3468 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3469 g_object_set_data( holder, "box3d_angle_x_action", eact );
3470 box3d_angle_x = eact;
3471 }
3473 if (!persp_impl || !persp3d_VP_is_finite(persp_impl, Proj::X)) {
3474 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3475 } else {
3476 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3477 }
3480 /* VP X state */
3481 {
3482 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
3483 // Translators: VP is short for 'vanishing point'
3484 _("State of VP in X direction"),
3485 _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
3486 INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3487 Inkscape::ICON_SIZE_DECORATION );
3488 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3489 g_object_set_data( holder, "box3d_vp_x_state_action", act );
3490 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
3491 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3492 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3493 }
3495 /* Angle Y */
3496 {
3497 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3498 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3499 eact = create_adjustment_action( "3DBoxAngleYAction",
3500 _("Angle in Y direction"), _("Angle Y:"),
3501 // Translators: PL is short for 'perspective line'
3502 _("Angle of PLs in Y direction"),
3503 "/tools/shapes/3dbox/box3d_angle_y", 30,
3504 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3505 -360.0, 360.0, 1.0, 10.0,
3506 labels, values, G_N_ELEMENTS(labels),
3507 box3d_angle_y_value_changed );
3508 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3509 g_object_set_data( holder, "box3d_angle_y_action", eact );
3510 box3d_angle_y = eact;
3511 }
3513 if (!persp_impl || !persp3d_VP_is_finite(persp_impl, Proj::Y)) {
3514 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3515 } else {
3516 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3517 }
3519 /* VP Y state */
3520 {
3521 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
3522 // Translators: VP is short for 'vanishing point'
3523 _("State of VP in Y direction"),
3524 _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
3525 INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3526 Inkscape::ICON_SIZE_DECORATION );
3527 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3528 g_object_set_data( holder, "box3d_vp_y_state_action", act );
3529 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
3530 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3531 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3532 }
3534 /* Angle Z */
3535 {
3536 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3537 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3538 eact = create_adjustment_action( "3DBoxAngleZAction",
3539 _("Angle in Z direction"), _("Angle Z:"),
3540 // Translators: PL is short for 'perspective line'
3541 _("Angle of PLs in Z direction"),
3542 "/tools/shapes/3dbox/box3d_angle_z", 30,
3543 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3544 -360.0, 360.0, 1.0, 10.0,
3545 labels, values, G_N_ELEMENTS(labels),
3546 box3d_angle_z_value_changed );
3547 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3548 g_object_set_data( holder, "box3d_angle_z_action", eact );
3549 box3d_angle_z = eact;
3550 }
3552 if (!persp_impl || !persp3d_VP_is_finite(persp_impl, Proj::Z)) {
3553 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3554 } else {
3555 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3556 }
3558 /* VP Z state */
3559 {
3560 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3561 // Translators: VP is short for 'vanishing point'
3562 _("State of VP in Z direction"),
3563 _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3564 INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3565 Inkscape::ICON_SIZE_DECORATION );
3566 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3567 g_object_set_data( holder, "box3d_vp_z_state_action", act );
3568 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3569 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3570 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3571 }
3573 sigc::connection *connection = new sigc::connection(
3574 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3575 );
3576 g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3577 g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3578 }
3580 //########################
3581 //## Spiral ##
3582 //########################
3584 static void
3585 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, Glib::ustring const &value_name)
3586 {
3587 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3589 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3590 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3591 prefs->setDouble("/tools/shapes/spiral/" + value_name, adj->value);
3592 }
3594 // quit if run by the attr_changed listener
3595 if (g_object_get_data( tbl, "freeze" )) {
3596 return;
3597 }
3599 // in turn, prevent listener from responding
3600 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3602 gchar* namespaced_name = g_strconcat("sodipodi:", value_name.data(), NULL);
3604 bool modmade = false;
3605 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3606 items != NULL;
3607 items = items->next)
3608 {
3609 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3610 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3611 sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3612 SP_OBJECT((SPItem *) items->data)->updateRepr();
3613 modmade = true;
3614 }
3615 }
3617 g_free(namespaced_name);
3619 if (modmade) {
3620 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3621 _("Change spiral"));
3622 }
3624 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3625 }
3627 static void
3628 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3629 {
3630 sp_spl_tb_value_changed(adj, tbl, "revolution");
3631 }
3633 static void
3634 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3635 {
3636 sp_spl_tb_value_changed(adj, tbl, "expansion");
3637 }
3639 static void
3640 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3641 {
3642 sp_spl_tb_value_changed(adj, tbl, "t0");
3643 }
3645 static void
3646 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3647 {
3648 GtkWidget *tbl = GTK_WIDGET(obj);
3650 GtkAdjustment *adj;
3652 // fixme: make settable
3653 gdouble rev = 5;
3654 gdouble exp = 1.0;
3655 gdouble t0 = 0.0;
3657 adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3658 gtk_adjustment_set_value(adj, rev);
3659 gtk_adjustment_value_changed(adj);
3661 adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3662 gtk_adjustment_set_value(adj, exp);
3663 gtk_adjustment_value_changed(adj);
3665 adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3666 gtk_adjustment_set_value(adj, t0);
3667 gtk_adjustment_value_changed(adj);
3669 spinbutton_defocus(GTK_OBJECT(tbl));
3670 }
3673 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3674 gchar const */*old_value*/, gchar const */*new_value*/,
3675 bool /*is_interactive*/, gpointer data)
3676 {
3677 GtkWidget *tbl = GTK_WIDGET(data);
3679 // quit if run by the _changed callbacks
3680 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3681 return;
3682 }
3684 // in turn, prevent callbacks from responding
3685 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3687 GtkAdjustment *adj;
3688 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3689 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3691 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3692 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3694 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3695 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3697 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3698 }
3701 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3702 NULL, /* child_added */
3703 NULL, /* child_removed */
3704 spiral_tb_event_attr_changed,
3705 NULL, /* content_changed */
3706 NULL /* order_changed */
3707 };
3709 static void
3710 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3711 {
3712 int n_selected = 0;
3713 Inkscape::XML::Node *repr = NULL;
3715 purge_repr_listener( tbl, tbl );
3717 for (GSList const *items = selection->itemList();
3718 items != NULL;
3719 items = items->next)
3720 {
3721 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3722 n_selected++;
3723 repr = SP_OBJECT_REPR((SPItem *) items->data);
3724 }
3725 }
3727 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3729 if (n_selected == 0) {
3730 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3731 } else if (n_selected == 1) {
3732 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3734 if (repr) {
3735 g_object_set_data( tbl, "repr", repr );
3736 Inkscape::GC::anchor(repr);
3737 sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3738 sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3739 }
3740 } else {
3741 // FIXME: implement averaging of all parameters for multiple selected
3742 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3743 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3744 }
3745 }
3748 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3749 {
3750 EgeAdjustmentAction* eact = 0;
3751 Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
3753 {
3754 EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3755 ege_output_action_set_use_markup( act, TRUE );
3756 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3757 g_object_set_data( holder, "mode_action", act );
3758 }
3760 /* Revolution */
3761 {
3762 gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3763 gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3764 eact = create_adjustment_action( "SpiralRevolutionAction",
3765 _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3766 "/tools/shapes/spiral/revolution", 3.0,
3767 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3768 0.01, 1024.0, 0.1, 1.0,
3769 labels, values, G_N_ELEMENTS(labels),
3770 sp_spl_tb_revolution_value_changed, 1, 2);
3771 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3772 }
3774 /* Expansion */
3775 {
3776 gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3777 gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3778 eact = create_adjustment_action( "SpiralExpansionAction",
3779 _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3780 "/tools/shapes/spiral/expansion", 1.0,
3781 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3782 0.0, 1000.0, 0.01, 1.0,
3783 labels, values, G_N_ELEMENTS(labels),
3784 sp_spl_tb_expansion_value_changed);
3785 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3786 }
3788 /* T0 */
3789 {
3790 gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3791 gdouble values[] = {0, 0.5, 0.9};
3792 eact = create_adjustment_action( "SpiralT0Action",
3793 _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3794 "/tools/shapes/spiral/t0", 0.0,
3795 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3796 0.0, 0.999, 0.01, 1.0,
3797 labels, values, G_N_ELEMENTS(labels),
3798 sp_spl_tb_t0_value_changed);
3799 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3800 }
3802 /* Reset */
3803 {
3804 InkAction* inky = ink_action_new( "SpiralResetAction",
3805 _("Defaults"),
3806 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3807 GTK_STOCK_CLEAR,
3808 secondarySize );
3809 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3810 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3811 }
3814 sigc::connection *connection = new sigc::connection(
3815 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3816 );
3817 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3818 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3819 }
3821 //########################
3822 //## Pen/Pencil ##
3823 //########################
3825 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3826 static Glib::ustring const
3827 freehand_tool_name(GObject *dataKludge)
3828 {
3829 SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3830 return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3831 ? "/tools/freehand/pen"
3832 : "/tools/freehand/pencil" );
3833 }
3835 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3836 {
3837 gint mode = ege_select_one_action_get_active(act);
3839 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3840 prefs->setInt(freehand_tool_name(tbl) + "/freehand-mode", mode);
3842 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3844 // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3845 // preparatory work here
3846 if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3847 SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3848 sp_pen_context_set_polyline_mode(pc);
3849 }
3850 }
3852 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3853 {
3854 /* Freehand mode toggle buttons */
3855 {
3856 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3857 guint freehandMode = prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/freehand-mode" : "/tools/freehand/pen/freehand-mode" ), 0);
3858 Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
3860 {
3861 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3863 GtkTreeIter iter;
3864 gtk_list_store_append( model, &iter );
3865 gtk_list_store_set( model, &iter,
3866 0, _("Bezier"),
3867 1, _("Create regular Bezier path"),
3868 2, INKSCAPE_ICON_PATH_MODE_BEZIER,
3869 -1 );
3871 gtk_list_store_append( model, &iter );
3872 gtk_list_store_set( model, &iter,
3873 0, _("Spiro"),
3874 1, _("Create Spiro path"),
3875 2, INKSCAPE_ICON_PATH_MODE_SPIRO,
3876 -1 );
3878 if (!tool_is_pencil) {
3879 gtk_list_store_append( model, &iter );
3880 gtk_list_store_set( model, &iter,
3881 0, _("Zigzag"),
3882 1, _("Create a sequence of straight line segments"),
3883 2, INKSCAPE_ICON_PATH_MODE_POLYLINE,
3884 -1 );
3886 gtk_list_store_append( model, &iter );
3887 gtk_list_store_set( model, &iter,
3888 0, _("Paraxial"),
3889 1, _("Create a sequence of paraxial line segments"),
3890 2, INKSCAPE_ICON_PATH_MODE_POLYLINE_PARAXIAL,
3891 -1 );
3892 }
3894 EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3895 "FreehandModeActionPencil" :
3896 "FreehandModeActionPen",
3897 (_("Mode:")), (_("Mode of new lines drawn by this tool")), NULL, GTK_TREE_MODEL(model) );
3898 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3900 ege_select_one_action_set_appearance( act, "full" );
3901 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3902 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3903 ege_select_one_action_set_icon_column( act, 2 );
3904 ege_select_one_action_set_icon_size( act, secondarySize );
3905 ege_select_one_action_set_tooltip_column( act, 1 );
3907 ege_select_one_action_set_active( act, freehandMode);
3908 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3909 }
3910 }
3911 }
3913 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3914 gint shape = ege_select_one_action_get_active( act );
3915 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3916 prefs->setInt(freehand_tool_name(dataKludge) + "/shape", shape);
3917 }
3919 /**
3920 * \brief Generate the list of freehand advanced shape option entries.
3921 */
3922 GList * freehand_shape_dropdown_items_list() {
3923 GList *glist = NULL;
3925 glist = g_list_append (glist, _("None"));
3926 glist = g_list_append (glist, _("Triangle in"));
3927 glist = g_list_append (glist, _("Triangle out"));
3928 glist = g_list_append (glist, _("Ellipse"));
3929 glist = g_list_append (glist, _("From clipboard"));
3931 return glist;
3932 }
3934 static void
3935 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3936 /*advanced shape options */
3937 {
3938 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3939 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3941 GList* items = 0;
3942 gint count = 0;
3943 for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3944 {
3945 GtkTreeIter iter;
3946 gtk_list_store_append( model, &iter );
3947 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3948 count++;
3949 }
3950 g_list_free( items );
3951 items = 0;
3952 EgeSelectOneAction* act1 = ege_select_one_action_new(
3953 tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3954 _("Shape:"), (_("Shape of new paths drawn by this tool")), NULL, GTK_TREE_MODEL(model));
3955 g_object_set( act1, "short_label", _("Shape:"), NULL );
3956 ege_select_one_action_set_appearance( act1, "compact" );
3957 ege_select_one_action_set_active( act1, prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/shape" : "/tools/freehand/pen/shape" ), 0) );
3958 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3959 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3960 g_object_set_data( holder, "shape_action", act1 );
3961 }
3962 }
3964 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3965 {
3966 sp_add_freehand_mode_toggle(mainActions, holder, false);
3967 freehand_add_advanced_shape_options(mainActions, holder, false);
3968 }
3971 static void
3972 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3973 {
3974 GtkWidget *tbl = GTK_WIDGET(obj);
3976 GtkAdjustment *adj;
3978 // fixme: make settable
3979 gdouble tolerance = 4;
3981 adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3982 gtk_adjustment_set_value(adj, tolerance);
3983 gtk_adjustment_value_changed(adj);
3985 spinbutton_defocus(GTK_OBJECT(tbl));
3986 }
3988 static void
3989 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3990 {
3991 // quit if run by the attr_changed listener
3992 if (g_object_get_data( tbl, "freeze" )) {
3993 return;
3994 }
3995 // in turn, prevent listener from responding
3996 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3997 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3998 prefs->setDouble("/tools/freehand/pencil/tolerance", adj->value);
3999 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4000 }
4002 /*
4003 class PencilToleranceObserver : public Inkscape::Preferences::Observer {
4004 public:
4005 PencilToleranceObserver(Glib::ustring const &path, GObject *x) : Observer(path), _obj(x)
4006 {
4007 g_object_set_data(_obj, "prefobserver", this);
4008 }
4009 virtual ~PencilToleranceObserver() {
4010 if (g_object_get_data(_obj, "prefobserver") == this) {
4011 g_object_set_data(_obj, "prefobserver", NULL);
4012 }
4013 }
4014 virtual void notify(Inkscape::Preferences::Entry const &val) {
4015 GObject* tbl = _obj;
4016 if (g_object_get_data( tbl, "freeze" )) {
4017 return;
4018 }
4019 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4021 GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl, "tolerance");
4023 double v = val.getDouble(adj->value);
4024 gtk_adjustment_set_value(adj, v);
4025 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4026 }
4027 private:
4028 GObject *_obj;
4029 };
4030 */
4032 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4033 {
4034 sp_add_freehand_mode_toggle(mainActions, holder, true);
4036 EgeAdjustmentAction* eact = 0;
4038 /* Tolerance */
4039 {
4040 gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
4041 gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
4042 eact = create_adjustment_action( "PencilToleranceAction",
4043 _("Smoothing:"), _("Smoothing: "),
4044 _("How much smoothing (simplifying) is applied to the line"),
4045 "/tools/freehand/pencil/tolerance",
4046 3.0,
4047 GTK_WIDGET(desktop->canvas), NULL,
4048 holder, TRUE, "altx-pencil",
4049 1, 100.0, 0.5, 1.0,
4050 labels, values, G_N_ELEMENTS(labels),
4051 sp_pencil_tb_tolerance_value_changed,
4052 1, 2);
4053 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4054 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4055 }
4057 /* advanced shape options */
4058 freehand_add_advanced_shape_options(mainActions, holder, true);
4060 /* Reset */
4061 {
4062 InkAction* inky = ink_action_new( "PencilResetAction",
4063 _("Defaults"),
4064 _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
4065 GTK_STOCK_CLEAR,
4066 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4067 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
4068 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4069 }
4071 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4073 }
4076 //########################
4077 //## Tweak ##
4078 //########################
4080 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4081 {
4082 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4083 prefs->setDouble( "/tools/tweak/width", adj->value * 0.01 );
4084 }
4086 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4087 {
4088 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4089 prefs->setDouble( "/tools/tweak/force", adj->value * 0.01 );
4090 }
4092 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4093 {
4094 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4095 prefs->setBool("/tools/tweak/usepressure", gtk_toggle_action_get_active(act));
4096 }
4098 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4099 {
4100 int mode = ege_select_one_action_get_active( act );
4101 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4102 prefs->setInt("/tools/tweak/mode", mode);
4104 GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
4105 GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
4106 GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
4107 GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
4108 GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
4109 GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
4110 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
4111 if (doh) gtk_action_set_sensitive (doh, TRUE);
4112 if (dos) gtk_action_set_sensitive (dos, TRUE);
4113 if (dol) gtk_action_set_sensitive (dol, TRUE);
4114 if (doo) gtk_action_set_sensitive (doo, TRUE);
4115 if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
4116 if (fid) gtk_action_set_sensitive (fid, FALSE);
4117 } else {
4118 if (doh) gtk_action_set_sensitive (doh, FALSE);
4119 if (dos) gtk_action_set_sensitive (dos, FALSE);
4120 if (dol) gtk_action_set_sensitive (dol, FALSE);
4121 if (doo) gtk_action_set_sensitive (doo, FALSE);
4122 if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
4123 if (fid) gtk_action_set_sensitive (fid, TRUE);
4124 }
4125 }
4127 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4128 {
4129 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4130 prefs->setDouble( "/tools/tweak/fidelity", adj->value * 0.01 );
4131 }
4133 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
4134 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4135 prefs->setBool("/tools/tweak/doh", gtk_toggle_action_get_active(act));
4136 }
4137 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
4138 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4139 prefs->setBool("/tools/tweak/dos", gtk_toggle_action_get_active(act));
4140 }
4141 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
4142 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4143 prefs->setBool("/tools/tweak/dol", gtk_toggle_action_get_active(act));
4144 }
4145 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
4146 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4147 prefs->setBool("/tools/tweak/doo", gtk_toggle_action_get_active(act));
4148 }
4150 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4151 {
4152 Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
4153 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4155 {
4156 /* Width */
4157 gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
4158 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4159 EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
4160 _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
4161 "/tools/tweak/width", 15,
4162 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
4163 1, 100, 1.0, 10.0,
4164 labels, values, G_N_ELEMENTS(labels),
4165 sp_tweak_width_value_changed, 0.01, 0, 100 );
4166 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4167 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4168 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4169 }
4172 {
4173 /* Force */
4174 gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
4175 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4176 EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
4177 _("Force"), _("Force:"), _("The force of the tweak action"),
4178 "/tools/tweak/force", 20,
4179 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
4180 1, 100, 1.0, 10.0,
4181 labels, values, G_N_ELEMENTS(labels),
4182 sp_tweak_force_value_changed, 0.01, 0, 100 );
4183 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4184 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4185 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4186 }
4188 /* Mode */
4189 {
4190 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4192 GtkTreeIter iter;
4193 gtk_list_store_append( model, &iter );
4194 gtk_list_store_set( model, &iter,
4195 0, _("Move mode"),
4196 1, _("Move objects in any direction"),
4197 2, INKSCAPE_ICON_OBJECT_TWEAK_PUSH,
4198 -1 );
4200 gtk_list_store_append( model, &iter );
4201 gtk_list_store_set( model, &iter,
4202 0, _("Move in/out mode"),
4203 1, _("Move objects towards cursor; with Shift from cursor"),
4204 2, INKSCAPE_ICON_OBJECT_TWEAK_ATTRACT,
4205 -1 );
4207 gtk_list_store_append( model, &iter );
4208 gtk_list_store_set( model, &iter,
4209 0, _("Move jitter mode"),
4210 1, _("Move objects in random directions"),
4211 2, INKSCAPE_ICON_OBJECT_TWEAK_RANDOMIZE,
4212 -1 );
4214 gtk_list_store_append( model, &iter );
4215 gtk_list_store_set( model, &iter,
4216 0, _("Scale mode"),
4217 1, _("Shrink objects, with Shift enlarge"),
4218 2, INKSCAPE_ICON_OBJECT_TWEAK_SHRINK,
4219 -1 );
4221 gtk_list_store_append( model, &iter );
4222 gtk_list_store_set( model, &iter,
4223 0, _("Rotate mode"),
4224 1, _("Rotate objects, with Shift counterclockwise"),
4225 2, INKSCAPE_ICON_OBJECT_TWEAK_ROTATE,
4226 -1 );
4228 gtk_list_store_append( model, &iter );
4229 gtk_list_store_set( model, &iter,
4230 0, _("Duplicate/delete mode"),
4231 1, _("Duplicate objects, with Shift delete"),
4232 2, INKSCAPE_ICON_OBJECT_TWEAK_DUPLICATE,
4233 -1 );
4235 gtk_list_store_append( model, &iter );
4236 gtk_list_store_set( model, &iter,
4237 0, _("Push mode"),
4238 1, _("Push parts of paths in any direction"),
4239 2, INKSCAPE_ICON_PATH_TWEAK_PUSH,
4240 -1 );
4242 gtk_list_store_append( model, &iter );
4243 gtk_list_store_set( model, &iter,
4244 0, _("Shrink/grow mode"),
4245 1, _("Shrink (inset) parts of paths; with Shift grow (outset)"),
4246 2, INKSCAPE_ICON_PATH_TWEAK_SHRINK,
4247 -1 );
4249 gtk_list_store_append( model, &iter );
4250 gtk_list_store_set( model, &iter,
4251 0, _("Attract/repel mode"),
4252 1, _("Attract parts of paths towards cursor; with Shift from cursor"),
4253 2, INKSCAPE_ICON_PATH_TWEAK_ATTRACT,
4254 -1 );
4256 gtk_list_store_append( model, &iter );
4257 gtk_list_store_set( model, &iter,
4258 0, _("Roughen mode"),
4259 1, _("Roughen parts of paths"),
4260 2, INKSCAPE_ICON_PATH_TWEAK_ROUGHEN,
4261 -1 );
4263 gtk_list_store_append( model, &iter );
4264 gtk_list_store_set( model, &iter,
4265 0, _("Color paint mode"),
4266 1, _("Paint the tool's color upon selected objects"),
4267 2, INKSCAPE_ICON_OBJECT_TWEAK_PAINT,
4268 -1 );
4270 gtk_list_store_append( model, &iter );
4271 gtk_list_store_set( model, &iter,
4272 0, _("Color jitter mode"),
4273 1, _("Jitter the colors of selected objects"),
4274 2, INKSCAPE_ICON_OBJECT_TWEAK_JITTER_COLOR,
4275 -1 );
4277 gtk_list_store_append( model, &iter );
4278 gtk_list_store_set( model, &iter,
4279 0, _("Blur mode"),
4280 1, _("Blur selected objects more; with Shift, blur less"),
4281 2, INKSCAPE_ICON_OBJECT_TWEAK_BLUR,
4282 -1 );
4285 EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4286 g_object_set( act, "short_label", _("Mode:"), NULL );
4287 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4288 g_object_set_data( holder, "mode_action", act );
4290 ege_select_one_action_set_appearance( act, "full" );
4291 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4292 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4293 ege_select_one_action_set_icon_column( act, 2 );
4294 ege_select_one_action_set_icon_size( act, secondarySize );
4295 ege_select_one_action_set_tooltip_column( act, 1 );
4297 gint mode = prefs->getInt("/tools/tweak/mode", 0);
4298 ege_select_one_action_set_active( act, mode );
4299 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
4301 g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
4302 }
4304 guint mode = prefs->getInt("/tools/tweak/mode", 0);
4306 {
4307 EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
4308 ege_output_action_set_use_markup( act, TRUE );
4309 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4310 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4311 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4312 g_object_set_data( holder, "tweak_channels_label", act);
4313 }
4315 {
4316 InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
4317 _("Hue"),
4318 _("In color mode, act on objects' hue"),
4319 NULL,
4320 Inkscape::ICON_SIZE_DECORATION );
4321 //TRANSLATORS: "H" here stands for hue
4322 g_object_set( act, "short_label", _("H"), NULL );
4323 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4324 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
4325 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doh", true) );
4326 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4327 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4328 g_object_set_data( holder, "tweak_doh", act);
4329 }
4330 {
4331 InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
4332 _("Saturation"),
4333 _("In color mode, act on objects' saturation"),
4334 NULL,
4335 Inkscape::ICON_SIZE_DECORATION );
4336 //TRANSLATORS: "S" here stands for Saturation
4337 g_object_set( act, "short_label", _("S"), NULL );
4338 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4339 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
4340 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dos", true) );
4341 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4342 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4343 g_object_set_data( holder, "tweak_dos", act );
4344 }
4345 {
4346 InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
4347 _("Lightness"),
4348 _("In color mode, act on objects' lightness"),
4349 NULL,
4350 Inkscape::ICON_SIZE_DECORATION );
4351 //TRANSLATORS: "L" here stands for Lightness
4352 g_object_set( act, "short_label", _("L"), NULL );
4353 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4354 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
4355 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dol", true) );
4356 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4357 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4358 g_object_set_data( holder, "tweak_dol", act );
4359 }
4360 {
4361 InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
4362 _("Opacity"),
4363 _("In color mode, act on objects' opacity"),
4364 NULL,
4365 Inkscape::ICON_SIZE_DECORATION );
4366 //TRANSLATORS: "O" here stands for Opacity
4367 g_object_set( act, "short_label", _("O"), NULL );
4368 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4369 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
4370 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doo", true) );
4371 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4372 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4373 g_object_set_data( holder, "tweak_doo", act );
4374 }
4376 { /* Fidelity */
4377 gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
4378 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4379 EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
4380 _("Fidelity"), _("Fidelity:"),
4381 _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
4382 "/tools/tweak/fidelity", 50,
4383 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
4384 1, 100, 1.0, 10.0,
4385 labels, values, G_N_ELEMENTS(labels),
4386 sp_tweak_fidelity_value_changed, 0.01, 0, 100 );
4387 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4388 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4389 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
4390 gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
4391 g_object_set_data( holder, "tweak_fidelity", eact );
4392 }
4395 /* Use Pressure button */
4396 {
4397 InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
4398 _("Pressure"),
4399 _("Use the pressure of the input device to alter the force of tweak action"),
4400 INKSCAPE_ICON_DRAW_USE_PRESSURE,
4401 Inkscape::ICON_SIZE_DECORATION );
4402 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4403 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
4404 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/usepressure", true) );
4405 }
4407 }
4410 //########################
4411 //## Spray ##
4412 //########################
4414 static void sp_spray_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4415 {
4416 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4417 prefs->setDouble( "/tools/spray/width", adj->value );
4418 }
4420 static void sp_spray_mean_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4421 {
4422 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4423 prefs->setDouble( "/tools/spray/mean", adj->value );
4424 }
4426 static void sp_spray_standard_deviation_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4427 {
4428 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4429 prefs->setDouble( "/tools/spray/standard_deviation", adj->value );
4430 }
4432 static void sp_spray_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4433 {
4434 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4435 prefs->setBool("/tools/spray/usepressure", gtk_toggle_action_get_active(act));
4436 }
4438 static void sp_spray_mode_changed( EgeSelectOneAction *act, GObject */*tbl*/ )
4439 {
4440 int mode = ege_select_one_action_get_active( act );
4441 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4442 prefs->setInt("/tools/spray/mode", mode);
4443 }
4445 static void sp_spray_population_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4446 {
4447 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4448 prefs->setDouble( "/tools/spray/population", adj->value );
4449 }
4451 static void sp_spray_rotation_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4452 {
4453 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4454 prefs->setDouble( "/tools/spray/rotation_variation", adj->value );
4455 }
4457 static void sp_spray_scale_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4458 {
4459 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4460 prefs->setDouble( "/tools/spray/scale_variation", adj->value );
4461 }
4464 static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4465 {
4466 Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
4467 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4469 {
4470 /* Width */
4471 gchar const* labels[] = {_("(narrow spray)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad spray)")};
4472 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4473 EgeAdjustmentAction *eact = create_adjustment_action( "SprayWidthAction",
4474 _("Width"), _("Width:"), _("The width of the spray area (relative to the visible canvas area)"),
4475 "/tools/spray/width", 15,
4476 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spray",
4477 1, 100, 1.0, 10.0,
4478 labels, values, G_N_ELEMENTS(labels),
4479 sp_spray_width_value_changed, 1, 0 );
4480 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4481 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4482 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4483 }
4485 {
4486 /* Mean */
4487 gchar const* labels[] = {_("(minimum mean)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum mean)")};
4488 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4489 EgeAdjustmentAction *eact = create_adjustment_action( "SprayMeanAction",
4490 _("Focus"), _("Focus:"), _("0 to spray a spot. Increase to enlarge the ring radius."),
4491 "/tools/spray/mean", 0,
4492 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-mean",
4493 0, 100, 1.0, 10.0,
4494 labels, values, G_N_ELEMENTS(labels),
4495 sp_spray_mean_value_changed, 1, 0 );
4496 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4497 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4498 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4499 }
4501 {
4502 /* Standard_deviation */
4503 gchar const* labels[] = {_("(minimum scatter)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum scatter)")};
4504 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4505 EgeAdjustmentAction *eact = create_adjustment_action( "SprayStandard_deviationAction",
4506 _("Scatter"), _("Scatter:"), _("Increase to scatter sprayed objects."),
4507 "/tools/spray/standard_deviation", 70,
4508 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-standard_deviation",
4509 1, 100, 1.0, 10.0,
4510 labels, values, G_N_ELEMENTS(labels),
4511 sp_spray_standard_deviation_value_changed, 1, 0 );
4512 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4513 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4514 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4515 }
4517 /* Mode */
4518 {
4519 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4521 GtkTreeIter iter;
4522 gtk_list_store_append( model, &iter );
4523 gtk_list_store_set( model, &iter,
4524 0, _("Spray with copies"),
4525 1, _("Spray copies of the initial selection"),
4526 2, INKSCAPE_ICON_SPRAY_COPY_MODE,
4527 -1 );
4529 gtk_list_store_append( model, &iter );
4530 gtk_list_store_set( model, &iter,
4531 0, _("Spray with clones"),
4532 1, _("Spray clones of the initial selection"),
4533 2, INKSCAPE_ICON_SPRAY_CLONE_MODE,
4534 -1 );
4536 gtk_list_store_append( model, &iter );
4537 gtk_list_store_set( model, &iter,
4538 0, _("Spray single path"),
4539 1, _("Spray objects in a single path"),
4540 2, INKSCAPE_ICON_SPRAY_UNION_MODE,
4541 -1 );
4543 EgeSelectOneAction* act = ege_select_one_action_new( "SprayModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4544 g_object_set( act, "short_label", _("Mode:"), NULL );
4545 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4546 g_object_set_data( holder, "mode_action", act );
4548 ege_select_one_action_set_appearance( act, "full" );
4549 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4550 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4551 ege_select_one_action_set_icon_column( act, 2 );
4552 ege_select_one_action_set_icon_size( act, secondarySize );
4553 ege_select_one_action_set_tooltip_column( act, 1 );
4555 gint mode = prefs->getInt("/tools/spray/mode", 1);
4556 ege_select_one_action_set_active( act, mode );
4557 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_spray_mode_changed), holder );
4559 g_object_set_data( G_OBJECT(holder), "spray_tool_mode", act);
4560 }
4562 { /* Population */
4563 gchar const* labels[] = {_("(low population)"), 0, 0, _("(default)"), 0, 0, _("(high population)")};
4564 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4565 EgeAdjustmentAction *eact = create_adjustment_action( "SprayPopulationAction",
4566 _("Amount"), _("Amount:"),
4567 _("Adjusts the number of items sprayed per clic."),
4568 "/tools/spray/population", 70,
4569 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-population",
4570 1, 100, 1.0, 10.0,
4571 labels, values, G_N_ELEMENTS(labels),
4572 sp_spray_population_value_changed, 1, 0 );
4573 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4574 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4575 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4576 g_object_set_data( holder, "spray_population", eact );
4577 }
4579 /* Use Pressure button */
4580 {
4581 InkToggleAction* act = ink_toggle_action_new( "SprayPressureAction",
4582 _("Pressure"),
4583 _("Use the pressure of the input device to alter the amount of sprayed objects."),
4584 "use_pressure",
4585 Inkscape::ICON_SIZE_DECORATION );
4586 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4587 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_spray_pressure_state_changed), NULL);
4588 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/spray/usepressure", true) );
4589 }
4591 { /* Rotation */
4592 gchar const* labels[] = {_("(low rotation variation)"), 0, 0, _("(default)"), 0, 0, _("(high rotation variation)")};
4593 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4594 EgeAdjustmentAction *eact = create_adjustment_action( "SprayRotationAction",
4595 _("Rotation"), _("Rotation:"),
4596 // xgettext:no-c-format
4597 _("Variation of the rotation of the sprayed objects. 0% for the same rotation than the original object."),
4598 "/tools/spray/rotation_variation", 0,
4599 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-rotation",
4600 0, 100, 1.0, 10.0,
4601 labels, values, G_N_ELEMENTS(labels),
4602 sp_spray_rotation_value_changed, 1, 0 );
4603 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4604 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4605 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4606 g_object_set_data( holder, "spray_rotation", eact );
4607 }
4609 { /* Scale */
4610 gchar const* labels[] = {_("(low scale variation)"), 0, 0, _("(default)"), 0, 0, _("(high scale variation)")};
4611 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4612 EgeAdjustmentAction *eact = create_adjustment_action( "SprayScaleAction",
4613 _("Scale"), _("Scale:"),
4614 // xgettext:no-c-format
4615 _("Variation in the scale of the sprayed objects. 0% for the same scale than the original object."),
4616 "/tools/spray/scale_variation", 0,
4617 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-scale",
4618 0, 100, 1.0, 10.0,
4619 labels, values, G_N_ELEMENTS(labels),
4620 sp_spray_scale_value_changed, 1, 0 );
4621 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4622 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4623 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4624 g_object_set_data( holder, "spray_scale", eact );
4625 }
4629 }
4632 //########################
4633 //## Calligraphy ##
4634 //########################
4635 static void update_presets_list (GObject *tbl)
4636 {
4637 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4638 if (g_object_get_data(tbl, "presets_blocked"))
4639 return;
4641 EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4642 if (!sel) {
4643 // WTF!? This will cause a segfault if ever reached
4644 //ege_select_one_action_set_active(sel, 0);
4645 return;
4646 }
4648 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4650 int ege_index = 1;
4651 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++ege_index) {
4652 bool match = true;
4654 std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(*i);
4655 for (std::vector<Inkscape::Preferences::Entry>::iterator j = preset.begin(); j != preset.end(); ++j) {
4656 Glib::ustring entry_name = j->getEntryName();
4657 if (entry_name == "id" || entry_name == "name") continue;
4659 void *widget = g_object_get_data(tbl, entry_name.data());
4660 if (widget) {
4661 if (GTK_IS_ADJUSTMENT(widget)) {
4662 double v = j->getDouble();
4663 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4664 //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
4665 if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
4666 match = false;
4667 break;
4668 }
4669 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4670 bool v = j->getBool();
4671 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4672 //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
4673 if ( static_cast<bool>(gtk_toggle_action_get_active(toggle)) != v ) {
4674 match = false;
4675 break;
4676 }
4677 }
4678 }
4679 }
4681 if (match) {
4682 // newly added item is at the same index as the
4683 // save command, so we need to change twice for it to take effect
4684 ege_select_one_action_set_active(sel, 0);
4685 ege_select_one_action_set_active(sel, ege_index); // one-based index
4686 return;
4687 }
4688 }
4690 // no match found
4691 ege_select_one_action_set_active(sel, 0);
4692 }
4694 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
4695 {
4696 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4697 prefs->setDouble( "/tools/calligraphic/mass", adj->value );
4698 update_presets_list(tbl);
4699 }
4701 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
4702 {
4703 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4704 prefs->setDouble( "/tools/calligraphic/wiggle", adj->value );
4705 update_presets_list(tbl);
4706 }
4708 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
4709 {
4710 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4711 prefs->setDouble( "/tools/calligraphic/angle", adj->value );
4712 update_presets_list(tbl);
4713 }
4715 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
4716 {
4717 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4718 prefs->setDouble( "/tools/calligraphic/width", adj->value );
4719 update_presets_list(tbl);
4720 }
4722 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
4723 {
4724 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4725 prefs->setDouble("/tools/calligraphic/thinning", adj->value );
4726 update_presets_list(tbl);
4727 }
4729 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
4730 {
4731 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4732 prefs->setDouble( "/tools/calligraphic/flatness", adj->value );
4733 update_presets_list(tbl);
4734 }
4736 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
4737 {
4738 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4739 prefs->setDouble( "/tools/calligraphic/tremor", adj->value );
4740 update_presets_list(tbl);
4741 }
4743 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
4744 {
4745 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4746 prefs->setDouble( "/tools/calligraphic/cap_rounding", adj->value );
4747 update_presets_list(tbl);
4748 }
4750 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject* tbl )
4751 {
4752 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4753 prefs->setBool("/tools/calligraphic/usepressure", gtk_toggle_action_get_active( act ));
4754 update_presets_list(tbl);
4755 }
4757 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject* tbl )
4758 {
4759 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4760 prefs->setBool("/tools/calligraphic/tracebackground", gtk_toggle_action_get_active( act ));
4761 update_presets_list(tbl);
4762 }
4764 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject* tbl )
4765 {
4766 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4767 GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
4768 prefs->setBool("/tools/calligraphic/usetilt", gtk_toggle_action_get_active( act ));
4769 update_presets_list(tbl);
4770 if (calligraphy_angle )
4771 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
4772 }
4775 static gchar const *const widget_names[] = {
4776 "width",
4777 "mass",
4778 "wiggle",
4779 "angle",
4780 "thinning",
4781 "tremor",
4782 "flatness",
4783 "cap_rounding",
4784 "usepressure",
4785 "tracebackground",
4786 "usetilt"
4787 };
4790 static void sp_dcc_build_presets_list(GObject *tbl)
4791 {
4792 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4794 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4795 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4796 gtk_list_store_clear (model);
4798 {
4799 GtkTreeIter iter;
4800 gtk_list_store_append( model, &iter );
4801 gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4802 }
4804 // iterate over all presets to populate the list
4805 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4806 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4807 int ii=1;
4809 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i) {
4810 GtkTreeIter iter;
4811 Glib::ustring preset_name = prefs->getString(*i + "/name");
4812 gtk_list_store_append( model, &iter );
4813 gtk_list_store_set( model, &iter, 0, _(preset_name.data()), 1, ii++, -1 );
4814 }
4816 {
4817 GtkTreeIter iter;
4818 gtk_list_store_append( model, &iter );
4819 gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4820 g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4821 }
4823 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4825 update_presets_list (tbl);
4826 }
4828 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4829 {
4830 using Inkscape::UI::Dialog::CalligraphicProfileRename;
4831 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4832 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4833 if (! desktop) return;
4835 if (g_object_get_data(tbl, "presets_blocked"))
4836 return;
4838 CalligraphicProfileRename::show(desktop);
4839 if ( !CalligraphicProfileRename::applied()) {
4840 // dialog cancelled
4841 update_presets_list (tbl);
4842 return;
4843 }
4844 Glib::ustring profile_name = CalligraphicProfileRename::getProfileName();
4846 if (profile_name.empty()) {
4847 // empty name entered
4848 update_presets_list (tbl);
4849 return;
4850 }
4852 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4854 // If there's a preset with the given name, find it and set save_path appropriately
4855 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4856 int total_presets = presets.size();
4857 int new_index = -1;
4858 Glib::ustring save_path; // profile pref path without a trailing slash
4860 int temp_index = 0;
4861 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++temp_index) {
4862 Glib::ustring name = prefs->getString(*i + "/name");
4863 if (!name.empty() && profile_name == name) {
4864 new_index = temp_index;
4865 save_path = *i;
4866 break;
4867 }
4868 }
4870 if (new_index == -1) {
4871 // no preset with this name, create
4872 new_index = total_presets + 1;
4873 gchar *profile_id = g_strdup_printf("/dcc%d", new_index);
4874 save_path = Glib::ustring("/tools/calligraphic/preset") + profile_id;
4875 g_free(profile_id);
4876 }
4878 for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4879 gchar const *const widget_name = widget_names[i];
4880 void *widget = g_object_get_data(tbl, widget_name);
4881 if (widget) {
4882 if (GTK_IS_ADJUSTMENT(widget)) {
4883 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4884 prefs->setDouble(save_path + "/" + widget_name, gtk_adjustment_get_value(adj));
4885 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4886 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4887 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4888 prefs->setBool(save_path + "/" + widget_name, gtk_toggle_action_get_active(toggle));
4889 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4890 } else {
4891 g_warning("Unknown widget type for preset: %s\n", widget_name);
4892 }
4893 } else {
4894 g_warning("Bad key when writing preset: %s\n", widget_name);
4895 }
4896 }
4897 prefs->setString(save_path + "/name", profile_name);
4899 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4900 sp_dcc_build_presets_list (tbl);
4901 }
4904 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4906 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4908 gint preset_index = ege_select_one_action_get_active( act );
4909 // This is necessary because EgeSelectOneAction spams us with GObject "changed" signal calls
4910 // even when the preset is not changed. It would be good to replace it with something more
4911 // modern. Index 0 means "No preset", so we don't do anything.
4912 if (preset_index == 0) return;
4914 gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4916 if (preset_index == save_presets_index) {
4917 // this is the Save command
4918 sp_dcc_save_profile(NULL, tbl);
4919 return;
4920 }
4922 if (g_object_get_data(tbl, "presets_blocked"))
4923 return;
4925 // preset_index is one-based so we subtract 1
4926 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4927 Glib::ustring preset_path = presets.at(preset_index - 1);
4929 if (!preset_path.empty()) {
4930 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
4932 std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(preset_path);
4934 // Shouldn't this be std::map?
4935 for (std::vector<Inkscape::Preferences::Entry>::iterator i = preset.begin(); i != preset.end(); ++i) {
4936 Glib::ustring entry_name = i->getEntryName();
4937 if (entry_name == "id" || entry_name == "name") continue;
4938 void *widget = g_object_get_data(tbl, entry_name.data());
4939 if (widget) {
4940 if (GTK_IS_ADJUSTMENT(widget)) {
4941 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4942 gtk_adjustment_set_value(adj, i->getDouble());
4943 //std::cout << "set adj " << attr_name << " to " << v << "\n";
4944 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4945 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4946 gtk_toggle_action_set_active(toggle, i->getBool());
4947 //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4948 } else {
4949 g_warning("Unknown widget type for preset: %s\n", entry_name.data());
4950 }
4951 } else {
4952 g_warning("Bad key found in a preset record: %s\n", entry_name.data());
4953 }
4954 }
4955 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4956 }
4957 }
4960 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4961 {
4962 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4963 {
4964 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4966 EgeAdjustmentAction* calligraphy_angle = 0;
4968 {
4969 /* Width */
4970 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4971 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4972 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4973 _("Pen Width"), _("Width:"),
4974 _("The width of the calligraphic pen (relative to the visible canvas area)"),
4975 "/tools/calligraphic/width", 15,
4976 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4977 1, 100, 1.0, 10.0,
4978 labels, values, G_N_ELEMENTS(labels),
4979 sp_ddc_width_value_changed, 1, 0 );
4980 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4981 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4982 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4983 }
4985 {
4986 /* Thinning */
4987 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4988 gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4989 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4990 _("Stroke Thinning"), _("Thinning:"),
4991 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4992 "/tools/calligraphic/thinning", 10,
4993 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4994 -100, 100, 1, 10.0,
4995 labels, values, G_N_ELEMENTS(labels),
4996 sp_ddc_velthin_value_changed, 1, 0);
4997 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4998 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4999 }
5001 {
5002 /* Angle */
5003 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
5004 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
5005 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
5006 _("Pen Angle"), _("Angle:"),
5007 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
5008 "/tools/calligraphic/angle", 30,
5009 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
5010 -90.0, 90.0, 1.0, 10.0,
5011 labels, values, G_N_ELEMENTS(labels),
5012 sp_ddc_angle_value_changed, 1, 0 );
5013 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5014 g_object_set_data( holder, "angle_action", eact );
5015 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5016 calligraphy_angle = eact;
5017 }
5019 {
5020 /* Fixation */
5021 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
5022 gdouble values[] = {0, 20, 40, 60, 90, 100};
5023 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
5024 _("Fixation"), _("Fixation:"),
5025 _("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"),
5026 "/tools/calligraphic/flatness", 90,
5027 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5028 0.0, 100, 1.0, 10.0,
5029 labels, values, G_N_ELEMENTS(labels),
5030 sp_ddc_flatness_value_changed, 1, 0);
5031 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5032 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5033 }
5035 {
5036 /* Cap Rounding */
5037 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
5038 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
5039 // TRANSLATORS: "cap" means "end" (both start and finish) here
5040 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
5041 _("Cap rounding"), _("Caps:"),
5042 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
5043 "/tools/calligraphic/cap_rounding", 0.0,
5044 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5045 0.0, 5.0, 0.01, 0.1,
5046 labels, values, G_N_ELEMENTS(labels),
5047 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
5048 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5049 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5050 }
5052 {
5053 /* Tremor */
5054 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
5055 gdouble values[] = {0, 10, 20, 40, 60, 100};
5056 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
5057 _("Stroke Tremor"), _("Tremor:"),
5058 _("Increase to make strokes rugged and trembling"),
5059 "/tools/calligraphic/tremor", 0.0,
5060 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5061 0.0, 100, 1, 10.0,
5062 labels, values, G_N_ELEMENTS(labels),
5063 sp_ddc_tremor_value_changed, 1, 0);
5065 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5066 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5067 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5068 }
5070 {
5071 /* Wiggle */
5072 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
5073 gdouble values[] = {0, 20, 40, 60, 100};
5074 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
5075 _("Pen Wiggle"), _("Wiggle:"),
5076 _("Increase to make the pen waver and wiggle"),
5077 "/tools/calligraphic/wiggle", 0.0,
5078 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5079 0.0, 100, 1, 10.0,
5080 labels, values, G_N_ELEMENTS(labels),
5081 sp_ddc_wiggle_value_changed, 1, 0);
5082 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5083 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5084 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5085 }
5087 {
5088 /* Mass */
5089 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
5090 gdouble values[] = {0.0, 2, 10, 20, 50, 100};
5091 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
5092 _("Pen Mass"), _("Mass:"),
5093 _("Increase to make the pen drag behind, as if slowed by inertia"),
5094 "/tools/calligraphic/mass", 2.0,
5095 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5096 0.0, 100, 1, 10.0,
5097 labels, values, G_N_ELEMENTS(labels),
5098 sp_ddc_mass_value_changed, 1, 0);
5099 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5100 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5101 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5102 }
5105 /* Trace Background button */
5106 {
5107 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
5108 _("Trace Background"),
5109 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
5110 INKSCAPE_ICON_DRAW_TRACE_BACKGROUND,
5111 Inkscape::ICON_SIZE_DECORATION );
5112 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5113 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
5114 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/tracebackground", false) );
5115 g_object_set_data( holder, "tracebackground", act );
5116 }
5118 /* Use Pressure button */
5119 {
5120 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
5121 _("Pressure"),
5122 _("Use the pressure of the input device to alter the width of the pen"),
5123 INKSCAPE_ICON_DRAW_USE_PRESSURE,
5124 Inkscape::ICON_SIZE_DECORATION );
5125 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5126 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
5127 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usepressure", true) );
5128 g_object_set_data( holder, "usepressure", act );
5129 }
5131 /* Use Tilt button */
5132 {
5133 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
5134 _("Tilt"),
5135 _("Use the tilt of the input device to alter the angle of the pen's nib"),
5136 INKSCAPE_ICON_DRAW_USE_TILT,
5137 Inkscape::ICON_SIZE_DECORATION );
5138 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5139 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
5140 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs->getBool("/tools/calligraphic/usetilt", true) );
5141 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usetilt", true) );
5142 g_object_set_data( holder, "usetilt", act );
5143 }
5145 /*calligraphic profile */
5146 {
5147 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5148 EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
5149 ege_select_one_action_set_appearance (act1, "compact");
5150 g_object_set_data (holder, "profile_selector", act1 );
5152 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
5154 sp_dcc_build_presets_list (holder);
5156 g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
5157 gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
5158 }
5159 }
5160 }
5163 //########################
5164 //## Circle / Arc ##
5165 //########################
5167 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
5168 {
5169 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
5170 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
5172 if (v1 == 0 && v2 == 0) {
5173 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
5174 gtk_action_set_sensitive( ocb, FALSE );
5175 gtk_action_set_sensitive( make_whole, FALSE );
5176 }
5177 } else {
5178 gtk_action_set_sensitive( ocb, TRUE );
5179 gtk_action_set_sensitive( make_whole, TRUE );
5180 }
5181 }
5183 static void
5184 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
5185 {
5186 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5188 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5189 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5190 prefs->setDouble(Glib::ustring("/tools/shapes/arc/") + value_name, adj->value);
5191 }
5193 // quit if run by the attr_changed listener
5194 if (g_object_get_data( tbl, "freeze" )) {
5195 return;
5196 }
5198 // in turn, prevent listener from responding
5199 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5201 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
5203 bool modmade = false;
5204 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5205 items != NULL;
5206 items = items->next)
5207 {
5208 SPItem *item = SP_ITEM(items->data);
5210 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
5212 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
5213 SPArc *arc = SP_ARC(item);
5215 if (!strcmp(value_name, "start"))
5216 ge->start = (adj->value * M_PI)/ 180;
5217 else
5218 ge->end = (adj->value * M_PI)/ 180;
5220 sp_genericellipse_normalize(ge);
5221 ((SPObject *)arc)->updateRepr();
5222 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
5224 modmade = true;
5225 }
5226 }
5228 g_free(namespaced_name);
5230 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
5232 sp_arctb_sensitivize( tbl, adj->value, other->value );
5234 if (modmade) {
5235 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
5236 _("Arc: Change start/end"));
5237 }
5239 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5240 }
5243 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
5244 {
5245 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
5246 }
5248 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
5249 {
5250 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
5251 }
5254 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
5255 {
5256 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5257 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5258 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5259 prefs->setBool("/tools/shapes/arc/open", ege_select_one_action_get_active(act) != 0);
5260 }
5262 // quit if run by the attr_changed listener
5263 if (g_object_get_data( tbl, "freeze" )) {
5264 return;
5265 }
5267 // in turn, prevent listener from responding
5268 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5270 bool modmade = false;
5272 if ( ege_select_one_action_get_active(act) != 0 ) {
5273 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5274 items != NULL;
5275 items = items->next)
5276 {
5277 if (SP_IS_ARC((SPItem *) items->data)) {
5278 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5279 repr->setAttribute("sodipodi:open", "true");
5280 SP_OBJECT((SPItem *) items->data)->updateRepr();
5281 modmade = true;
5282 }
5283 }
5284 } else {
5285 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5286 items != NULL;
5287 items = items->next)
5288 {
5289 if (SP_IS_ARC((SPItem *) items->data)) {
5290 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5291 repr->setAttribute("sodipodi:open", NULL);
5292 SP_OBJECT((SPItem *) items->data)->updateRepr();
5293 modmade = true;
5294 }
5295 }
5296 }
5298 if (modmade) {
5299 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
5300 _("Arc: Change open/closed"));
5301 }
5303 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5304 }
5306 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
5307 {
5308 GtkAdjustment *adj;
5309 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
5310 gtk_adjustment_set_value(adj, 0.0);
5311 gtk_adjustment_value_changed(adj);
5313 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
5314 gtk_adjustment_set_value(adj, 0.0);
5315 gtk_adjustment_value_changed(adj);
5317 spinbutton_defocus( GTK_OBJECT(obj) );
5318 }
5320 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
5321 gchar const */*old_value*/, gchar const */*new_value*/,
5322 bool /*is_interactive*/, gpointer data)
5323 {
5324 GObject *tbl = G_OBJECT(data);
5326 // quit if run by the _changed callbacks
5327 if (g_object_get_data( tbl, "freeze" )) {
5328 return;
5329 }
5331 // in turn, prevent callbacks from responding
5332 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5334 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
5335 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
5337 GtkAdjustment *adj1,*adj2;
5338 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
5339 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
5340 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
5341 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
5343 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
5345 char const *openstr = NULL;
5346 openstr = repr->attribute("sodipodi:open");
5347 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
5349 if (openstr) {
5350 ege_select_one_action_set_active( ocb, 1 );
5351 } else {
5352 ege_select_one_action_set_active( ocb, 0 );
5353 }
5355 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5356 }
5358 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
5359 NULL, /* child_added */
5360 NULL, /* child_removed */
5361 arc_tb_event_attr_changed,
5362 NULL, /* content_changed */
5363 NULL /* order_changed */
5364 };
5367 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
5368 {
5369 int n_selected = 0;
5370 Inkscape::XML::Node *repr = NULL;
5372 purge_repr_listener( tbl, tbl );
5374 for (GSList const *items = selection->itemList();
5375 items != NULL;
5376 items = items->next)
5377 {
5378 if (SP_IS_ARC((SPItem *) items->data)) {
5379 n_selected++;
5380 repr = SP_OBJECT_REPR((SPItem *) items->data);
5381 }
5382 }
5384 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
5386 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
5387 if (n_selected == 0) {
5388 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
5389 } else if (n_selected == 1) {
5390 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
5391 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5393 if (repr) {
5394 g_object_set_data( tbl, "repr", repr );
5395 Inkscape::GC::anchor(repr);
5396 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
5397 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
5398 }
5399 } else {
5400 // FIXME: implement averaging of all parameters for multiple selected
5401 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
5402 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5403 sp_arctb_sensitivize( tbl, 1, 0 );
5404 }
5405 }
5408 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5409 {
5410 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5412 EgeAdjustmentAction* eact = 0;
5413 Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
5416 {
5417 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
5418 ege_output_action_set_use_markup( act, TRUE );
5419 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5420 g_object_set_data( holder, "mode_action", act );
5421 }
5423 /* Start */
5424 {
5425 eact = create_adjustment_action( "ArcStartAction",
5426 _("Start"), _("Start:"),
5427 _("The angle (in degrees) from the horizontal to the arc's start point"),
5428 "/tools/shapes/arc/start", 0.0,
5429 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
5430 -360.0, 360.0, 1.0, 10.0,
5431 0, 0, 0,
5432 sp_arctb_start_value_changed);
5433 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5434 }
5436 /* End */
5437 {
5438 eact = create_adjustment_action( "ArcEndAction",
5439 _("End"), _("End:"),
5440 _("The angle (in degrees) from the horizontal to the arc's end point"),
5441 "/tools/shapes/arc/end", 0.0,
5442 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
5443 -360.0, 360.0, 1.0, 10.0,
5444 0, 0, 0,
5445 sp_arctb_end_value_changed);
5446 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5447 }
5449 /* Segments / Pie checkbox */
5450 {
5451 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5453 GtkTreeIter iter;
5454 gtk_list_store_append( model, &iter );
5455 gtk_list_store_set( model, &iter,
5456 0, _("Closed arc"),
5457 1, _("Switch to segment (closed shape with two radii)"),
5458 2, INKSCAPE_ICON_DRAW_ELLIPSE_SEGMENT,
5459 -1 );
5461 gtk_list_store_append( model, &iter );
5462 gtk_list_store_set( model, &iter,
5463 0, _("Open Arc"),
5464 1, _("Switch to arc (unclosed shape)"),
5465 2, INKSCAPE_ICON_DRAW_ELLIPSE_ARC,
5466 -1 );
5468 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5469 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5470 g_object_set_data( holder, "open_action", act );
5472 ege_select_one_action_set_appearance( act, "full" );
5473 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5474 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5475 ege_select_one_action_set_icon_column( act, 2 );
5476 ege_select_one_action_set_icon_size( act, secondarySize );
5477 ege_select_one_action_set_tooltip_column( act, 1 );
5479 bool isClosed = !prefs->getBool("/tools/shapes/arc/open", false);
5480 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
5481 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
5482 }
5484 /* Make Whole */
5485 {
5486 InkAction* inky = ink_action_new( "ArcResetAction",
5487 _("Make whole"),
5488 _("Make the shape a whole ellipse, not arc or segment"),
5489 INKSCAPE_ICON_DRAW_ELLIPSE_WHOLE,
5490 secondarySize );
5491 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
5492 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5493 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
5494 g_object_set_data( holder, "make_whole", inky );
5495 }
5497 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
5498 // sensitivize make whole and open checkbox
5499 {
5500 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
5501 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
5502 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
5503 }
5506 sigc::connection *connection = new sigc::connection(
5507 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
5508 );
5509 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
5510 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
5511 }
5516 // toggle button callbacks and updaters
5518 //########################
5519 //## Dropper ##
5520 //########################
5522 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
5523 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5524 prefs->setInt( "/tools/dropper/pick", gtk_toggle_action_get_active( act ) );
5525 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
5526 if ( set_action ) {
5527 if ( gtk_toggle_action_get_active( act ) ) {
5528 gtk_action_set_sensitive( set_action, TRUE );
5529 } else {
5530 gtk_action_set_sensitive( set_action, FALSE );
5531 }
5532 }
5534 spinbutton_defocus(GTK_OBJECT(tbl));
5535 }
5537 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
5538 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5539 prefs->setBool( "/tools/dropper/setalpha", gtk_toggle_action_get_active( act ) );
5540 spinbutton_defocus(GTK_OBJECT(tbl));
5541 }
5544 /**
5545 * Dropper auxiliary toolbar construction and setup.
5546 *
5547 * TODO: Would like to add swatch of current color.
5548 * TODO: Add queue of last 5 or so colors selected with new swatches so that
5549 * can drag and drop places. Will provide a nice mixing palette.
5550 */
5551 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
5552 {
5553 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5554 gint pickAlpha = prefs->getInt( "/tools/dropper/pick", 1 );
5556 {
5557 EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
5558 ege_output_action_set_use_markup( act, TRUE );
5559 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5560 }
5562 {
5563 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
5564 _("Pick opacity"),
5565 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
5566 NULL,
5567 Inkscape::ICON_SIZE_DECORATION );
5568 g_object_set( act, "short_label", _("Pick"), NULL );
5569 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5570 g_object_set_data( holder, "pick_action", act );
5571 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
5572 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
5573 }
5575 {
5576 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
5577 _("Assign opacity"),
5578 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
5579 NULL,
5580 Inkscape::ICON_SIZE_DECORATION );
5581 g_object_set( act, "short_label", _("Assign"), NULL );
5582 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5583 g_object_set_data( holder, "set_action", act );
5584 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/dropper/setalpha", true) );
5585 // make sure it's disabled if we're not picking alpha
5586 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
5587 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
5588 }
5589 }
5592 //########################
5593 //## LPETool ##
5594 //########################
5596 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
5598 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
5599 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
5600 {
5601 using namespace Inkscape::LivePathEffect;
5603 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
5604 SPEventContext *ec = desktop->event_context;
5605 if (!SP_IS_LPETOOL_CONTEXT(ec)) {
5606 return;
5607 }
5609 // only take action if run by the attr_changed listener
5610 if (!g_object_get_data(tbl, "freeze")) {
5611 // in turn, prevent listener from responding
5612 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5614 gint mode = ege_select_one_action_get_active(act);
5615 EffectType type = lpesubtools[mode].type;
5617 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5618 bool success = lpetool_try_construction(lc, type);
5619 if (success) {
5620 // since the construction was already performed, we set the state back to inactive
5621 ege_select_one_action_set_active(act, 0);
5622 mode = 0;
5623 } else {
5624 // switch to the chosen subtool
5625 SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
5626 }
5628 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5629 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5630 prefs->setInt( "/tools/lpetool/mode", mode );
5631 }
5633 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
5634 }
5635 }
5637 void sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject */*tbl*/)
5638 {
5639 SPEventContext *ec = selection->desktop()->event_context;
5640 if (!SP_IS_LPETOOL_CONTEXT(ec))
5641 return;
5643 lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
5644 }
5646 void
5647 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
5648 {
5649 using namespace Inkscape::LivePathEffect;
5650 SPEventContext *ec = selection->desktop()->event_context;
5651 if (!SP_IS_LPETOOL_CONTEXT(ec))
5652 return;
5653 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
5655 lpetool_delete_measuring_items(lc);
5656 lpetool_create_measuring_items(lc, selection);
5658 // activate line segment combo box if a single item with LPELineSegment is selected
5659 GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
5660 SPItem *item = selection->singleItem();
5661 if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
5662 SPLPEItem *lpeitem = SP_LPE_ITEM(item);
5663 Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
5664 if (lpe && lpe->effectType() == LINE_SEGMENT) {
5665 LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
5666 g_object_set_data(tbl, "currentlpe", lpe);
5667 g_object_set_data(tbl, "currentlpeitem", lpeitem);
5668 gtk_action_set_sensitive(w, TRUE);
5669 ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
5670 } else {
5671 g_object_set_data(tbl, "currentlpe", NULL);
5672 g_object_set_data(tbl, "currentlpeitem", NULL);
5673 gtk_action_set_sensitive(w, FALSE);
5674 }
5675 } else {
5676 g_object_set_data(tbl, "currentlpe", NULL);
5677 g_object_set_data(tbl, "currentlpeitem", NULL);
5678 gtk_action_set_sensitive(w, FALSE);
5679 }
5680 }
5682 static void
5683 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
5684 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5685 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5687 bool show = gtk_toggle_action_get_active( act );
5688 prefs->setBool("/tools/lpetool/show_bbox", show);
5690 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5691 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5692 lpetool_context_reset_limiting_bbox(lc);
5693 }
5694 }
5696 static void
5697 lpetool_toggle_show_measuring_info (GtkToggleAction *act, GObject *tbl) {
5698 SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
5699 if (!tools_isactive(desktop, TOOLS_LPETOOL))
5700 return;
5702 GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
5703 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5704 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5705 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5706 bool show = gtk_toggle_action_get_active( act );
5707 prefs->setBool("/tools/lpetool/show_measuring_info", show);
5708 lpetool_show_measuring_info(lc, show);
5709 gtk_action_set_sensitive(GTK_ACTION(unitact), show);
5710 }
5711 }
5713 static void lpetool_unit_changed(GtkAction* /*act*/, GObject* tbl) {
5714 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
5715 SPUnit const *unit = tracker->getActiveUnit();
5716 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5717 prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5719 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5720 if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
5721 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5722 lpetool_delete_measuring_items(lc);
5723 lpetool_create_measuring_items(lc);
5724 }
5725 }
5727 static void
5728 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
5729 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5730 Inkscape::Selection *selection = desktop->selection;
5732 Geom::OptRect bbox = selection->bounds();
5734 if (bbox) {
5735 Geom::Point A(bbox->min());
5736 Geom::Point B(bbox->max());
5738 A *= desktop->doc2dt();
5739 B *= desktop->doc2dt();
5741 // TODO: should we provide a way to store points in prefs?
5742 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5743 prefs->setDouble("/tools/lpetool/bbox_upperleftx", A[Geom::X]);
5744 prefs->setDouble("/tools/lpetool/bbox_upperlefty", A[Geom::Y]);
5745 prefs->setDouble("/tools/lpetool/bbox_lowerrightx", B[Geom::X]);
5746 prefs->setDouble("/tools/lpetool/bbox_lowerrighty", B[Geom::Y]);
5748 lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
5749 }
5751 gtk_toggle_action_set_active(act, false);
5752 }
5754 static void
5755 sp_line_segment_build_list(GObject *tbl)
5756 {
5757 g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
5759 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
5760 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
5761 gtk_list_store_clear (model);
5763 // TODO: we add the entries of rht combo box manually; later this should be done automatically
5764 {
5765 GtkTreeIter iter;
5766 gtk_list_store_append( model, &iter );
5767 gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
5768 gtk_list_store_append( model, &iter );
5769 gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
5770 gtk_list_store_append( model, &iter );
5771 gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
5772 gtk_list_store_append( model, &iter );
5773 gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
5774 }
5776 g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5777 }
5779 static void
5780 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
5781 using namespace Inkscape::LivePathEffect;
5783 // quit if run by the attr_changed listener
5784 if (g_object_get_data(tbl, "freeze")) {
5785 return;
5786 }
5788 // in turn, prevent listener from responding
5789 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5791 LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
5792 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5793 if (lpeitem) {
5794 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5795 lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
5796 sp_lpe_item_update_patheffect(lpeitem, true, true);
5797 }
5799 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5800 }
5802 static void
5803 lpetool_open_lpe_dialog (GtkToggleAction *act, gpointer data) {
5804 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5806 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5807 sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5808 }
5809 gtk_toggle_action_set_active(act, false);
5810 }
5812 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5813 {
5814 UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5815 tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5816 g_object_set_data(holder, "tracker", tracker);
5817 SPUnit const *unit = tracker->getActiveUnit();
5819 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5820 prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5822 /** Automatically create a list of LPEs that get added to the toolbar **/
5823 {
5824 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5826 GtkTreeIter iter;
5828 // the first toggle button represents the state that no subtool is active (remove this when
5829 // this can be modeled by EgeSelectOneAction or some other action)
5830 gtk_list_store_append( model, &iter );
5831 gtk_list_store_set( model, &iter,
5832 0, _("All inactive"),
5833 1, _("No geometric tool is active"),
5834 2, "draw-geometry-inactive",
5835 -1 );
5837 Inkscape::LivePathEffect::EffectType type;
5838 for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5839 type = lpesubtools[i].type;
5840 gtk_list_store_append( model, &iter );
5841 gtk_list_store_set( model, &iter,
5842 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5843 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5844 2, lpesubtools[i].icon_name,
5845 -1 );
5846 }
5848 EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5849 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5850 g_object_set_data( holder, "lpetool_mode_action", act );
5852 ege_select_one_action_set_appearance( act, "full" );
5853 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5854 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5855 ege_select_one_action_set_icon_column( act, 2 );
5856 ege_select_one_action_set_tooltip_column( act, 1 );
5858 gint lpeToolMode = prefs->getInt("/tools/lpetool/mode", 0);
5859 ege_select_one_action_set_active( act, lpeToolMode );
5860 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5861 }
5863 /* Show limiting bounding box */
5864 {
5865 InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5866 _("Show limiting bounding box"),
5867 _("Show bounding box (used to cut infinite lines)"),
5868 "show-bounding-box",
5869 Inkscape::ICON_SIZE_DECORATION );
5870 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5871 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5872 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_bbox", true ) );
5873 }
5875 /* Set limiting bounding box to bbox of current selection */
5876 {
5877 InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5878 _("Get limiting bounding box from selection"),
5879 _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5880 "draw-geometry-set-bounding-box",
5881 Inkscape::ICON_SIZE_DECORATION );
5882 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5883 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5884 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5885 }
5888 /* Combo box to choose line segment type */
5889 {
5890 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5891 EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5892 ege_select_one_action_set_appearance (act, "compact");
5893 g_object_set_data (holder, "lpetool_line_segment_action", act );
5895 g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5897 sp_line_segment_build_list (holder);
5899 g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5900 gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5901 gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5902 }
5904 /* Display measuring info for selected items */
5905 {
5906 InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5907 _("Display measuring info"),
5908 _("Display measuring info for selected items"),
5909 "draw-geometry-show-measuring-info",
5910 Inkscape::ICON_SIZE_DECORATION );
5911 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5912 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
5913 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_measuring_info", true ) );
5914 }
5916 // add the units menu
5917 {
5918 GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5919 gtk_action_group_add_action( mainActions, act );
5920 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5921 g_object_set_data(holder, "lpetool_units_action", act);
5922 gtk_action_set_sensitive(act, prefs->getBool("/tools/lpetool/show_measuring_info", true));
5923 }
5925 /* Open LPE dialog (to adapt parameters numerically) */
5926 {
5927 InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
5928 _("Open LPE dialog"),
5929 _("Open LPE dialog (to adapt parameters numerically)"),
5930 "dialog-geometry",
5931 Inkscape::ICON_SIZE_DECORATION );
5932 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5933 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
5934 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5935 }
5937 //watch selection
5938 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5940 sigc::connection *c_selection_modified =
5941 new sigc::connection (sp_desktop_selection (desktop)->connectModified
5942 (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
5943 pool->add_connection ("selection-modified", c_selection_modified);
5945 sigc::connection *c_selection_changed =
5946 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5947 (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5948 pool->add_connection ("selection-changed", c_selection_changed);
5949 }
5951 //########################
5952 //## Eraser ##
5953 //########################
5955 static void sp_erc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
5956 {
5957 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5958 prefs->setDouble( "/tools/eraser/width", adj->value );
5959 update_presets_list(tbl);
5960 }
5962 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5963 {
5964 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5965 bool eraserMode = ege_select_one_action_get_active( act ) != 0;
5966 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5967 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5968 prefs->setBool( "/tools/eraser/mode", eraserMode );
5969 }
5971 // only take action if run by the attr_changed listener
5972 if (!g_object_get_data( tbl, "freeze" )) {
5973 // in turn, prevent listener from responding
5974 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5976 if ( eraserMode != 0 ) {
5977 } else {
5978 }
5979 // TODO finish implementation
5981 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5982 }
5983 }
5985 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5986 {
5987 {
5988 /* Width */
5989 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5990 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5991 EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5992 _("Pen Width"), _("Width:"),
5993 _("The width of the eraser pen (relative to the visible canvas area)"),
5994 "/tools/eraser/width", 15,
5995 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5996 1, 100, 1.0, 10.0,
5997 labels, values, G_N_ELEMENTS(labels),
5998 sp_erc_width_value_changed, 1, 0);
5999 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6000 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6001 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
6002 }
6004 {
6005 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
6007 GtkTreeIter iter;
6008 gtk_list_store_append( model, &iter );
6009 gtk_list_store_set( model, &iter,
6010 0, _("Delete"),
6011 1, _("Delete objects touched by the eraser"),
6012 2, INKSCAPE_ICON_DRAW_ERASER_DELETE_OBJECTS,
6013 -1 );
6015 gtk_list_store_append( model, &iter );
6016 gtk_list_store_set( model, &iter,
6017 0, _("Cut"),
6018 1, _("Cut out from objects"),
6019 2, INKSCAPE_ICON_PATH_DIFFERENCE,
6020 -1 );
6022 EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
6023 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
6024 g_object_set_data( holder, "eraser_mode_action", act );
6026 ege_select_one_action_set_appearance( act, "full" );
6027 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
6028 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
6029 ege_select_one_action_set_icon_column( act, 2 );
6030 ege_select_one_action_set_tooltip_column( act, 1 );
6032 /// @todo Convert to boolean?
6033 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6034 gint eraserMode = prefs->getBool("/tools/eraser/mode") ? 1 : 0;
6035 ege_select_one_action_set_active( act, eraserMode );
6036 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
6037 }
6039 }
6041 //########################
6042 //## Text Toolbox ##
6043 //########################
6044 /*
6045 static void
6046 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
6047 {
6048 //Call back for letter sizing spinbutton
6049 }
6051 static void
6052 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
6053 {
6054 //Call back for line height spinbutton
6055 }
6057 static void
6058 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
6059 {
6060 //Call back for horizontal kerning spinbutton
6061 }
6063 static void
6064 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
6065 {
6066 //Call back for vertical kerning spinbutton
6067 }
6069 static void
6070 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
6071 {
6072 //Call back for letter rotation spinbutton
6073 }*/
6075 namespace {
6077 void
6078 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
6079 {
6080 // quit if run by the _changed callbacks
6081 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6082 return;
6083 }
6085 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6087 SPStyle *query =
6088 sp_style_new (SP_ACTIVE_DOCUMENT);
6090 int result_family =
6091 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6093 int result_style =
6094 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6096 int result_numbers =
6097 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6099 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6101 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6102 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
6103 // there are no texts in selection, read from prefs
6105 sp_style_read_from_prefs(query, "/tools/text");
6107 if (g_object_get_data(tbl, "text_style_from_prefs")) {
6108 // do not reset the toolbar style from prefs if we already did it last time
6109 sp_style_unref(query);
6110 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6111 return;
6112 }
6113 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
6114 } else {
6115 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
6116 }
6118 if (query->text)
6119 {
6120 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
6121 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6122 gtk_entry_set_text (GTK_ENTRY (entry), "");
6124 } else if (query->text->font_specification.value || query->text->font_family.value) {
6126 Gtk::ComboBoxEntry *combo = (Gtk::ComboBoxEntry *) (g_object_get_data (G_OBJECT (tbl), "family-entry-combo"));
6127 GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6129 // Get the font that corresponds
6130 Glib::ustring familyName;
6132 font_instance * font = font_factory::Default()->FaceFromStyle(query);
6133 if (font) {
6134 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
6135 font->Unref();
6136 font = NULL;
6137 }
6139 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
6141 Gtk::TreeIter iter;
6142 try {
6143 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
6144 Glib::RefPtr<Gtk::TreeModel> model = combo->get_model();
6145 iter = model->get_iter(path);
6146 } catch (...) {
6147 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
6148 sp_style_unref(query);
6149 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6150 return;
6151 }
6153 combo->set_active (iter);
6154 }
6156 //Size
6157 {
6158 GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
6159 gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
6160 gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
6161 g_free(str);
6162 }
6164 //Anchor
6165 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
6166 {
6167 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
6168 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6169 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6170 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6171 }
6172 else
6173 {
6174 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
6175 {
6176 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
6177 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6178 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6179 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6180 }
6181 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
6182 {
6183 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
6184 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6185 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6186 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6187 }
6188 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
6189 {
6190 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
6191 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6192 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6193 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6194 }
6195 }
6197 //Style
6198 {
6199 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
6201 gboolean active = gtk_toggle_button_get_active (button);
6202 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));
6204 if (active != check)
6205 {
6206 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6207 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6208 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6209 }
6210 }
6212 {
6213 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
6215 gboolean active = gtk_toggle_button_get_active (button);
6216 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
6218 if (active != check)
6219 {
6220 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6221 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6222 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6223 }
6224 }
6226 //Orientation
6227 //locking both buttons, changing one affect all group (both)
6228 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
6229 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6231 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
6232 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
6234 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
6235 {
6236 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6237 }
6238 else
6239 {
6240 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
6241 }
6242 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6243 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
6244 }
6246 sp_style_unref(query);
6248 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6249 }
6251 void
6252 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
6253 {
6254 sp_text_toolbox_selection_changed (selection, tbl);
6255 }
6257 void
6258 sp_text_toolbox_subselection_changed (gpointer /*tc*/, GObject *tbl)
6259 {
6260 sp_text_toolbox_selection_changed (NULL, tbl);
6261 }
6263 void
6264 sp_text_toolbox_family_changed (GtkComboBoxEntry *,
6265 GObject *tbl)
6266 {
6267 // quit if run by the _changed callbacks
6268 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6269 return;
6270 }
6272 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6274 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6275 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6276 const gchar* family = gtk_entry_get_text (GTK_ENTRY (entry));
6278 //g_print ("family changed to: %s\n", family);
6280 SPStyle *query =
6281 sp_style_new (SP_ACTIVE_DOCUMENT);
6283 int result_fontspec =
6284 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6286 SPCSSAttr *css = sp_repr_css_attr_new ();
6288 // First try to get the font spec from the stored value
6289 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
6291 if (fontSpec.empty()) {
6292 // Construct a new font specification if it does not yet exist
6293 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6294 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6295 fontFromStyle->Unref();
6296 }
6298 if (!fontSpec.empty()) {
6300 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
6302 if (!newFontSpec.empty()) {
6304 if (fontSpec != newFontSpec) {
6306 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
6308 if (font) {
6309 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6311 // Set all the these just in case they were altered when finding the best
6312 // match for the new family and old style...
6314 gchar c[256];
6316 font->Family(c, 256);
6318 sp_repr_css_set_property (css, "font-family", c);
6320 font->Attribute( "weight", c, 256);
6321 sp_repr_css_set_property (css, "font-weight", c);
6323 font->Attribute("style", c, 256);
6324 sp_repr_css_set_property (css, "font-style", c);
6326 font->Attribute("stretch", c, 256);
6327 sp_repr_css_set_property (css, "font-stretch", c);
6329 font->Attribute("variant", c, 256);
6330 sp_repr_css_set_property (css, "font-variant", c);
6332 font->Unref();
6333 }
6334 }
6336 } else {
6337 // If the old font on selection (or default) was not existing on the system,
6338 // ReplaceFontSpecificationFamily does not work. In that case we fall back to blindly
6339 // setting the family reported by the family chooser.
6341 //g_print ("fallback setting family: %s\n", family);
6342 sp_repr_css_set_property (css, "-inkscape-font-specification", family);
6343 sp_repr_css_set_property (css, "font-family", family);
6344 }
6345 }
6347 // If querying returned nothing, set the default style of the tool (for new texts)
6348 if (result_fontspec == QUERY_STYLE_NOTHING)
6349 {
6350 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6351 prefs->mergeStyle("/tools/text/style", css);
6352 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
6353 }
6354 else
6355 {
6356 sp_desktop_set_style (desktop, css, true, true);
6357 }
6359 sp_style_unref(query);
6361 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6362 _("Text: Change font family"));
6363 sp_repr_css_attr_unref (css);
6365 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6367 // unfreeze
6368 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6370 // focus to canvas
6371 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6372 }
6375 void
6376 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
6377 gpointer data)
6378 {
6379 if (g_object_get_data (G_OBJECT (button), "block")) return;
6380 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
6381 int prop = GPOINTER_TO_INT(data);
6383 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6385 // move the x of all texts to preserve the same bbox
6386 Inkscape::Selection *selection = sp_desktop_selection(desktop);
6387 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
6388 if (SP_IS_TEXT((SPItem *) items->data)) {
6389 SPItem *item = SP_ITEM(items->data);
6391 unsigned writing_mode = SP_OBJECT_STYLE(item)->writing_mode.value;
6392 // below, variable names suggest horizontal move, but we check the writing direction
6393 // and move in the corresponding axis
6394 int axis;
6395 if (writing_mode == SP_CSS_WRITING_MODE_LR_TB || writing_mode == SP_CSS_WRITING_MODE_RL_TB) {
6396 axis = NR::X;
6397 } else {
6398 axis = NR::Y;
6399 }
6401 Geom::OptRect bbox
6402 = item->getBounds(Geom::identity(), SPItem::GEOMETRIC_BBOX);
6403 if (!bbox)
6404 continue;
6405 double width = bbox->dimensions()[axis];
6406 // If you want to align within some frame, other than the text's own bbox, calculate
6407 // the left and right (or top and bottom for tb text) slacks of the text inside that
6408 // frame (currently unused)
6409 double left_slack = 0;
6410 double right_slack = 0;
6411 unsigned old_align = SP_OBJECT_STYLE(item)->text_align.value;
6412 double move = 0;
6413 if (old_align == SP_CSS_TEXT_ALIGN_START || old_align == SP_CSS_TEXT_ALIGN_LEFT) {
6414 switch (prop) {
6415 case 0:
6416 move = -left_slack;
6417 break;
6418 case 1:
6419 move = width/2 + (right_slack - left_slack)/2;
6420 break;
6421 case 2:
6422 move = width + right_slack;
6423 break;
6424 }
6425 } else if (old_align == SP_CSS_TEXT_ALIGN_CENTER) {
6426 switch (prop) {
6427 case 0:
6428 move = -width/2 - left_slack;
6429 break;
6430 case 1:
6431 move = (right_slack - left_slack)/2;
6432 break;
6433 case 2:
6434 move = width/2 + right_slack;
6435 break;
6436 }
6437 } else if (old_align == SP_CSS_TEXT_ALIGN_END || old_align == SP_CSS_TEXT_ALIGN_RIGHT) {
6438 switch (prop) {
6439 case 0:
6440 move = -width - left_slack;
6441 break;
6442 case 1:
6443 move = -width/2 + (right_slack - left_slack)/2;
6444 break;
6445 case 2:
6446 move = right_slack;
6447 break;
6448 }
6449 }
6450 Geom::Point XY = SP_TEXT(item)->attributes.firstXY();
6451 if (axis == NR::X) {
6452 XY = XY + Geom::Point (move, 0);
6453 } else {
6454 XY = XY + Geom::Point (0, move);
6455 }
6456 SP_TEXT(item)->attributes.setFirstXY(XY);
6457 SP_OBJECT(item)->updateRepr();
6458 SP_OBJECT(item)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
6459 }
6460 }
6462 SPCSSAttr *css = sp_repr_css_attr_new ();
6463 switch (prop)
6464 {
6465 case 0:
6466 {
6467 sp_repr_css_set_property (css, "text-anchor", "start");
6468 sp_repr_css_set_property (css, "text-align", "start");
6469 break;
6470 }
6471 case 1:
6472 {
6473 sp_repr_css_set_property (css, "text-anchor", "middle");
6474 sp_repr_css_set_property (css, "text-align", "center");
6475 break;
6476 }
6478 case 2:
6479 {
6480 sp_repr_css_set_property (css, "text-anchor", "end");
6481 sp_repr_css_set_property (css, "text-align", "end");
6482 break;
6483 }
6485 case 3:
6486 {
6487 sp_repr_css_set_property (css, "text-anchor", "start");
6488 sp_repr_css_set_property (css, "text-align", "justify");
6489 break;
6490 }
6491 }
6493 SPStyle *query =
6494 sp_style_new (SP_ACTIVE_DOCUMENT);
6495 int result_numbers =
6496 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6498 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6499 if (result_numbers == QUERY_STYLE_NOTHING)
6500 {
6501 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6502 prefs->mergeStyle("/tools/text/style", css);
6503 }
6505 sp_style_unref(query);
6507 sp_desktop_set_style (desktop, css, true, true);
6508 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6509 _("Text: Change alignment"));
6510 sp_repr_css_attr_unref (css);
6512 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6513 }
6515 void
6516 sp_text_toolbox_style_toggled (GtkToggleButton *button,
6517 gpointer data)
6518 {
6519 if (g_object_get_data (G_OBJECT (button), "block")) return;
6521 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6522 SPCSSAttr *css = sp_repr_css_attr_new ();
6523 int prop = GPOINTER_TO_INT(data);
6524 bool active = gtk_toggle_button_get_active (button);
6526 SPStyle *query =
6527 sp_style_new (SP_ACTIVE_DOCUMENT);
6529 int result_fontspec =
6530 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6532 //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6533 //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6534 //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6536 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
6537 Glib::ustring newFontSpec = "";
6539 if (fontSpec.empty()) {
6540 // Construct a new font specification if it does not yet exist
6541 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6542 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6543 fontFromStyle->Unref();
6544 }
6546 bool nochange = true;
6547 switch (prop)
6548 {
6549 case 0:
6550 {
6551 if (!fontSpec.empty()) {
6552 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
6553 if (!newFontSpec.empty()) {
6554 // Don't even set the bold if the font didn't exist on the system
6555 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
6556 nochange = false;
6557 }
6558 }
6559 // set or reset the button according
6560 if(nochange) {
6561 gboolean check = gtk_toggle_button_get_active (button);
6563 if (active != check)
6564 {
6565 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6566 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6567 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6568 }
6569 }
6571 break;
6572 }
6574 case 1:
6575 {
6576 if (!fontSpec.empty()) {
6577 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
6578 if (!newFontSpec.empty()) {
6579 // Don't even set the italic if the font didn't exist on the system
6580 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
6581 nochange = false;
6582 }
6583 }
6584 if(nochange) {
6585 gboolean check = gtk_toggle_button_get_active (button);
6587 if (active != check)
6588 {
6589 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6590 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6591 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6592 }
6593 }
6594 break;
6595 }
6596 }
6598 if (!newFontSpec.empty()) {
6599 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6600 }
6602 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6603 if (result_fontspec == QUERY_STYLE_NOTHING)
6604 {
6605 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6606 prefs->mergeStyle("/tools/text/style", css);
6607 }
6609 sp_style_unref(query);
6611 sp_desktop_set_style (desktop, css, true, true);
6612 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6613 _("Text: Change font style"));
6614 sp_repr_css_attr_unref (css);
6616 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6617 }
6619 void
6620 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
6621 gpointer data)
6622 {
6623 if (g_object_get_data (G_OBJECT (button), "block")) {
6624 return;
6625 }
6627 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6628 SPCSSAttr *css = sp_repr_css_attr_new ();
6629 int prop = GPOINTER_TO_INT(data);
6631 switch (prop)
6632 {
6633 case 0:
6634 {
6635 sp_repr_css_set_property (css, "writing-mode", "lr");
6636 break;
6637 }
6639 case 1:
6640 {
6641 sp_repr_css_set_property (css, "writing-mode", "tb");
6642 break;
6643 }
6644 }
6646 SPStyle *query =
6647 sp_style_new (SP_ACTIVE_DOCUMENT);
6648 int result_numbers =
6649 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6651 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6652 if (result_numbers == QUERY_STYLE_NOTHING)
6653 {
6654 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6655 prefs->mergeStyle("/tools/text/style", css);
6656 }
6658 sp_desktop_set_style (desktop, css, true, true);
6659 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6660 _("Text: Change orientation"));
6661 sp_repr_css_attr_unref (css);
6663 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6664 }
6666 gboolean
6667 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6668 {
6669 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6670 if (!desktop) return FALSE;
6672 switch (get_group0_keyval (event)) {
6673 case GDK_KP_Enter: // chosen
6674 case GDK_Return:
6675 // unfreeze and update, which will defocus
6676 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6677 sp_text_toolbox_family_changed (NULL, tbl);
6678 return TRUE; // I consumed the event
6679 break;
6680 case GDK_Escape:
6681 // defocus
6682 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6683 return TRUE; // I consumed the event
6684 break;
6685 }
6686 return FALSE;
6687 }
6689 gboolean
6690 sp_text_toolbox_family_list_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject */*tbl*/)
6691 {
6692 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6693 if (!desktop) return FALSE;
6695 switch (get_group0_keyval (event)) {
6696 case GDK_KP_Enter:
6697 case GDK_Return:
6698 case GDK_Escape: // defocus
6699 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6700 return TRUE; // I consumed the event
6701 break;
6702 case GDK_w:
6703 case GDK_W:
6704 if (event->state & GDK_CONTROL_MASK) {
6705 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6706 return TRUE; // I consumed the event
6707 }
6708 break;
6709 }
6710 return FALSE;
6711 }
6714 void
6715 sp_text_toolbox_size_changed (GtkComboBox *cbox,
6716 GObject *tbl)
6717 {
6718 // quit if run by the _changed callbacks
6719 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6720 return;
6721 }
6723 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6725 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6727 // If this is not from selecting a size in the list (in which case get_active will give the
6728 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
6729 // process this event. This fixes GTK's stupid insistence on sending an activate change every
6730 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
6731 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed")) {
6732 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6733 return;
6734 }
6736 gdouble value = -1;
6737 {
6738 gchar *endptr;
6739 gchar *const text = gtk_combo_box_get_active_text(cbox);
6740 if (text) {
6741 value = g_strtod(text, &endptr);
6742 if (endptr == text) { // Conversion failed, non-numeric input.
6743 value = -1;
6744 }
6745 g_free(text);
6746 }
6747 }
6748 if (value <= 0) {
6749 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6750 return; // could not parse value
6751 }
6753 SPCSSAttr *css = sp_repr_css_attr_new ();
6754 Inkscape::CSSOStringStream osfs;
6755 osfs << value;
6756 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
6758 SPStyle *query =
6759 sp_style_new (SP_ACTIVE_DOCUMENT);
6760 int result_numbers =
6761 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6763 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6764 if (result_numbers == QUERY_STYLE_NOTHING)
6765 {
6766 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6767 prefs->mergeStyle("/tools/text/style", css);
6768 }
6770 sp_style_unref(query);
6772 sp_desktop_set_style (desktop, css, true, true);
6773 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
6774 _("Text: Change font size"));
6775 sp_repr_css_attr_unref (css);
6777 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6779 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6780 }
6782 gboolean
6783 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
6784 {
6785 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6786 if (!desktop) return FALSE;
6788 if (!g_object_get_data (tbl, "esc-pressed")) {
6789 g_object_set_data (tbl, "enter-pressed", gpointer(1));
6790 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6791 sp_text_toolbox_size_changed (cbox, tbl);
6792 g_object_set_data (tbl, "enter-pressed", gpointer(0));
6793 }
6794 return FALSE; // I consumed the event
6795 }
6798 gboolean
6799 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6800 {
6801 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6802 if (!desktop) return FALSE;
6804 switch (get_group0_keyval (event)) {
6805 case GDK_Escape: // defocus
6806 g_object_set_data (tbl, "esc-pressed", gpointer(1));
6807 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6808 g_object_set_data (tbl, "esc-pressed", gpointer(0));
6809 return TRUE; // I consumed the event
6810 break;
6811 case GDK_Return: // defocus
6812 case GDK_KP_Enter:
6813 g_object_set_data (tbl, "enter-pressed", gpointer(1));
6814 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6815 sp_text_toolbox_size_changed (cbox, tbl);
6816 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6817 g_object_set_data (tbl, "enter-pressed", gpointer(0));
6818 return TRUE; // I consumed the event
6819 break;
6820 }
6821 return FALSE;
6822 }
6824 // While editing font name in the entry, disable family_changed by freezing, otherwise completion
6825 // does not work!
6826 gboolean
6827 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
6828 GdkEventFocus */*event*/,
6829 GObject *tbl)
6830 {
6831 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6832 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1); // select all
6833 return FALSE;
6834 }
6836 gboolean
6837 sp_text_toolbox_entry_focus_out (GtkWidget *entry,
6838 GdkEventFocus */*event*/,
6839 GObject *tbl)
6840 {
6841 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6842 gtk_entry_select_region (GTK_ENTRY (entry), 0, 0); // deselect
6843 return FALSE;
6844 }
6846 void
6847 cell_data_func (GtkCellLayout */*cell_layout*/,
6848 GtkCellRenderer *cell,
6849 GtkTreeModel *tree_model,
6850 GtkTreeIter *iter,
6851 gpointer /*data*/)
6852 {
6853 gchar *family;
6854 gtk_tree_model_get(tree_model, iter, 0, &family, -1);
6855 gchar *const family_escaped = g_markup_escape_text(family, -1);
6857 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6858 int show_sample = prefs->getInt("/tools/text/show_sample_in_list", 1);
6859 if (show_sample) {
6861 Glib::ustring sample = prefs->getString("/tools/text/font_sample");
6862 gchar *const sample_escaped = g_markup_escape_text(sample.data(), -1);
6864 std::stringstream markup;
6865 markup << family_escaped << " <span foreground='darkgray' font_family='"
6866 << family_escaped << "'>" << sample_escaped << "</span>";
6867 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6869 g_free(sample_escaped);
6870 } else {
6871 g_object_set (G_OBJECT (cell), "markup", family_escaped, NULL);
6872 }
6874 g_free(family);
6875 g_free(family_escaped);
6876 }
6878 gboolean text_toolbox_completion_match_selected(GtkEntryCompletion */*widget*/,
6879 GtkTreeModel *model,
6880 GtkTreeIter *iter,
6881 GObject *tbl)
6882 {
6883 // We intercept this signal so as to fire family_changed at once (without it, you'd have to
6884 // press Enter again after choosing a completion)
6885 gchar *family = 0;
6886 gtk_tree_model_get(model, iter, 0, &family, -1);
6888 GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6889 gtk_entry_set_text (GTK_ENTRY (entry), family);
6891 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6892 sp_text_toolbox_family_changed (NULL, tbl);
6893 return TRUE;
6894 }
6897 static void
6898 cbe_add_completion (GtkComboBoxEntry *cbe, GObject *tbl){
6899 GtkEntry *entry;
6900 GtkEntryCompletion *completion;
6901 GtkTreeModel *model;
6903 entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(cbe)));
6904 completion = gtk_entry_completion_new();
6905 model = gtk_combo_box_get_model(GTK_COMBO_BOX(cbe));
6906 gtk_entry_completion_set_model(completion, model);
6907 gtk_entry_completion_set_text_column(completion, 0);
6908 gtk_entry_completion_set_inline_completion(completion, FALSE);
6909 gtk_entry_completion_set_inline_selection(completion, FALSE);
6910 gtk_entry_completion_set_popup_completion(completion, TRUE);
6911 gtk_entry_set_completion(entry, completion);
6913 g_signal_connect (G_OBJECT (completion), "match-selected", G_CALLBACK (text_toolbox_completion_match_selected), tbl);
6915 g_object_unref(completion);
6916 }
6918 void sp_text_toolbox_family_popnotify(GtkComboBox *widget,
6919 void */*property*/,
6920 GObject *tbl)
6921 {
6922 // while the drop-down is open, we disable font family changing, reenabling it only when it closes
6924 gboolean shown;
6925 g_object_get (G_OBJECT(widget), "popup-shown", &shown, NULL);
6926 if (shown) {
6927 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6928 //g_print("POP: notify: SHOWN\n");
6929 } else {
6930 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6932 // stupid GTK doesn't let us attach to events in the drop-down window, so we peek here to
6933 // find out if the drop down was closed by Enter and if so, manually update (only
6934 // necessary on Windows, on Linux it updates itself - what a mess, but we'll manage)
6935 GdkEvent *ev = gtk_get_current_event();
6936 if (ev) {
6937 //g_print ("ev type: %d\n", ev->type);
6938 if (ev->type == GDK_KEY_PRESS) {
6939 switch (get_group0_keyval ((GdkEventKey *) ev)) {
6940 case GDK_KP_Enter: // chosen
6941 case GDK_Return:
6942 {
6943 // make sure the chosen one is inserted into the entry
6944 GtkComboBox *combo = GTK_COMBO_BOX (((Gtk::ComboBox *) (g_object_get_data (tbl, "family-entry-combo")))->gobj());
6945 GtkTreeModel *model = gtk_combo_box_get_model(combo);
6946 GtkTreeIter iter;
6947 gboolean has_active = gtk_combo_box_get_active_iter (combo, &iter);
6948 if (has_active) {
6949 gchar *family;
6950 gtk_tree_model_get(model, &iter, 0, &family, -1);
6951 GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6952 gtk_entry_set_text (GTK_ENTRY (entry), family);
6953 }
6955 // update
6956 sp_text_toolbox_family_changed (NULL, tbl);
6957 break;
6958 }
6959 }
6960 }
6961 }
6963 // regardless of whether we updated, defocus the widget
6964 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6965 if (desktop)
6966 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6967 //g_print("POP: notify: HIDDEN\n");
6968 }
6969 }
6971 GtkWidget *sp_text_toolbox_new (SPDesktop *desktop)
6972 {
6973 GtkToolbar *tbl = GTK_TOOLBAR(gtk_toolbar_new());
6974 GtkIconSize secondarySize = static_cast<GtkIconSize>(ToolboxFactory::prefToSize("/toolbox/secondary", 1));
6976 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
6977 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
6979 GtkTooltips *tt = gtk_tooltips_new();
6981 ////////////Family
6982 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
6983 Gtk::ComboBoxEntry *font_sel = Gtk::manage(new Gtk::ComboBoxEntry(store));
6985 gtk_rc_parse_string (
6986 "style \"dropdown-as-list-style\"\n"
6987 "{\n"
6988 " GtkComboBox::appears-as-list = 1\n"
6989 "}\n"
6990 "widget \"*.toolbox-fontfamily-list\" style \"dropdown-as-list-style\"");
6991 gtk_widget_set_name(GTK_WIDGET (font_sel->gobj()), "toolbox-fontfamily-list");
6992 gtk_tooltips_set_tip (tt, GTK_WIDGET (font_sel->gobj()), _("Select font family (Alt+X to access)"), "");
6994 g_signal_connect (G_OBJECT (font_sel->gobj()), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
6996 cbe_add_completion(font_sel->gobj(), G_OBJECT(tbl));
6998 gtk_toolbar_append_widget( tbl, (GtkWidget*) font_sel->gobj(), "", "");
6999 g_object_set_data (G_OBJECT (tbl), "family-entry-combo", font_sel);
7001 // expand the field a bit so as to view more of the previews in the drop-down
7002 GtkRequisition req;
7003 gtk_widget_size_request (GTK_WIDGET (font_sel->gobj()), &req);
7004 gtk_widget_set_size_request (GTK_WIDGET (font_sel->gobj()), MIN(req.width + 50, 500), -1);
7006 GtkWidget* entry = (GtkWidget*) font_sel->get_entry()->gobj();
7007 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
7009 g_signal_connect (G_OBJECT (font_sel->gobj()), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
7010 g_signal_connect (G_OBJECT (font_sel->gobj()), "notify::popup-shown",
7011 G_CALLBACK (sp_text_toolbox_family_popnotify), tbl);
7012 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
7013 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
7014 g_signal_connect (G_OBJECT (entry), "focus-out-event", G_CALLBACK (sp_text_toolbox_entry_focus_out), tbl);
7016 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
7017 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
7019 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
7020 gtk_cell_layout_clear( GTK_CELL_LAYOUT(font_sel->gobj()) );
7021 gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(font_sel->gobj()) , cell , TRUE );
7022 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT(font_sel->gobj()), cell, GtkCellLayoutDataFunc (cell_data_func), NULL, NULL);
7024 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
7025 GtkWidget *box = gtk_event_box_new ();
7026 gtk_container_add (GTK_CONTAINER (box), image);
7027 gtk_toolbar_append_widget( tbl, box, "", "");
7028 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
7029 gtk_tooltips_set_tip (tt, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
7030 gtk_widget_hide (GTK_WIDGET (box));
7031 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
7033 ////////////Size
7034 gchar const *const sizes[] = {
7035 "4", "6", "8", "9", "10", "11", "12", "13", "14",
7036 "16", "18", "20", "22", "24", "28",
7037 "32", "36", "40", "48", "56", "64", "72", "144"
7038 };
7040 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
7041 for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
7042 gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
7043 }
7044 gtk_widget_set_size_request (cbox, 80, -1);
7045 gtk_toolbar_append_widget( tbl, cbox, "", "");
7046 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
7047 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
7048 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
7049 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
7051 ////////////Text anchor
7052 GtkWidget *group = gtk_radio_button_new (NULL);
7053 GtkWidget *row = gtk_hbox_new (FALSE, 4);
7054 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
7056 // left
7057 GtkWidget *rbutton = group;
7058 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7059 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
7060 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7062 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7063 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
7064 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
7065 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
7067 // center
7068 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7069 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7070 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
7071 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7073 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7074 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
7075 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
7076 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
7078 // right
7079 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7080 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7081 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
7082 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7084 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7085 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
7086 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
7087 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
7089 // fill
7090 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7091 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7092 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
7093 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7095 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7096 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
7097 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
7098 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
7100 gtk_toolbar_append_widget( tbl, row, "", "");
7102 //spacer
7103 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
7105 ////////////Text style
7106 row = gtk_hbox_new (FALSE, 4);
7108 // bold
7109 rbutton = gtk_toggle_button_new ();
7110 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7111 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
7112 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7113 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
7115 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7116 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
7117 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
7119 // italic
7120 rbutton = gtk_toggle_button_new ();
7121 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7122 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
7123 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7124 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
7126 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7127 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
7128 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
7130 gtk_toolbar_append_widget( tbl, row, "", "");
7132 //spacer
7133 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
7135 // Text orientation
7136 group = gtk_radio_button_new (NULL);
7137 row = gtk_hbox_new (FALSE, 4);
7138 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
7140 // horizontal
7141 rbutton = group;
7142 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7143 gtk_container_add (GTK_CONTAINER (rbutton),
7144 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_HORIZONTAL));
7145 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7146 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
7148 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7149 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
7150 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
7152 // vertical
7153 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7154 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7155 gtk_container_add (GTK_CONTAINER (rbutton),
7156 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_VERTICAL));
7157 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7158 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
7160 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7161 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
7162 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
7163 gtk_toolbar_append_widget( tbl, row, "", "" );
7166 //watch selection
7167 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
7169 sigc::connection *c_selection_changed =
7170 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
7171 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
7172 pool->add_connection ("selection-changed", c_selection_changed);
7174 sigc::connection *c_selection_modified =
7175 new sigc::connection (sp_desktop_selection (desktop)->connectModified
7176 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
7177 pool->add_connection ("selection-modified", c_selection_modified);
7179 sigc::connection *c_subselection_changed =
7180 new sigc::connection (desktop->connectToolSubselectionChanged
7181 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
7182 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
7184 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
7187 gtk_widget_show_all( GTK_WIDGET(tbl) );
7189 return GTK_WIDGET(tbl);
7190 } // end of sp_text_toolbox_new()
7192 }//<unnamed> namespace
7195 //#########################
7196 //## Connector ##
7197 //#########################
7199 static void sp_connector_mode_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7200 {
7201 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7202 prefs->setBool("/tools/connector/mode",
7203 gtk_toggle_action_get_active( act ));
7204 }
7206 static void sp_connector_path_set_avoid(void)
7207 {
7208 cc_selection_set_avoid(true);
7209 }
7212 static void sp_connector_path_set_ignore(void)
7213 {
7214 cc_selection_set_avoid(false);
7215 }
7217 static void sp_connector_orthogonal_toggled( GtkToggleAction* act, GObject *tbl )
7218 {
7219 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7220 Inkscape::Selection * selection = sp_desktop_selection(desktop);
7221 SPDocument *doc = sp_desktop_document(desktop);
7223 if (!sp_document_get_undo_sensitive(doc))
7224 {
7225 return;
7226 }
7229 // quit if run by the _changed callbacks
7230 if (g_object_get_data( tbl, "freeze" )) {
7231 return;
7232 }
7234 // in turn, prevent callbacks from responding
7235 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7237 bool is_orthog = gtk_toggle_action_get_active( act );
7238 gchar orthog_str[] = "orthogonal";
7239 gchar polyline_str[] = "polyline";
7240 gchar *value = is_orthog ? orthog_str : polyline_str ;
7242 bool modmade = false;
7243 GSList *l = (GSList *) selection->itemList();
7244 while (l) {
7245 SPItem *item = (SPItem *) l->data;
7247 if (cc_item_is_connector(item)) {
7248 sp_object_setAttribute(item, "inkscape:connector-type",
7249 value, false);
7250 item->avoidRef->handleSettingChange();
7251 modmade = true;
7252 }
7253 l = l->next;
7254 }
7256 if (!modmade)
7257 {
7258 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7259 prefs->setBool("/tools/connector/orthogonal", is_orthog);
7260 }
7262 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7263 is_orthog ? _("Set connector type: orthogonal"): _("Set connector type: polyline"));
7265 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7266 }
7268 static void connector_curvature_changed(GtkAdjustment *adj, GObject* tbl)
7269 {
7270 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7271 Inkscape::Selection * selection = sp_desktop_selection(desktop);
7272 SPDocument *doc = sp_desktop_document(desktop);
7274 if (!sp_document_get_undo_sensitive(doc))
7275 {
7276 return;
7277 }
7280 // quit if run by the _changed callbacks
7281 if (g_object_get_data( tbl, "freeze" )) {
7282 return;
7283 }
7285 // in turn, prevent callbacks from responding
7286 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7288 gdouble newValue = gtk_adjustment_get_value(adj);
7289 gchar value[G_ASCII_DTOSTR_BUF_SIZE];
7290 g_ascii_dtostr(value, G_ASCII_DTOSTR_BUF_SIZE, newValue);
7292 bool modmade = false;
7293 GSList *l = (GSList *) selection->itemList();
7294 while (l) {
7295 SPItem *item = (SPItem *) l->data;
7297 if (cc_item_is_connector(item)) {
7298 sp_object_setAttribute(item, "inkscape:connector-curvature",
7299 value, false);
7300 item->avoidRef->handleSettingChange();
7301 modmade = true;
7302 }
7303 l = l->next;
7304 }
7306 if (!modmade)
7307 {
7308 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7309 prefs->setDouble(Glib::ustring("/tools/connector/curvature"), newValue);
7310 }
7312 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7313 _("Change connector curvature"));
7315 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7316 }
7319 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
7320 {
7321 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7322 SPDocument *doc = sp_desktop_document(desktop);
7324 if (!sp_document_get_undo_sensitive(doc))
7325 {
7326 return;
7327 }
7329 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7331 if ( !repr->attribute("inkscape:connector-spacing") &&
7332 ( adj->value == defaultConnSpacing )) {
7333 // Don't need to update the repr if the attribute doesn't
7334 // exist and it is being set to the default value -- as will
7335 // happen at startup.
7336 return;
7337 }
7339 // quit if run by the attr_changed listener
7340 if (g_object_get_data( tbl, "freeze" )) {
7341 return;
7342 }
7344 // in turn, prevent listener from responding
7345 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
7347 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
7348 SP_OBJECT(desktop->namedview)->updateRepr();
7350 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
7351 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
7352 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
7353 Geom::Matrix m = Geom::identity();
7354 avoid_item_move(&m, item);
7355 }
7357 if (items) {
7358 g_slist_free(items);
7359 }
7361 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7362 _("Change connector spacing"));
7364 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7365 }
7367 static void sp_connector_graph_layout(void)
7368 {
7369 if (!SP_ACTIVE_DESKTOP) return;
7370 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7372 // hack for clones, see comment in align-and-distribute.cpp
7373 int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7374 prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7376 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
7378 prefs->setInt("/options/clonecompensation/value", saved_compensation);
7380 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
7381 }
7383 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7384 {
7385 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7386 prefs->setBool("/tools/connector/directedlayout",
7387 gtk_toggle_action_get_active( act ));
7388 }
7390 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7391 {
7392 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7393 prefs->setBool("/tools/connector/avoidoverlaplayout",
7394 gtk_toggle_action_get_active( act ));
7395 }
7398 static void connector_length_changed(GtkAdjustment *adj, GObject* /*tbl*/)
7399 {
7400 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7401 prefs->setDouble("/tools/connector/length", adj->value);
7402 }
7404 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
7405 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
7406 bool /*is_interactive*/, gpointer data)
7407 {
7408 GtkWidget *tbl = GTK_WIDGET(data);
7410 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
7411 return;
7412 }
7413 if (strcmp(name, "inkscape:connector-spacing") == 0)
7414 {
7415 GtkAdjustment *adj = (GtkAdjustment*)
7416 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
7417 gdouble spacing = defaultConnSpacing;
7418 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
7420 gtk_adjustment_set_value(adj, spacing);
7421 gtk_adjustment_value_changed(adj);
7422 }
7424 spinbutton_defocus(GTK_OBJECT(tbl));
7425 }
7427 static void sp_connector_new_connection_point(GtkWidget *, GObject *tbl)
7428 {
7429 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7430 SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7432 if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE)
7433 cc_create_connection_point(cc);
7434 }
7436 static void sp_connector_remove_connection_point(GtkWidget *, GObject *tbl)
7437 {
7438 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7439 SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7441 if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE)
7442 cc_remove_connection_point(cc);
7443 }
7445 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
7446 NULL, /* child_added */
7447 NULL, /* child_removed */
7448 connector_tb_event_attr_changed,
7449 NULL, /* content_changed */
7450 NULL /* order_changed */
7451 };
7453 static void sp_connector_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
7454 {
7455 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "curvature" ) );
7456 GtkToggleAction *act = GTK_TOGGLE_ACTION( g_object_get_data( tbl, "orthogonal" ) );
7457 SPItem *item = selection->singleItem();
7458 if (SP_IS_PATH(item))
7459 {
7460 gdouble curvature = SP_PATH(item)->connEndPair.getCurvature();
7461 bool is_orthog = SP_PATH(item)->connEndPair.isOrthogonal();
7462 gtk_toggle_action_set_active(act, is_orthog);
7463 gtk_adjustment_set_value(adj, curvature);
7464 }
7466 }
7468 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
7469 {
7470 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7471 Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
7473 // Editing mode toggle button
7474 {
7475 InkToggleAction* act = ink_toggle_action_new( "ConnectorEditModeAction",
7476 _("EditMode"),
7477 _("Switch between connection point editing and connector drawing mode"),
7478 INKSCAPE_ICON_CONNECTOR_EDIT,
7479 Inkscape::ICON_SIZE_DECORATION );
7480 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7482 bool tbuttonstate = prefs->getBool("/tools/connector/mode");
7483 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7484 g_object_set_data( holder, "mode", act );
7485 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_mode_toggled), holder );
7486 }
7489 {
7490 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
7491 _("Avoid"),
7492 _("Make connectors avoid selected objects"),
7493 INKSCAPE_ICON_CONNECTOR_AVOID,
7494 secondarySize );
7495 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
7496 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7497 }
7499 {
7500 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
7501 _("Ignore"),
7502 _("Make connectors ignore selected objects"),
7503 INKSCAPE_ICON_CONNECTOR_IGNORE,
7504 secondarySize );
7505 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
7506 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7507 }
7509 // Orthogonal connectors toggle button
7510 {
7511 InkToggleAction* act = ink_toggle_action_new( "ConnectorOrthogonalAction",
7512 _("Orthogonal"),
7513 _("Make connector orthogonal or polyline"),
7514 INKSCAPE_ICON_CONNECTOR_ORTHOGONAL,
7515 Inkscape::ICON_SIZE_DECORATION );
7516 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7518 bool tbuttonstate = prefs->getBool("/tools/connector/orthogonal");
7519 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7520 g_object_set_data( holder, "orthogonal", act );
7521 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_orthogonal_toggled), holder );
7522 }
7524 EgeAdjustmentAction* eact = 0;
7525 // Curvature spinbox
7526 eact = create_adjustment_action( "ConnectorCurvatureAction",
7527 _("Connector Curvature"), _("Curvature:"),
7528 _("The amount of connectors curvature"),
7529 "/tools/connector/curvature", defaultConnCurvature,
7530 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-curvature",
7531 0, 100, 1.0, 10.0,
7532 0, 0, 0,
7533 connector_curvature_changed, 1, 0 );
7534 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7536 // Spacing spinbox
7537 eact = create_adjustment_action( "ConnectorSpacingAction",
7538 _("Connector Spacing"), _("Spacing:"),
7539 _("The amount of space left around objects by auto-routing connectors"),
7540 "/tools/connector/spacing", defaultConnSpacing,
7541 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
7542 0, 100, 1.0, 10.0,
7543 0, 0, 0,
7544 connector_spacing_changed, 1, 0 );
7545 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7547 // Graph (connector network) layout
7548 {
7549 InkAction* inky = ink_action_new( "ConnectorGraphAction",
7550 _("Graph"),
7551 _("Nicely arrange selected connector network"),
7552 INKSCAPE_ICON_DISTRIBUTE_GRAPH,
7553 secondarySize );
7554 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
7555 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7556 }
7558 // Default connector length spinbox
7559 eact = create_adjustment_action( "ConnectorLengthAction",
7560 _("Connector Length"), _("Length:"),
7561 _("Ideal length for connectors when layout is applied"),
7562 "/tools/connector/length", 100,
7563 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
7564 10, 1000, 10.0, 100.0,
7565 0, 0, 0,
7566 connector_length_changed, 1, 0 );
7567 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7570 // Directed edges toggle button
7571 {
7572 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
7573 _("Downwards"),
7574 _("Make connectors with end-markers (arrows) point downwards"),
7575 INKSCAPE_ICON_DISTRIBUTE_GRAPH_DIRECTED,
7576 Inkscape::ICON_SIZE_DECORATION );
7577 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7579 bool tbuttonstate = prefs->getBool("/tools/connector/directedlayout");
7580 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7582 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
7583 sigc::connection *connection = new sigc::connection(sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_connector_toolbox_selection_changed), (GObject *)holder))
7584 );
7585 }
7587 // Avoid overlaps toggle button
7588 {
7589 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
7590 _("Remove overlaps"),
7591 _("Do not allow overlapping shapes"),
7592 INKSCAPE_ICON_DISTRIBUTE_REMOVE_OVERLAPS,
7593 Inkscape::ICON_SIZE_DECORATION );
7594 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7596 bool tbuttonstate = prefs->getBool("/tools/connector/avoidoverlaplayout");
7597 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), (tbuttonstate ? TRUE : FALSE ));
7599 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
7600 }
7603 // New connection point button
7604 {
7605 InkAction* inky = ink_action_new( "ConnectorNewConnPointAction",
7606 _("New connection point"),
7607 _("Add a new connection point to the currently selected item"),
7608 INKSCAPE_ICON_CONNECTOR_NEW_CONNPOINT,
7609 secondarySize );
7610 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_new_connection_point), holder );
7611 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7612 }
7614 // Remove selected connection point button
7616 {
7617 InkAction* inky = ink_action_new( "ConnectorRemoveConnPointAction",
7618 _("Remove connection point"),
7619 _("Remove the currently selected connection point"),
7620 INKSCAPE_ICON_CONNECTOR_REMOVE_CONNPOINT,
7621 secondarySize );
7622 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_remove_connection_point), holder );
7623 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7624 }
7627 // Code to watch for changes to the connector-spacing attribute in
7628 // the XML.
7629 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7630 g_assert(repr != NULL);
7632 purge_repr_listener( holder, holder );
7634 if (repr) {
7635 g_object_set_data( holder, "repr", repr );
7636 Inkscape::GC::anchor(repr);
7637 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
7638 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
7639 }
7640 } // end of sp_connector_toolbox_prep()
7643 //#########################
7644 //## Paintbucket ##
7645 //#########################
7647 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
7648 {
7649 gint channels = ege_select_one_action_get_active( act );
7650 flood_channels_set_channels( channels );
7651 }
7653 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
7654 {
7655 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7656 prefs->setInt("/tools/paintbucket/threshold", (gint)adj->value);
7657 }
7659 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
7660 {
7661 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7662 prefs->setBool("/tools/paintbucket/autogap", ege_select_one_action_get_active( act ));
7663 }
7665 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
7666 {
7667 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
7668 SPUnit const *unit = tracker->getActiveUnit();
7669 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7671 prefs->setDouble("/tools/paintbucket/offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
7672 prefs->setString("/tools/paintbucket/offsetunits", sp_unit_get_abbreviation(unit));
7673 }
7675 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
7676 {
7677 // FIXME: make defaults settable via Inkscape Options
7678 struct KeyValue {
7679 char const *key;
7680 double value;
7681 } const key_values[] = {
7682 {"threshold", 15},
7683 {"offset", 0.0}
7684 };
7686 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
7687 KeyValue const &kv = key_values[i];
7688 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
7689 if ( adj ) {
7690 gtk_adjustment_set_value(adj, kv.value);
7691 }
7692 }
7694 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
7695 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
7696 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
7697 ege_select_one_action_set_active( autogap_action, 0 );
7698 }
7700 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
7701 {
7702 EgeAdjustmentAction* eact = 0;
7703 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7705 {
7706 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7708 GList* items = 0;
7709 gint count = 0;
7710 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
7711 {
7712 GtkTreeIter iter;
7713 gtk_list_store_append( model, &iter );
7714 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7715 count++;
7716 }
7717 g_list_free( items );
7718 items = 0;
7719 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
7720 g_object_set( act1, "short_label", _("Fill by:"), NULL );
7721 ege_select_one_action_set_appearance( act1, "compact" );
7722 ege_select_one_action_set_active( act1, prefs->getInt("/tools/paintbucket/channels", 0) );
7723 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
7724 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
7725 g_object_set_data( holder, "channels_action", act1 );
7726 }
7728 // Spacing spinbox
7729 {
7730 eact = create_adjustment_action(
7731 "ThresholdAction",
7732 _("Fill Threshold"), _("Threshold:"),
7733 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
7734 "/tools/paintbucket/threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
7735 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
7736 0, 0, 0,
7737 paintbucket_threshold_changed, 1, 0 );
7739 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
7740 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7741 }
7743 // Create the units menu.
7744 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
7745 Glib::ustring stored_unit = prefs->getString("/tools/paintbucket/offsetunits");
7746 if (!stored_unit.empty())
7747 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit.data()));
7748 g_object_set_data( holder, "tracker", tracker );
7749 {
7750 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
7751 gtk_action_group_add_action( mainActions, act );
7752 }
7754 // Offset spinbox
7755 {
7756 eact = create_adjustment_action(
7757 "OffsetAction",
7758 _("Grow/shrink by"), _("Grow/shrink by:"),
7759 _("The amount to grow (positive) or shrink (negative) the created fill path"),
7760 "/tools/paintbucket/offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
7761 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
7762 0, 0, 0,
7763 paintbucket_offset_changed, 1, 2);
7764 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
7766 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7767 }
7769 /* Auto Gap */
7770 {
7771 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7773 GList* items = 0;
7774 gint count = 0;
7775 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
7776 {
7777 GtkTreeIter iter;
7778 gtk_list_store_append( model, &iter );
7779 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7780 count++;
7781 }
7782 g_list_free( items );
7783 items = 0;
7784 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
7785 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
7786 ege_select_one_action_set_appearance( act2, "compact" );
7787 ege_select_one_action_set_active( act2, prefs->getBool("/tools/paintbucket/autogap") );
7788 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
7789 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
7790 g_object_set_data( holder, "autogap_action", act2 );
7791 }
7793 /* Reset */
7794 {
7795 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
7796 _("Defaults"),
7797 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
7798 GTK_STOCK_CLEAR );
7799 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
7800 gtk_action_group_add_action( mainActions, act );
7801 gtk_action_set_sensitive( act, TRUE );
7802 }
7804 }
7806 /*
7807 Local Variables:
7808 mode:c++
7809 c-file-style:"stroustrup"
7810 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
7811 indent-tabs-mode:nil
7812 fill-column:99
7813 End:
7814 */
7815 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :