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@scislac.com>
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.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 "../pen-context.h"
69 #include "../preferences.h"
70 #include "../selection-chemistry.h"
71 #include "../selection.h"
72 #include "select-toolbar.h"
73 #include "../shape-editor.h"
74 #include "../shortcuts.h"
75 #include "../sp-clippath.h"
76 #include "../sp-ellipse.h"
77 #include "../sp-flowtext.h"
78 #include "../sp-mask.h"
79 #include "../sp-namedview.h"
80 #include "../sp-rect.h"
81 #include "../sp-spiral.h"
82 #include "../sp-star.h"
83 #include "../sp-text.h"
84 #include "../style.h"
85 #include "../svg/css-ostringstream.h"
86 #include "../tools-switch.h"
87 #include "../tweak-context.h"
88 #include "../spray-context.h"
89 #include "../ui/dialog/calligraphic-profile-rename.h"
90 #include "../ui/icon-names.h"
91 #include "../ui/tool/control-point-selection.h"
92 #include "../ui/tool/node-tool.h"
93 #include "../ui/tool/multi-path-manipulator.h"
94 #include "../ui/widget/style-swatch.h"
95 #include "../verbs.h"
96 #include "../widgets/button.h"
97 #include "../widgets/spinbutton-events.h"
98 #include "../widgets/spw-utilities.h"
99 #include "../widgets/widget-sizes.h"
100 #include "../xml/attribute-record.h"
101 #include "../xml/node-event-vector.h"
102 #include "../xml/repr.h"
103 #include "ui/uxmanager.h"
105 #include "toolbox.h"
107 #define ENABLE_TASK_SUPPORT 1
109 using Inkscape::UnitTracker;
110 using Inkscape::UI::UXManager;
112 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
113 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
115 enum BarId {
116 BAR_TOOL = 0,
117 BAR_AUX,
118 BAR_COMMANDS,
119 BAR_SNAP,
120 };
122 #define BAR_ID_KEY "BarIdValue"
123 #define HANDLE_POS_MARK "x-inkscape-pos"
125 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
126 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
127 static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
128 static void sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
129 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
130 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
131 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
132 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
133 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
134 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
135 static void sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
136 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
137 static void sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
138 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
139 static void sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
140 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
141 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
142 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
144 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
147 #if ENABLE_TASK_SUPPORT
148 static void fireTaskChange( EgeSelectOneAction *act, SPDesktop *dt )
149 {
150 gint selected = ege_select_one_action_get_active( act );
151 UXManager::getInstance()->setTask(dt, selected);
152 }
153 #endif // ENABLE_TASK_SUPPORT
155 using Inkscape::UI::ToolboxFactory;
158 Inkscape::IconSize ToolboxFactory::prefToSize( Glib::ustring const &path, int base ) {
159 static Inkscape::IconSize sizeChoices[] = {
160 Inkscape::ICON_SIZE_LARGE_TOOLBAR,
161 Inkscape::ICON_SIZE_SMALL_TOOLBAR,
162 Inkscape::ICON_SIZE_MENU
163 };
164 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
165 int index = prefs->getIntLimited( path, base, 0, G_N_ELEMENTS(sizeChoices) );
166 return sizeChoices[index];
167 }
169 static struct {
170 gchar const *type_name;
171 gchar const *data_name;
172 sp_verb_t verb;
173 sp_verb_t doubleclick_verb;
174 } const tools[] = {
175 { "SPSelectContext", "select_tool", SP_VERB_CONTEXT_SELECT, SP_VERB_CONTEXT_SELECT_PREFS},
176 { "InkNodeTool", "node_tool", SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
177 { "SPTweakContext", "tweak_tool", SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
178 { "SPSprayContext", "spray_tool", SP_VERB_CONTEXT_SPRAY, SP_VERB_CONTEXT_SPRAY_PREFS },
179 { "SPZoomContext", "zoom_tool", SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
180 { "SPRectContext", "rect_tool", SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
181 { "Box3DContext", "3dbox_tool", SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
182 { "SPArcContext", "arc_tool", SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
183 { "SPStarContext", "star_tool", SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
184 { "SPSpiralContext", "spiral_tool", SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
185 { "SPPencilContext", "pencil_tool", SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
186 { "SPPenContext", "pen_tool", SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
187 { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
188 { "SPLPEToolContext", "lpetool_tool", SP_VERB_CONTEXT_LPETOOL, SP_VERB_CONTEXT_LPETOOL_PREFS },
189 { "SPEraserContext", "eraser_tool", SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
190 { "SPFloodContext", "paintbucket_tool", SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
191 { "SPTextContext", "text_tool", SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
192 { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
193 { "SPGradientContext", "gradient_tool", SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
194 { "SPDropperContext", "dropper_tool", SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
195 { NULL, NULL, 0, 0 }
196 };
198 static struct {
199 gchar const *type_name;
200 gchar const *data_name;
201 GtkWidget *(*create_func)(SPDesktop *desktop);
202 void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
203 gchar const *ui_name;
204 gint swatch_verb_id;
205 gchar const *swatch_tool;
206 gchar const *swatch_tip;
207 } const aux_toolboxes[] = {
208 { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep, "SelectToolbar",
209 SP_VERB_INVALID, 0, 0},
210 { "InkNodeTool", "node_toolbox", 0, sp_node_toolbox_prep, "NodeToolbar",
211 SP_VERB_INVALID, 0, 0},
212 { "SPTweakContext", "tweak_toolbox", 0, sp_tweak_toolbox_prep, "TweakToolbar",
213 SP_VERB_CONTEXT_TWEAK_PREFS, "/tools/tweak", N_("Color/opacity used for color tweaking")},
214 { "SPSprayContext", "spray_toolbox", 0, sp_spray_toolbox_prep, "SprayToolbar",
215 SP_VERB_CONTEXT_SPRAY_PREFS, "/tools/spray", N_("Color/opacity used for color spraying")},
216 { "SPZoomContext", "zoom_toolbox", 0, sp_zoom_toolbox_prep, "ZoomToolbar",
217 SP_VERB_INVALID, 0, 0},
218 { "SPStarContext", "star_toolbox", 0, sp_star_toolbox_prep, "StarToolbar",
219 SP_VERB_CONTEXT_STAR_PREFS, "/tools/shapes/star", N_("Style of new stars")},
220 { "SPRectContext", "rect_toolbox", 0, sp_rect_toolbox_prep, "RectToolbar",
221 SP_VERB_CONTEXT_RECT_PREFS, "/tools/shapes/rect", N_("Style of new rectangles")},
222 { "Box3DContext", "3dbox_toolbox", 0, box3d_toolbox_prep, "3DBoxToolbar",
223 SP_VERB_CONTEXT_3DBOX_PREFS, "/tools/shapes/3dbox", N_("Style of new 3D boxes")},
224 { "SPArcContext", "arc_toolbox", 0, sp_arc_toolbox_prep, "ArcToolbar",
225 SP_VERB_CONTEXT_ARC_PREFS, "/tools/shapes/arc", N_("Style of new ellipses")},
226 { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep, "SpiralToolbar",
227 SP_VERB_CONTEXT_SPIRAL_PREFS, "/tools/shapes/spiral", N_("Style of new spirals")},
228 { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep, "PencilToolbar",
229 SP_VERB_CONTEXT_PENCIL_PREFS, "/tools/freehand/pencil", N_("Style of new paths created by Pencil")},
230 { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep, "PenToolbar",
231 SP_VERB_CONTEXT_PEN_PREFS, "/tools/freehand/pen", N_("Style of new paths created by Pen")},
232 { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
233 SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "/tools/calligraphic", N_("Style of new calligraphic strokes")},
234 { "SPEraserContext", "eraser_toolbox", 0, sp_eraser_toolbox_prep,"EraserToolbar",
235 SP_VERB_CONTEXT_ERASER_PREFS, "/tools/eraser", _("TBD")},
236 { "SPLPEToolContext", "lpetool_toolbox", 0, sp_lpetool_toolbox_prep, "LPEToolToolbar",
237 SP_VERB_CONTEXT_LPETOOL_PREFS, "/tools/lpetool", _("TBD")},
238 { "SPTextContext", "text_toolbox", sp_text_toolbox_new, 0, 0,
239 SP_VERB_INVALID, 0, 0},
240 { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep, "DropperToolbar",
241 SP_VERB_INVALID, 0, 0},
242 { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0, 0,
243 SP_VERB_INVALID, 0, 0},
244 { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep, "ConnectorToolbar",
245 SP_VERB_INVALID, 0, 0},
246 { "SPFloodContext", "paintbucket_toolbox", 0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
247 SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "/tools/paintbucket", N_("Style of Paint Bucket fill objects")},
248 { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
249 };
251 #define TOOLBAR_SLIDER_HINT "full"
253 static gchar const * ui_descr =
254 "<ui>"
255 " <toolbar name='SelectToolbar'>"
256 " <toolitem action='EditSelectAll' />"
257 " <toolitem action='EditSelectAllInAllLayers' />"
258 " <toolitem action='EditDeselect' />"
259 " <separator />"
260 " <toolitem action='ObjectRotate90CCW' />"
261 " <toolitem action='ObjectRotate90' />"
262 " <toolitem action='ObjectFlipHorizontally' />"
263 " <toolitem action='ObjectFlipVertically' />"
264 " <separator />"
265 " <toolitem action='SelectionToBack' />"
266 " <toolitem action='SelectionLower' />"
267 " <toolitem action='SelectionRaise' />"
268 " <toolitem action='SelectionToFront' />"
269 " <separator />"
270 " <toolitem action='XAction' />"
271 " <toolitem action='YAction' />"
272 " <toolitem action='WidthAction' />"
273 " <toolitem action='LockAction' />"
274 " <toolitem action='HeightAction' />"
275 " <toolitem action='UnitsAction' />"
276 " <separator />"
277 " <toolitem action='transform_affect_label' />"
278 " <toolitem action='transform_stroke' />"
279 " <toolitem action='transform_corners' />"
280 " <toolitem action='transform_gradient' />"
281 " <toolitem action='transform_pattern' />"
282 " </toolbar>"
284 " <toolbar name='NodeToolbar'>"
285 " <separator />"
286 " <toolitem action='NodeInsertAction' />"
287 " <toolitem action='NodeDeleteAction' />"
288 " <separator />"
289 " <toolitem action='NodeJoinAction' />"
290 " <toolitem action='NodeBreakAction' />"
291 " <separator />"
292 " <toolitem action='NodeJoinSegmentAction' />"
293 " <toolitem action='NodeDeleteSegmentAction' />"
294 " <separator />"
295 " <toolitem action='NodeCuspAction' />"
296 " <toolitem action='NodeSmoothAction' />"
297 " <toolitem action='NodeSymmetricAction' />"
298 " <toolitem action='NodeAutoAction' />"
299 " <separator />"
300 " <toolitem action='NodeLineAction' />"
301 " <toolitem action='NodeCurveAction' />"
302 " <separator />"
303 " <toolitem action='ObjectToPath' />"
304 " <toolitem action='StrokeToPath' />"
305 " <separator />"
306 " <toolitem action='NodeXAction' />"
307 " <toolitem action='NodeYAction' />"
308 " <toolitem action='NodeUnitsAction' />"
309 " <separator />"
310 " <toolitem action='ObjectEditClipPathAction' />"
311 " <toolitem action='ObjectEditMaskPathAction' />"
312 " <toolitem action='EditNextLPEParameterAction' />"
313 " <separator />"
314 " <toolitem action='NodesShowTransformHandlesAction' />"
315 " <toolitem action='NodesShowHandlesAction' />"
316 " <toolitem action='NodesShowHelperpath' />"
317 " </toolbar>"
319 " <toolbar name='TweakToolbar'>"
320 " <toolitem action='TweakWidthAction' />"
321 " <separator />"
322 " <toolitem action='TweakForceAction' />"
323 " <toolitem action='TweakPressureAction' />"
324 " <separator />"
325 " <toolitem action='TweakModeAction' />"
326 " <separator />"
327 " <toolitem action='TweakFidelityAction' />"
328 " <separator />"
329 " <toolitem action='TweakChannelsLabel' />"
330 " <toolitem action='TweakDoH' />"
331 " <toolitem action='TweakDoS' />"
332 " <toolitem action='TweakDoL' />"
333 " <toolitem action='TweakDoO' />"
334 " </toolbar>"
336 " <toolbar name='SprayToolbar'>"
337 " <toolitem action='SprayModeAction' />"
338 " <separator />"
339 " <separator />"
340 " <toolitem action='SprayWidthAction' />"
341 " <toolitem action='SprayPressureAction' />"
342 " <toolitem action='SprayPopulationAction' />"
343 " <separator />"
344 " <toolitem action='SprayRotationAction' />"
345 " <toolitem action='SprayScaleAction' />"
346 " <separator />"
347 " <toolitem action='SprayStandard_deviationAction' />"
348 " <toolitem action='SprayMeanAction' />"
349 " </toolbar>"
351 " <toolbar name='ZoomToolbar'>"
352 " <toolitem action='ZoomIn' />"
353 " <toolitem action='ZoomOut' />"
354 " <separator />"
355 " <toolitem action='Zoom1:0' />"
356 " <toolitem action='Zoom1:2' />"
357 " <toolitem action='Zoom2:1' />"
358 " <separator />"
359 " <toolitem action='ZoomSelection' />"
360 " <toolitem action='ZoomDrawing' />"
361 " <toolitem action='ZoomPage' />"
362 " <toolitem action='ZoomPageWidth' />"
363 " <separator />"
364 " <toolitem action='ZoomPrev' />"
365 " <toolitem action='ZoomNext' />"
366 " </toolbar>"
368 " <toolbar name='StarToolbar'>"
369 " <separator />"
370 " <toolitem action='StarStateAction' />"
371 " <separator />"
372 " <toolitem action='FlatAction' />"
373 " <separator />"
374 " <toolitem action='MagnitudeAction' />"
375 " <toolitem action='SpokeAction' />"
376 " <toolitem action='RoundednessAction' />"
377 " <toolitem action='RandomizationAction' />"
378 " <separator />"
379 " <toolitem action='StarResetAction' />"
380 " </toolbar>"
382 " <toolbar name='RectToolbar'>"
383 " <toolitem action='RectStateAction' />"
384 " <toolitem action='RectWidthAction' />"
385 " <toolitem action='RectHeightAction' />"
386 " <toolitem action='RadiusXAction' />"
387 " <toolitem action='RadiusYAction' />"
388 " <toolitem action='RectUnitsAction' />"
389 " <separator />"
390 " <toolitem action='RectResetAction' />"
391 " </toolbar>"
393 " <toolbar name='3DBoxToolbar'>"
394 " <toolitem action='3DBoxAngleXAction' />"
395 " <toolitem action='3DBoxVPXStateAction' />"
396 " <separator />"
397 " <toolitem action='3DBoxAngleYAction' />"
398 " <toolitem action='3DBoxVPYStateAction' />"
399 " <separator />"
400 " <toolitem action='3DBoxAngleZAction' />"
401 " <toolitem action='3DBoxVPZStateAction' />"
402 " </toolbar>"
404 " <toolbar name='SpiralToolbar'>"
405 " <toolitem action='SpiralStateAction' />"
406 " <toolitem action='SpiralRevolutionAction' />"
407 " <toolitem action='SpiralExpansionAction' />"
408 " <toolitem action='SpiralT0Action' />"
409 " <separator />"
410 " <toolitem action='SpiralResetAction' />"
411 " </toolbar>"
413 " <toolbar name='PenToolbar'>"
414 " <toolitem action='FreehandModeActionPen' />"
415 " <separator />"
416 " <toolitem action='SetPenShapeAction'/>"
417 " </toolbar>"
419 " <toolbar name='PencilToolbar'>"
420 " <toolitem action='FreehandModeActionPencil' />"
421 " <separator />"
422 " <toolitem action='PencilToleranceAction' />"
423 " <separator />"
424 " <toolitem action='PencilResetAction' />"
425 " <separator />"
426 " <toolitem action='SetPencilShapeAction'/>"
427 " </toolbar>"
429 " <toolbar name='CalligraphyToolbar'>"
430 " <separator />"
431 " <toolitem action='SetProfileAction'/>"
432 " <separator />"
433 " <toolitem action='CalligraphyWidthAction' />"
434 " <toolitem action='PressureAction' />"
435 " <toolitem action='TraceAction' />"
436 " <toolitem action='ThinningAction' />"
437 " <separator />"
438 " <toolitem action='AngleAction' />"
439 " <toolitem action='TiltAction' />"
440 " <toolitem action='FixationAction' />"
441 " <separator />"
442 " <toolitem action='CapRoundingAction' />"
443 " <separator />"
444 " <toolitem action='TremorAction' />"
445 " <toolitem action='WiggleAction' />"
446 " <toolitem action='MassAction' />"
447 " <separator />"
448 " </toolbar>"
450 " <toolbar name='ArcToolbar'>"
451 " <toolitem action='ArcStateAction' />"
452 " <separator />"
453 " <toolitem action='ArcStartAction' />"
454 " <toolitem action='ArcEndAction' />"
455 " <separator />"
456 " <toolitem action='ArcOpenAction' />"
457 " <separator />"
458 " <toolitem action='ArcResetAction' />"
459 " <separator />"
460 " </toolbar>"
462 " <toolbar name='PaintbucketToolbar'>"
463 " <toolitem action='ChannelsAction' />"
464 " <separator />"
465 " <toolitem action='ThresholdAction' />"
466 " <separator />"
467 " <toolitem action='OffsetAction' />"
468 " <toolitem action='PaintbucketUnitsAction' />"
469 " <separator />"
470 " <toolitem action='AutoGapAction' />"
471 " <separator />"
472 " <toolitem action='PaintbucketResetAction' />"
473 " </toolbar>"
475 " <toolbar name='EraserToolbar'>"
476 " <toolitem action='EraserWidthAction' />"
477 " <separator />"
478 " <toolitem action='EraserModeAction' />"
479 " </toolbar>"
481 " <toolbar name='LPEToolToolbar'>"
482 " <toolitem action='LPEToolModeAction' />"
483 " <separator />"
484 " <toolitem action='LPEShowBBoxAction' />"
485 " <toolitem action='LPEBBoxFromSelectionAction' />"
486 " <separator />"
487 " <toolitem action='LPELineSegmentAction' />"
488 " <separator />"
489 " <toolitem action='LPEMeasuringAction' />"
490 " <toolitem action='LPEToolUnitsAction' />"
491 " <separator />"
492 " <toolitem action='LPEOpenLPEDialogAction' />"
493 " </toolbar>"
495 " <toolbar name='DropperToolbar'>"
496 " <toolitem action='DropperOpacityAction' />"
497 " <toolitem action='DropperPickAlphaAction' />"
498 " <toolitem action='DropperSetAlphaAction' />"
499 " </toolbar>"
501 " <toolbar name='ConnectorToolbar'>"
502 // " <toolitem action='ConnectorEditModeAction' />"
503 " <toolitem action='ConnectorAvoidAction' />"
504 " <toolitem action='ConnectorIgnoreAction' />"
505 " <toolitem action='ConnectorOrthogonalAction' />"
506 " <toolitem action='ConnectorCurvatureAction' />"
507 " <toolitem action='ConnectorSpacingAction' />"
508 " <toolitem action='ConnectorGraphAction' />"
509 " <toolitem action='ConnectorLengthAction' />"
510 " <toolitem action='ConnectorDirectedAction' />"
511 " <toolitem action='ConnectorOverlapAction' />"
512 // " <toolitem action='ConnectorNewConnPointAction' />"
513 // " <toolitem action='ConnectorRemoveConnPointAction' />"
514 " </toolbar>"
516 "</ui>"
517 ;
519 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
521 static void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop);
523 static void setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop);
524 static void update_tool_toolbox(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
526 static void setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop);
527 static void update_aux_toolbox(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
529 static void setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop);
530 static void update_commands_toolbox(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
532 static GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick( GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
533 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
534 Inkscape::UI::View::View *view, GtkTooltips *tt);
536 class VerbAction : public Gtk::Action {
537 public:
538 static Glib::RefPtr<VerbAction> create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
540 virtual ~VerbAction();
541 virtual void set_active(bool active = true);
543 protected:
544 virtual Gtk::Widget* create_menu_item_vfunc();
545 virtual Gtk::Widget* create_tool_item_vfunc();
547 virtual void connect_proxy_vfunc(Gtk::Widget* proxy);
548 virtual void disconnect_proxy_vfunc(Gtk::Widget* proxy);
550 virtual void on_activate();
552 private:
553 Inkscape::Verb* verb;
554 Inkscape::Verb* verb2;
555 Inkscape::UI::View::View *view;
556 GtkTooltips *tooltips;
557 bool active;
559 VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
560 };
563 Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips)
564 {
565 Glib::RefPtr<VerbAction> result;
566 SPAction *action = verb->get_action(view);
567 if ( action ) {
568 //SPAction* action2 = verb2 ? verb2->get_action(view) : 0;
569 result = Glib::RefPtr<VerbAction>(new VerbAction(verb, verb2, view, tooltips));
570 }
572 return result;
573 }
575 VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips) :
576 Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(verb->get_image()), Glib::ustring(_(verb->get_name())), Glib::ustring(_(verb->get_tip()))),
577 verb(verb),
578 verb2(verb2),
579 view(view),
580 tooltips(tooltips),
581 active(false)
582 {
583 }
585 VerbAction::~VerbAction()
586 {
587 }
589 Gtk::Widget* VerbAction::create_menu_item_vfunc()
590 {
591 // First call in to get the icon rendered if present in SVG
592 Gtk::Widget *widget = sp_icon_get_icon( property_stock_id().get_value().get_string(), Inkscape::ICON_SIZE_MENU );
593 delete widget;
594 widget = 0;
596 Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
597 // g_message("create_menu_item_vfunc() = %p for '%s'", widg, verb->get_id());
598 return widg;
599 }
601 Gtk::Widget* VerbAction::create_tool_item_vfunc()
602 {
603 // Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
604 Inkscape::IconSize toolboxSize = ToolboxFactory::prefToSize("/toolbox/tools/small");
605 GtkWidget* toolbox = 0;
606 GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
607 SP_BUTTON_TYPE_TOGGLE,
608 verb,
609 verb2,
610 view,
611 tooltips );
612 if ( active ) {
613 sp_button_toggle_set_down( SP_BUTTON(button), active);
614 }
615 gtk_widget_show_all( button );
616 Gtk::Widget* wrapped = Glib::wrap(button);
617 Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
618 holder->add(*wrapped);
620 // g_message("create_tool_item_vfunc() = %p for '%s'", holder, verb->get_id());
621 return holder;
622 }
624 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
625 {
626 // g_message("connect_proxy_vfunc(%p) for '%s'", proxy, verb->get_id());
627 Gtk::Action::connect_proxy_vfunc(proxy);
628 }
630 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
631 {
632 // g_message("disconnect_proxy_vfunc(%p) for '%s'", proxy, verb->get_id());
633 Gtk::Action::disconnect_proxy_vfunc(proxy);
634 }
636 void VerbAction::set_active(bool active)
637 {
638 this->active = active;
639 Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
640 for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
641 Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
642 if (ti) {
643 // *should* have one child that is the SPButton
644 Gtk::Widget* child = ti->get_child();
645 if ( child && SP_IS_BUTTON(child->gobj()) ) {
646 SPButton* button = SP_BUTTON(child->gobj());
647 sp_button_toggle_set_down( button, active );
648 }
649 }
650 }
651 }
653 void VerbAction::on_activate()
654 {
655 if ( verb ) {
656 SPAction *action = verb->get_action(view);
657 if ( action ) {
658 sp_action_perform(action, 0);
659 }
660 }
661 }
663 /* Global text entry widgets necessary for update */
664 /* GtkWidget *dropper_rgb_entry,
665 *dropper_opacity_entry ; */
666 // should be made a private member once this is converted to class
668 static void delete_connection(GObject * /*obj*/, sigc::connection *connection)
669 {
670 connection->disconnect();
671 delete connection;
672 }
674 static void purge_repr_listener( GObject* /*obj*/, GObject* tbl )
675 {
676 Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
677 if (oldrepr) { // remove old listener
678 sp_repr_remove_listener_by_data(oldrepr, tbl);
679 Inkscape::GC::release(oldrepr);
680 oldrepr = 0;
681 g_object_set_data( tbl, "repr", NULL );
682 }
683 }
685 // ------------------------------------------------------
687 /**
688 * A simple mediator class that keeps UI controls matched to the preference values they set.
689 */
690 class PrefPusher : public Inkscape::Preferences::Observer
691 {
692 public:
693 /**
694 * Constructor for a boolean value that syncs to the supplied path.
695 * Initializes the widget to the current preference stored state and registers callbacks
696 * for widget changes and preference changes.
697 *
698 * @param act the widget to synchronize preference with.
699 * @param path the path to the preference the widget is synchronized with.
700 * @param callback function to invoke when changes are pushed.
701 * @param cbData data to be passed on to the callback function.
702 */
703 PrefPusher( GtkToggleAction *act, Glib::ustring const &path, void (*callback)(GObject*) = 0, GObject *cbData = 0 );
705 /**
706 * Destructor that unregisters the preference callback.
707 */
708 virtual ~PrefPusher();
710 /**
711 * Callback method invoked when the preference setting changes.
712 */
713 virtual void notify(Inkscape::Preferences::Entry const &new_val);
715 private:
716 /**
717 * Callback hook invoked when the widget changes.
718 *
719 * @param act the toggle action widget that was changed.
720 * @param self the PrefPusher instance the callback was registered to.
721 */
722 static void toggleCB( GtkToggleAction *act, PrefPusher *self );
724 /**
725 * Method to handle the widget change.
726 */
727 void handleToggled();
729 GtkToggleAction *act;
730 void (*callback)(GObject*);
731 GObject *cbData;
732 bool freeze;
733 };
735 PrefPusher::PrefPusher( GtkToggleAction *act, Glib::ustring const &path, void (*callback)(GObject*), GObject *cbData ) :
736 Observer(path),
737 act(act),
738 callback(callback),
739 cbData(cbData),
740 freeze(false)
741 {
742 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggleCB), this);
743 freeze = true;
744 gtk_toggle_action_set_active( act, Inkscape::Preferences::get()->getBool(observed_path, true) );
745 freeze = false;
747 Inkscape::Preferences::get()->addObserver(*this);
748 }
750 PrefPusher::~PrefPusher()
751 {
752 Inkscape::Preferences::get()->removeObserver(*this);
753 }
755 void PrefPusher::toggleCB( GtkToggleAction * /*act*/, PrefPusher *self )
756 {
757 if (self) {
758 self->handleToggled();
759 }
760 }
762 void PrefPusher::handleToggled()
763 {
764 if (!freeze) {
765 freeze = true;
766 Inkscape::Preferences::get()->setBool(observed_path, gtk_toggle_action_get_active( act ));
767 if (callback) {
768 (*callback)(cbData);
769 }
770 freeze = false;
771 }
772 }
774 void PrefPusher::notify(Inkscape::Preferences::Entry const &newVal)
775 {
776 bool newBool = newVal.getBool();
777 bool oldBool = gtk_toggle_action_get_active(act);
779 if (!freeze && (newBool != oldBool)) {
780 gtk_toggle_action_set_active( act, newBool );
781 }
782 }
784 static void delete_prefspusher(GtkObject * /*obj*/, PrefPusher *watcher )
785 {
786 delete watcher;
787 }
789 // ------------------------------------------------------
792 GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
793 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
794 Inkscape::UI::View::View *view, GtkTooltips *tt)
795 {
796 SPAction *action = verb->get_action(view);
797 if (!action) {
798 return NULL;
799 }
801 SPAction *doubleclick_action;
802 if (doubleclick_verb) {
803 doubleclick_action = doubleclick_verb->get_action(view);
804 } else {
805 doubleclick_action = NULL;
806 }
808 /* fixme: Handle sensitive/unsensitive */
809 /* fixme: Implement sp_button_new_from_action */
810 GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
811 gtk_widget_show(b);
814 unsigned int shortcut = sp_shortcut_get_primary(verb);
815 if (shortcut) {
816 gchar key[256];
817 sp_ui_shortcut_string(shortcut, key);
818 gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
819 if ( t ) {
820 gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
821 }
822 g_free(tip);
823 } else {
824 if ( t ) {
825 gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
826 }
827 }
829 return b;
830 }
833 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
834 {
835 SPAction* targetAction = SP_ACTION(user_data);
836 if ( targetAction ) {
837 sp_action_perform( targetAction, NULL );
838 }
839 }
841 static void sp_action_action_set_sensitive(SPAction * /*action*/, unsigned int sensitive, void *data)
842 {
843 if ( data ) {
844 GtkAction* act = GTK_ACTION(data);
845 gtk_action_set_sensitive( act, sensitive );
846 }
847 }
849 static SPActionEventVector action_event_vector = {
850 {NULL},
851 NULL,
852 NULL,
853 sp_action_action_set_sensitive,
854 NULL,
855 NULL
856 };
858 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
859 {
860 GtkAction* act = 0;
862 SPAction* targetAction = verb->get_action(view);
863 InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size );
864 act = GTK_ACTION(inky);
865 gtk_action_set_sensitive( act, targetAction->sensitive );
867 g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
869 SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
870 nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
872 return act;
873 }
875 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
876 {
877 Inkscape::UI::View::View *view = desktop;
878 gint verbsToUse[] = {
879 // disabled until we have icons for them:
880 //find
881 //SP_VERB_EDIT_TILE,
882 //SP_VERB_EDIT_UNTILE,
883 SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
884 SP_VERB_DIALOG_DISPLAY,
885 SP_VERB_DIALOG_FILL_STROKE,
886 SP_VERB_DIALOG_NAMEDVIEW,
887 SP_VERB_DIALOG_TEXT,
888 SP_VERB_DIALOG_XML_EDITOR,
889 SP_VERB_DIALOG_LAYERS,
890 SP_VERB_EDIT_CLONE,
891 SP_VERB_EDIT_COPY,
892 SP_VERB_EDIT_CUT,
893 SP_VERB_EDIT_DUPLICATE,
894 SP_VERB_EDIT_PASTE,
895 SP_VERB_EDIT_REDO,
896 SP_VERB_EDIT_UNDO,
897 SP_VERB_EDIT_UNLINK_CLONE,
898 SP_VERB_FILE_EXPORT,
899 SP_VERB_FILE_IMPORT,
900 SP_VERB_FILE_NEW,
901 SP_VERB_FILE_OPEN,
902 SP_VERB_FILE_PRINT,
903 SP_VERB_FILE_SAVE,
904 SP_VERB_OBJECT_TO_CURVE,
905 SP_VERB_SELECTION_GROUP,
906 SP_VERB_SELECTION_OUTLINE,
907 SP_VERB_SELECTION_UNGROUP,
908 SP_VERB_ZOOM_1_1,
909 SP_VERB_ZOOM_1_2,
910 SP_VERB_ZOOM_2_1,
911 SP_VERB_ZOOM_DRAWING,
912 SP_VERB_ZOOM_IN,
913 SP_VERB_ZOOM_NEXT,
914 SP_VERB_ZOOM_OUT,
915 SP_VERB_ZOOM_PAGE,
916 SP_VERB_ZOOM_PAGE_WIDTH,
917 SP_VERB_ZOOM_PREV,
918 SP_VERB_ZOOM_SELECTION,
919 };
921 Inkscape::IconSize toolboxSize = ToolboxFactory::prefToSize("/toolbox/small");
923 static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
924 Glib::RefPtr<Gtk::ActionGroup> mainActions;
925 if ( groups.find(desktop) != groups.end() ) {
926 mainActions = groups[desktop];
927 }
929 if ( !mainActions ) {
930 mainActions = Gtk::ActionGroup::create("main");
931 groups[desktop] = mainActions;
932 }
934 for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
935 Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
936 if ( verb ) {
937 if (!mainActions->get_action(verb->get_id())) {
938 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
939 mainActions->add(Glib::wrap(act));
940 }
941 }
942 }
944 if ( !mainActions->get_action("ToolZoom") ) {
945 GtkTooltips *tt = gtk_tooltips_new();
946 for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
947 Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
948 if ( va ) {
949 mainActions->add(va);
950 if ( i == 0 ) {
951 va->set_active(true);
952 }
953 }
954 }
955 }
957 #if ENABLE_TASK_SUPPORT
958 if ( !mainActions->get_action("TaskSetAction") ) {
959 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_STRING );
961 GtkTreeIter iter;
962 gtk_list_store_append( model, &iter );
963 gtk_list_store_set( model, &iter,
964 0, _("Default"),
965 1, _("Default interface setup"),
966 -1 );
968 gtk_list_store_append( model, &iter );
969 gtk_list_store_set( model, &iter,
970 0, _("Custom"),
971 1, _("Set the custom task"),
972 -1 );
974 gtk_list_store_append( model, &iter );
975 gtk_list_store_set( model, &iter,
976 0, _("Wide"),
977 1, _("Setup for widescreen work"),
978 -1 );
980 EgeSelectOneAction* act = ege_select_one_action_new( "TaskSetAction", _("Task"), (""), NULL, GTK_TREE_MODEL(model) );
981 g_object_set( act, "short_label", _("Task:"), NULL );
982 mainActions->add(Glib::wrap(GTK_ACTION(act)));
983 //g_object_set_data( holder, "mode_action", act );
985 ege_select_one_action_set_appearance( act, "minimal" );
986 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
987 //ege_select_one_action_set_icon_size( act, secondarySize );
988 ege_select_one_action_set_tooltip_column( act, 1 );
990 //ege_select_one_action_set_active( act, mode );
991 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(fireTaskChange), desktop );
992 }
993 #endif // ENABLE_TASK_SUPPORT
995 return mainActions;
996 }
999 static void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
1000 {
1001 gtk_widget_set_size_request( widget,
1002 widget->allocation.width,
1003 widget->allocation.height );
1004 }
1006 static void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
1007 {
1008 gtk_widget_set_size_request( widget, -1, -1 );
1009 }
1011 static GtkWidget* toolboxNewCommon( GtkWidget* tb, BarId id, GtkPositionType handlePos )
1012 {
1013 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
1015 gtk_widget_set_sensitive(tb, FALSE);
1017 GtkWidget *hb = 0;
1018 if ( UXManager::getInstance()->isFloatWindowProblem() ) {
1019 hb = gtk_event_box_new(); // A simple, neutral container.
1020 } else {
1021 hb = gtk_handle_box_new();
1022 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), handlePos);
1023 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
1024 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
1025 }
1027 gtk_container_add(GTK_CONTAINER(hb), tb);
1028 gtk_widget_show(GTK_WIDGET(tb));
1030 sigc::connection* conn = new sigc::connection;
1031 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
1033 if ( GTK_IS_HANDLE_BOX(hb) ) {
1034 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
1035 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
1036 }
1038 gpointer val = GINT_TO_POINTER(id);
1039 g_object_set_data(G_OBJECT(hb), BAR_ID_KEY, val);
1041 return hb;
1042 }
1044 GtkWidget *ToolboxFactory::createToolToolbox()
1045 {
1046 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
1048 return toolboxNewCommon( tb, BAR_TOOL, GTK_POS_TOP );
1049 }
1051 GtkWidget *ToolboxFactory::createAuxToolbox()
1052 {
1053 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
1055 return toolboxNewCommon( tb, BAR_AUX, GTK_POS_LEFT );
1056 }
1058 //####################################
1059 //# Commands Bar
1060 //####################################
1062 GtkWidget *ToolboxFactory::createCommandsToolbox()
1063 {
1064 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
1066 return toolboxNewCommon( tb, BAR_COMMANDS, GTK_POS_LEFT );
1067 }
1069 GtkWidget *ToolboxFactory::createSnapToolbox()
1070 {
1071 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
1073 return toolboxNewCommon( tb, BAR_SNAP, GTK_POS_LEFT );
1074 }
1076 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
1077 gchar const *label, gchar const *shortLabel, gchar const *tooltip,
1078 Glib::ustring const &path, gdouble def,
1079 GtkWidget *focusTarget,
1080 GtkWidget *us,
1081 GObject *dataKludge,
1082 gboolean altx, gchar const *altx_mark,
1083 gdouble lower, gdouble upper, gdouble step, gdouble page,
1084 gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
1085 void (*callback)(GtkAdjustment *, GObject *),
1086 gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
1087 {
1088 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1089 GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs->getDouble(path, def) * factor,
1090 lower, upper, step, page, 0 ) );
1091 if (us) {
1092 sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
1093 }
1095 gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
1097 EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
1098 if ( shortLabel ) {
1099 g_object_set( act, "short_label", shortLabel, NULL );
1100 }
1102 if ( (descrCount > 0) && descrLabels && descrValues ) {
1103 ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
1104 }
1106 if ( focusTarget ) {
1107 ege_adjustment_action_set_focuswidget( act, focusTarget );
1108 }
1110 if ( altx && altx_mark ) {
1111 g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
1112 }
1114 if ( dataKludge ) {
1115 // Rather lame, but it's the only place where we need to get the entry name
1116 // but we don't have an Entry
1117 g_object_set_data( dataKludge, prefs->getEntry(path).getEntryName().data(), adj );
1118 }
1120 // Using a cast just to make sure we pass in the right kind of function pointer
1121 g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
1123 return act;
1124 }
1127 //####################################
1128 //# node editing callbacks
1129 //####################################
1131 /** Temporary hack: Returns the node tool in the active desktop.
1132 * Will go away during tool refactoring. */
1133 static InkNodeTool *get_node_tool()
1134 {
1135 InkNodeTool *tool = 0;
1136 if (SP_ACTIVE_DESKTOP ) {
1137 SPEventContext *ec = SP_ACTIVE_DESKTOP->event_context;
1138 if (INK_IS_NODE_TOOL(ec)) {
1139 tool = static_cast<InkNodeTool*>(ec);
1140 }
1141 }
1142 return tool;
1143 }
1145 static void sp_node_path_edit_add(void)
1146 {
1147 InkNodeTool *nt = get_node_tool();
1148 if (nt) {
1149 nt->_multipath->insertNodes();
1150 }
1151 }
1153 static void sp_node_path_edit_delete(void)
1154 {
1155 InkNodeTool *nt = get_node_tool();
1156 if (nt) {
1157 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1158 nt->_multipath->deleteNodes(prefs->getBool("/tools/nodes/delete_preserves_shape", true));
1159 }
1160 }
1162 static void sp_node_path_edit_delete_segment(void)
1163 {
1164 InkNodeTool *nt = get_node_tool();
1165 if (nt) {
1166 nt->_multipath->deleteSegments();
1167 }
1168 }
1170 static void sp_node_path_edit_break(void)
1171 {
1172 InkNodeTool *nt = get_node_tool();
1173 if (nt) {
1174 nt->_multipath->breakNodes();
1175 }
1176 }
1178 static void sp_node_path_edit_join(void)
1179 {
1180 InkNodeTool *nt = get_node_tool();
1181 if (nt) {
1182 nt->_multipath->joinNodes();
1183 }
1184 }
1186 static void sp_node_path_edit_join_segment(void)
1187 {
1188 InkNodeTool *nt = get_node_tool();
1189 if (nt) {
1190 nt->_multipath->joinSegments();
1191 }
1192 }
1194 static void sp_node_path_edit_toline(void)
1195 {
1196 InkNodeTool *nt = get_node_tool();
1197 if (nt) {
1198 nt->_multipath->setSegmentType(Inkscape::UI::SEGMENT_STRAIGHT);
1199 }
1200 }
1202 static void sp_node_path_edit_tocurve(void)
1203 {
1204 InkNodeTool *nt = get_node_tool();
1205 if (nt) {
1206 nt->_multipath->setSegmentType(Inkscape::UI::SEGMENT_CUBIC_BEZIER);
1207 }
1208 }
1210 static void sp_node_path_edit_cusp(void)
1211 {
1212 InkNodeTool *nt = get_node_tool();
1213 if (nt) {
1214 nt->_multipath->setNodeType(Inkscape::UI::NODE_CUSP);
1215 }
1216 }
1218 static void sp_node_path_edit_smooth(void)
1219 {
1220 InkNodeTool *nt = get_node_tool();
1221 if (nt) {
1222 nt->_multipath->setNodeType(Inkscape::UI::NODE_SMOOTH);
1223 }
1224 }
1226 static void sp_node_path_edit_symmetrical(void)
1227 {
1228 InkNodeTool *nt = get_node_tool();
1229 if (nt) {
1230 nt->_multipath->setNodeType(Inkscape::UI::NODE_SYMMETRIC);
1231 }
1232 }
1234 static void sp_node_path_edit_auto(void)
1235 {
1236 InkNodeTool *nt = get_node_tool();
1237 if (nt) {
1238 nt->_multipath->setNodeType(Inkscape::UI::NODE_AUTO);
1239 }
1240 }
1242 static void sp_node_path_edit_nextLPEparam(GtkAction * /*act*/, gpointer data) {
1243 sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1244 }
1246 /* is called when the node selection is modified */
1247 static void sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1248 {
1249 GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1250 GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1251 GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1252 GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1254 // quit if run by the attr_changed listener
1255 if (g_object_get_data( tbl, "freeze" )) {
1256 return;
1257 }
1259 // in turn, prevent listener from responding
1260 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1262 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1263 SPUnit const *unit = tracker->getActiveUnit();
1265 InkNodeTool *nt = get_node_tool();
1266 if (!nt || nt->_selected_nodes->empty()) {
1267 // no path selected
1268 gtk_action_set_sensitive(xact, FALSE);
1269 gtk_action_set_sensitive(yact, FALSE);
1270 } else {
1271 gtk_action_set_sensitive(xact, TRUE);
1272 gtk_action_set_sensitive(yact, TRUE);
1273 Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1274 Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1275 Geom::Point mid = nt->_selected_nodes->pointwiseBounds()->midpoint();
1277 if (oldx != mid[Geom::X]) {
1278 gtk_adjustment_set_value(xadj, sp_pixels_get_units(mid[Geom::X], *unit));
1279 }
1280 if (oldy != mid[Geom::Y]) {
1281 gtk_adjustment_set_value(yadj, sp_pixels_get_units(mid[Geom::Y], *unit));
1282 }
1283 }
1285 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1286 }
1288 static void sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, Geom::Dim2 d)
1289 {
1290 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1291 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1293 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1294 SPUnit const *unit = tracker->getActiveUnit();
1296 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1297 prefs->setDouble(Glib::ustring("/tools/nodes/") + (d == Geom::X ? "x" : "y"),
1298 sp_units_get_pixels(adj->value, *unit));
1299 }
1301 // quit if run by the attr_changed listener
1302 if (g_object_get_data( tbl, "freeze" )) {
1303 return;
1304 }
1306 // in turn, prevent listener from responding
1307 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1309 InkNodeTool *nt = get_node_tool();
1310 if (nt && !nt->_selected_nodes->empty()) {
1311 double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1312 double oldval = nt->_selected_nodes->pointwiseBounds()->midpoint()[d];
1313 Geom::Point delta(0,0);
1314 delta[d] = val - oldval;
1315 nt->_multipath->move(delta);
1316 }
1318 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1319 }
1321 static void sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1322 {
1323 sp_node_path_value_changed(adj, tbl, Geom::X);
1324 }
1326 static void sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1327 {
1328 sp_node_path_value_changed(adj, tbl, Geom::Y);
1329 }
1331 static void sp_node_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
1332 {
1333 {
1334 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1335 SPItem *item = selection->singleItem();
1336 if (item && SP_IS_LPE_ITEM(item)) {
1337 if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1338 gtk_action_set_sensitive(w, TRUE);
1339 } else {
1340 gtk_action_set_sensitive(w, FALSE);
1341 }
1342 } else {
1343 gtk_action_set_sensitive(w, FALSE);
1344 }
1345 }
1346 }
1348 static void sp_node_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1349 {
1350 sp_node_toolbox_sel_changed (selection, tbl);
1351 }
1355 //################################
1356 //## Node Editing Toolbox ##
1357 //################################
1359 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1360 {
1361 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1362 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1363 g_object_set_data( holder, "tracker", tracker );
1365 Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
1367 {
1368 InkAction* inky = ink_action_new( "NodeInsertAction",
1369 _("Insert node"),
1370 _("Insert new nodes into selected segments"),
1371 INKSCAPE_ICON_NODE_ADD,
1372 secondarySize );
1373 g_object_set( inky, "short_label", _("Insert"), NULL );
1374 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1375 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1376 }
1378 {
1379 InkAction* inky = ink_action_new( "NodeDeleteAction",
1380 _("Delete node"),
1381 _("Delete selected nodes"),
1382 INKSCAPE_ICON_NODE_DELETE,
1383 secondarySize );
1384 g_object_set( inky, "short_label", _("Delete"), NULL );
1385 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1386 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1387 }
1389 {
1390 InkAction* inky = ink_action_new( "NodeJoinAction",
1391 _("Join nodes"),
1392 _("Join selected nodes"),
1393 INKSCAPE_ICON_NODE_JOIN,
1394 secondarySize );
1395 g_object_set( inky, "short_label", _("Join"), NULL );
1396 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1397 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1398 }
1400 {
1401 InkAction* inky = ink_action_new( "NodeBreakAction",
1402 _("Break nodes"),
1403 _("Break path at selected nodes"),
1404 INKSCAPE_ICON_NODE_BREAK,
1405 secondarySize );
1406 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1407 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1408 }
1411 {
1412 InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1413 _("Join with segment"),
1414 _("Join selected endnodes with a new segment"),
1415 INKSCAPE_ICON_NODE_JOIN_SEGMENT,
1416 secondarySize );
1417 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1418 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1419 }
1421 {
1422 InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1423 _("Delete segment"),
1424 _("Delete segment between two non-endpoint nodes"),
1425 INKSCAPE_ICON_NODE_DELETE_SEGMENT,
1426 secondarySize );
1427 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1428 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1429 }
1431 {
1432 InkAction* inky = ink_action_new( "NodeCuspAction",
1433 _("Node Cusp"),
1434 _("Make selected nodes corner"),
1435 INKSCAPE_ICON_NODE_TYPE_CUSP,
1436 secondarySize );
1437 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1438 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1439 }
1441 {
1442 InkAction* inky = ink_action_new( "NodeSmoothAction",
1443 _("Node Smooth"),
1444 _("Make selected nodes smooth"),
1445 INKSCAPE_ICON_NODE_TYPE_SMOOTH,
1446 secondarySize );
1447 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1448 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1449 }
1451 {
1452 InkAction* inky = ink_action_new( "NodeSymmetricAction",
1453 _("Node Symmetric"),
1454 _("Make selected nodes symmetric"),
1455 INKSCAPE_ICON_NODE_TYPE_SYMMETRIC,
1456 secondarySize );
1457 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1458 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1459 }
1461 {
1462 InkAction* inky = ink_action_new( "NodeAutoAction",
1463 _("Node Auto"),
1464 _("Make selected nodes auto-smooth"),
1465 INKSCAPE_ICON_NODE_TYPE_AUTO_SMOOTH,
1466 secondarySize );
1467 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_auto), 0 );
1468 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1469 }
1471 {
1472 InkAction* inky = ink_action_new( "NodeLineAction",
1473 _("Node Line"),
1474 _("Make selected segments lines"),
1475 INKSCAPE_ICON_NODE_SEGMENT_LINE,
1476 secondarySize );
1477 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1478 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1479 }
1481 {
1482 InkAction* inky = ink_action_new( "NodeCurveAction",
1483 _("Node Curve"),
1484 _("Make selected segments curves"),
1485 INKSCAPE_ICON_NODE_SEGMENT_CURVE,
1486 secondarySize );
1487 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1488 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1489 }
1491 {
1492 InkToggleAction* act = ink_toggle_action_new( "NodesShowTransformHandlesAction",
1493 _("Show Transform Handles"),
1494 _("Show node transformation handles"),
1495 "node-transform",
1496 secondarySize );
1497 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1498 PrefPusher *pusher = new PrefPusher(GTK_TOGGLE_ACTION(act), "/tools/nodes/show_transform_handles");
1499 g_signal_connect( holder, "destroy", G_CALLBACK(delete_prefspusher), pusher);
1500 }
1502 {
1503 InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1504 _("Show Handles"),
1505 _("Show the Bezier handles of selected nodes"),
1506 INKSCAPE_ICON_SHOW_NODE_HANDLES,
1507 secondarySize );
1508 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1509 PrefPusher *pusher = new PrefPusher(GTK_TOGGLE_ACTION(act), "/tools/nodes/show_handles");
1510 g_signal_connect( holder, "destroy", G_CALLBACK(delete_prefspusher), pusher);
1511 }
1513 {
1514 InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1515 _("Show Outline"),
1516 _("Show the outline of the path"),
1517 INKSCAPE_ICON_SHOW_PATH_OUTLINE,
1518 secondarySize );
1519 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1520 PrefPusher *pusher = new PrefPusher(GTK_TOGGLE_ACTION(act), "/tools/nodes/show_outline");
1521 g_signal_connect( holder, "destroy", G_CALLBACK(delete_prefspusher), pusher);
1522 }
1524 {
1525 InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1526 _("Next path effect parameter"),
1527 _("Show next path effect parameter for editing"),
1528 INKSCAPE_ICON_PATH_EFFECT_PARAMETER_NEXT,
1529 secondarySize );
1530 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1531 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1532 g_object_set_data( holder, "nodes_lpeedit", inky);
1533 }
1535 {
1536 InkToggleAction* inky = ink_toggle_action_new( "ObjectEditClipPathAction",
1537 _("Edit clipping paths"),
1538 _("Show editing controls for clipping paths of selected objects"),
1539 INKSCAPE_ICON_PATH_CLIP_EDIT,
1540 secondarySize );
1541 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1542 PrefPusher *pusher = new PrefPusher(GTK_TOGGLE_ACTION(inky), "/tools/nodes/edit_clipping_paths");
1543 g_signal_connect( holder, "destroy", G_CALLBACK(delete_prefspusher), pusher);
1544 }
1546 {
1547 InkToggleAction* inky = ink_toggle_action_new( "ObjectEditMaskPathAction",
1548 _("Edit masks"),
1549 _("Show editing controls for masks of selected objects"),
1550 INKSCAPE_ICON_PATH_MASK_EDIT,
1551 secondarySize );
1552 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1553 PrefPusher *pusher = new PrefPusher(GTK_TOGGLE_ACTION(inky), "/tools/nodes/edit_masks");
1554 g_signal_connect( holder, "destroy", G_CALLBACK(delete_prefspusher), pusher);
1555 }
1557 /* X coord of selected node(s) */
1558 {
1559 EgeAdjustmentAction* eact = 0;
1560 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1561 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1562 eact = create_adjustment_action( "NodeXAction",
1563 _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1564 "/tools/nodes/Xcoord", 0,
1565 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1566 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1567 labels, values, G_N_ELEMENTS(labels),
1568 sp_node_path_x_value_changed );
1569 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1570 g_object_set_data( holder, "nodes_x_action", eact );
1571 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1572 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1573 }
1575 /* Y coord of selected node(s) */
1576 {
1577 EgeAdjustmentAction* eact = 0;
1578 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1579 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1580 eact = create_adjustment_action( "NodeYAction",
1581 _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1582 "/tools/nodes/Ycoord", 0,
1583 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1584 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1585 labels, values, G_N_ELEMENTS(labels),
1586 sp_node_path_y_value_changed );
1587 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1588 g_object_set_data( holder, "nodes_y_action", eact );
1589 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1590 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1591 }
1593 // add the units menu
1594 {
1595 GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1596 gtk_action_group_add_action( mainActions, act );
1597 }
1600 sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1602 //watch selection
1603 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1605 sigc::connection *c_selection_changed =
1606 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1607 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1608 pool->add_connection ("selection-changed", c_selection_changed);
1610 sigc::connection *c_selection_modified =
1611 new sigc::connection (sp_desktop_selection (desktop)->connectModified
1612 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1613 pool->add_connection ("selection-modified", c_selection_modified);
1615 sigc::connection *c_subselection_changed =
1616 new sigc::connection (desktop->connectToolSubselectionChanged
1617 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1618 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1620 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1622 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1623 } // end of sp_node_toolbox_prep()
1626 //########################
1627 //## Zoom Toolbox ##
1628 //########################
1630 static void sp_zoom_toolbox_prep(SPDesktop * /*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1631 {
1632 // no custom GtkAction setup needed
1633 } // end of sp_zoom_toolbox_prep()
1635 void ToolboxFactory::setToolboxDesktop(GtkWidget *toolbox, SPDesktop *desktop)
1636 {
1637 sigc::connection *conn = static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1638 "event_context_connection"));
1640 BarId id = static_cast<BarId>( GPOINTER_TO_INT(g_object_get_data(G_OBJECT(toolbox), BAR_ID_KEY)) );
1642 SetupFunction setup_func = 0;
1643 UpdateFunction update_func = 0;
1645 switch (id) {
1646 case BAR_TOOL:
1647 setup_func = setup_tool_toolbox;
1648 update_func = update_tool_toolbox;
1649 break;
1651 case BAR_AUX:
1652 toolbox = gtk_bin_get_child(GTK_BIN(toolbox));
1653 setup_func = setup_aux_toolbox;
1654 update_func = update_aux_toolbox;
1655 break;
1657 case BAR_COMMANDS:
1658 setup_func = setup_commands_toolbox;
1659 update_func = update_commands_toolbox;
1660 break;
1662 case BAR_SNAP:
1663 setup_func = setup_snap_toolbox;
1664 update_func = updateSnapToolbox;
1665 break;
1666 default:
1667 g_warning("Unexpected toolbox id encountered.");
1668 }
1670 gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1671 SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1673 if (old_desktop) {
1674 GList *children, *iter;
1676 children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1677 for ( iter = children ; iter ; iter = iter->next ) {
1678 gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1679 }
1680 g_list_free(children);
1681 }
1683 g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1685 if (desktop && setup_func && update_func) {
1686 gtk_widget_set_sensitive(toolbox, TRUE);
1687 setup_func(toolbox, desktop);
1688 update_func(desktop, desktop->event_context, toolbox);
1689 *conn = desktop->connectEventContextChanged(sigc::bind (sigc::ptr_fun(update_func), toolbox));
1690 } else {
1691 gtk_widget_set_sensitive(toolbox, FALSE);
1692 }
1694 } // end of sp_toolbox_set_desktop()
1697 static void setupToolboxCommon( GtkWidget *toolbox,
1698 SPDesktop *desktop,
1699 gchar const *descr,
1700 gchar const* toolbarName,
1701 gchar const* sizePref )
1702 {
1703 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1704 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1706 GtkUIManager* mgr = gtk_ui_manager_new();
1707 GError* errVal = 0;
1709 GtkOrientation orientation = GTK_ORIENTATION_HORIZONTAL;
1711 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1712 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1714 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, toolbarName );
1715 if ( prefs->getBool("/toolbox/icononly", true) ) {
1716 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1717 }
1719 Inkscape::IconSize toolboxSize = ToolboxFactory::prefToSize(sizePref);
1720 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1722 if (GTK_IS_HANDLE_BOX(toolbox)) {
1723 // g_message("GRABBING ORIENTATION [%s]", toolbarName);
1724 GtkPositionType pos = gtk_handle_box_get_handle_position(GTK_HANDLE_BOX(toolbox));
1725 orientation = ((pos == GTK_POS_LEFT) || (pos == GTK_POS_RIGHT)) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
1726 } else {
1727 GtkPositionType pos = static_cast<GtkPositionType>(GPOINTER_TO_INT(g_object_get_data( G_OBJECT(toolbox), HANDLE_POS_MARK )));
1728 orientation = ((pos == GTK_POS_LEFT) || (pos == GTK_POS_RIGHT)) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
1729 }
1730 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), orientation);
1731 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1733 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1735 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1736 if ( child ) {
1737 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1738 }
1740 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1741 }
1743 #define noDUMP_DETAILS 1
1745 void ToolboxFactory::setOrientation(GtkWidget* toolbox, GtkOrientation orientation)
1746 {
1747 #if DUMP_DETAILS
1748 g_message("Set orientation for %p to be %d", toolbox, orientation);
1749 GType type = GTK_WIDGET_TYPE(toolbox);
1750 g_message(" [%s]", g_type_name(type));
1751 g_message(" %p", g_object_get_data(G_OBJECT(toolbox), BAR_ID_KEY));
1752 #endif
1754 GtkPositionType pos = (orientation == GTK_ORIENTATION_HORIZONTAL) ? GTK_POS_LEFT : GTK_POS_TOP;
1755 GtkHandleBox* handleBox = 0;
1757 if (GTK_IS_BIN(toolbox)) {
1758 #if DUMP_DETAILS
1759 g_message(" is a BIN");
1760 #endif // DUMP_DETAILS
1761 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1762 if (child) {
1763 #if DUMP_DETAILS
1764 GType type2 = GTK_WIDGET_TYPE(child);
1765 g_message(" child [%s]", g_type_name(type2));
1766 #endif // DUMP_DETAILS
1768 if (GTK_IS_BOX(child)) {
1769 #if DUMP_DETAILS
1770 g_message(" is a BOX");
1771 #endif // DUMP_DETAILS
1773 GList* children = gtk_container_get_children(GTK_CONTAINER(child));
1774 if (children) {
1775 for (GList* curr = children; curr; curr = g_list_next(curr)) {
1776 GtkWidget* child2 = GTK_WIDGET(curr->data);
1777 #if DUMP_DETAILS
1778 GType type3 = GTK_WIDGET_TYPE(child2);
1779 g_message(" child2 [%s]", g_type_name(type3));
1780 #endif // DUMP_DETAILS
1782 if (GTK_IS_CONTAINER(child2)) {
1783 GList* children2 = gtk_container_get_children(GTK_CONTAINER(child2));
1784 if (children2) {
1785 for (GList* curr2 = children2; curr2; curr2 = g_list_next(curr2)) {
1786 GtkWidget* child3 = GTK_WIDGET(curr2->data);
1787 #if DUMP_DETAILS
1788 GType type4 = GTK_WIDGET_TYPE(child3);
1789 g_message(" child3 [%s]", g_type_name(type4));
1790 #endif // DUMP_DETAILS
1791 if (GTK_IS_TOOLBAR(child3)) {
1792 GtkToolbar* childBar = GTK_TOOLBAR(child3);
1793 gtk_toolbar_set_orientation(childBar, orientation);
1794 }
1795 }
1796 g_list_free(children2);
1797 }
1798 }
1801 if (GTK_IS_TOOLBAR(child2)) {
1802 GtkToolbar* childBar = GTK_TOOLBAR(child2);
1803 gtk_toolbar_set_orientation(childBar, orientation);
1804 if (GTK_IS_HANDLE_BOX(toolbox)) {
1805 handleBox = GTK_HANDLE_BOX(toolbox);
1806 }
1807 } else {
1808 g_message("need to add dynamic switch");
1809 }
1810 }
1811 g_list_free(children);
1812 } else {
1813 // The call is being made before the toolbox proper has been setup.
1814 if (GTK_IS_HANDLE_BOX(toolbox)) {
1815 handleBox = GTK_HANDLE_BOX(toolbox);
1816 } else {
1817 g_object_set_data(G_OBJECT(toolbox), HANDLE_POS_MARK, GINT_TO_POINTER(pos));
1818 }
1819 }
1820 } else if (GTK_IS_TOOLBAR(child)) {
1821 GtkToolbar* toolbar = GTK_TOOLBAR(child);
1822 gtk_toolbar_set_orientation( toolbar, orientation );
1823 if (GTK_IS_HANDLE_BOX(toolbox)) {
1824 handleBox = GTK_HANDLE_BOX(toolbox);
1825 }
1826 }
1827 }
1828 }
1830 if (handleBox) {
1831 gtk_handle_box_set_handle_position(handleBox, pos);
1832 }
1833 }
1835 void setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1836 {
1837 gchar const * descr =
1838 "<ui>"
1839 " <toolbar name='ToolToolbar'>"
1840 " <toolitem action='ToolSelector' />"
1841 " <toolitem action='ToolNode' />"
1842 " <toolitem action='ToolTweak' />"
1843 " <toolitem action='ToolSpray' />"
1844 " <toolitem action='ToolZoom' />"
1845 " <toolitem action='ToolRect' />"
1846 " <toolitem action='Tool3DBox' />"
1847 " <toolitem action='ToolArc' />"
1848 " <toolitem action='ToolStar' />"
1849 " <toolitem action='ToolSpiral' />"
1850 " <toolitem action='ToolPencil' />"
1851 " <toolitem action='ToolPen' />"
1852 " <toolitem action='ToolCalligraphic' />"
1853 " <toolitem action='ToolEraser' />"
1854 // " <toolitem action='ToolLPETool' />"
1855 " <toolitem action='ToolPaintBucket' />"
1856 " <toolitem action='ToolText' />"
1857 " <toolitem action='ToolConnector' />"
1858 " <toolitem action='ToolGradient' />"
1859 " <toolitem action='ToolDropper' />"
1860 " </toolbar>"
1861 "</ui>";
1863 setupToolboxCommon( toolbox, desktop, descr,
1864 "/ui/ToolToolbar",
1865 "/toolbox/tools/small");
1866 }
1868 void update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget * /*toolbox*/ )
1869 {
1870 gchar const *const tname = ( eventcontext
1871 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1872 : NULL );
1873 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1875 for (int i = 0 ; tools[i].type_name ; i++ ) {
1876 Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1877 if ( act ) {
1878 bool setActive = tname && !strcmp(tname, tools[i].type_name);
1879 Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1880 if ( verbAct ) {
1881 verbAct->set_active(setActive);
1882 }
1883 }
1884 }
1885 }
1887 void setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1888 {
1889 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1890 GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1891 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1892 GtkUIManager* mgr = gtk_ui_manager_new();
1893 GError* errVal = 0;
1894 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1895 gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1897 std::map<std::string, GtkWidget*> dataHolders;
1899 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1900 if ( aux_toolboxes[i].prep_func ) {
1901 // converted to GtkActions and UIManager
1903 GtkWidget* kludge = gtk_toolbar_new();
1904 g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1905 g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1906 dataHolders[aux_toolboxes[i].type_name] = kludge;
1907 aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1908 } else {
1910 GtkWidget *sub_toolbox = 0;
1911 if (aux_toolboxes[i].create_func == NULL) {
1912 sub_toolbox = sp_empty_toolbox_new(desktop);
1913 } else {
1914 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1915 }
1917 gtk_size_group_add_widget( grouper, sub_toolbox );
1919 gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1920 g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1922 }
1923 }
1925 // Second pass to create toolbars *after* all GtkActions are created
1926 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1927 if ( aux_toolboxes[i].prep_func ) {
1928 // converted to GtkActions and UIManager
1930 GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1932 GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1933 gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1935 gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1936 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1937 g_free( tmp );
1938 tmp = 0;
1940 if ( prefs->getBool( "/toolbox/icononly", true) ) {
1941 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1942 }
1944 Inkscape::IconSize toolboxSize = ToolboxFactory::prefToSize("/toolbox/small");
1945 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1947 gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1949 if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1950 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1951 swatch->setDesktop( desktop );
1952 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1953 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1954 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1955 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 );
1956 }
1958 gtk_widget_show_all( holder );
1959 sp_set_font_size_smaller( holder );
1961 gtk_size_group_add_widget( grouper, holder );
1963 gtk_container_add( GTK_CONTAINER(toolbox), holder );
1964 g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1965 }
1966 }
1968 g_object_unref( G_OBJECT(grouper) );
1969 }
1971 void update_aux_toolbox(SPDesktop * /*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1972 {
1973 gchar const *tname = ( eventcontext
1974 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1975 : NULL );
1976 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1977 GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1978 if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1979 gtk_widget_show_all(sub_toolbox);
1980 g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1981 } else {
1982 gtk_widget_hide(sub_toolbox);
1983 }
1984 }
1985 }
1987 void setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1988 {
1989 gchar const * descr =
1990 "<ui>"
1991 " <toolbar name='CommandsToolbar'>"
1992 " <toolitem action='FileNew' />"
1993 " <toolitem action='FileOpen' />"
1994 " <toolitem action='FileSave' />"
1995 " <toolitem action='FilePrint' />"
1996 " <separator />"
1997 " <toolitem action='FileImport' />"
1998 " <toolitem action='FileExport' />"
1999 " <separator />"
2000 " <toolitem action='EditUndo' />"
2001 " <toolitem action='EditRedo' />"
2002 " <separator />"
2003 " <toolitem action='EditCopy' />"
2004 " <toolitem action='EditCut' />"
2005 " <toolitem action='EditPaste' />"
2006 " <separator />"
2007 " <toolitem action='ZoomSelection' />"
2008 " <toolitem action='ZoomDrawing' />"
2009 " <toolitem action='ZoomPage' />"
2010 " <separator />"
2011 " <toolitem action='EditDuplicate' />"
2012 " <toolitem action='EditClone' />"
2013 " <toolitem action='EditUnlinkClone' />"
2014 " <separator />"
2015 " <toolitem action='SelectionGroup' />"
2016 " <toolitem action='SelectionUnGroup' />"
2017 " <separator />"
2018 " <toolitem action='DialogFillStroke' />"
2019 " <toolitem action='DialogText' />"
2020 " <toolitem action='DialogLayers' />"
2021 " <toolitem action='DialogXMLEditor' />"
2022 " <toolitem action='DialogAlignDistribute' />"
2023 " <separator />"
2024 " <toolitem action='DialogPreferences' />"
2025 " <toolitem action='DialogDocumentProperties' />"
2026 #if ENABLE_TASK_SUPPORT
2027 " <separator />"
2028 " <toolitem action='TaskSetAction' />"
2029 #endif // ENABLE_TASK_SUPPORT
2030 " </toolbar>"
2031 "</ui>";
2033 setupToolboxCommon( toolbox, desktop, descr,
2034 "/ui/CommandsToolbar",
2035 "/toolbox/small" );
2036 }
2038 void update_commands_toolbox(SPDesktop * /*desktop*/, SPEventContext * /*eventcontext*/, GtkWidget * /*toolbox*/)
2039 {
2040 }
2042 static void toggle_snap_callback(GtkToggleAction *act, gpointer data) //data points to the toolbox
2043 {
2044 if (g_object_get_data(G_OBJECT(data), "freeze" )) {
2045 return;
2046 }
2048 gpointer ptr = g_object_get_data(G_OBJECT(data), "desktop");
2049 g_assert(ptr != NULL);
2051 SPDesktop *dt = reinterpret_cast<SPDesktop*>(ptr);
2052 SPNamedView *nv = sp_desktop_namedview(dt);
2053 SPDocument *doc = SP_OBJECT_DOCUMENT(nv);
2055 if (dt == NULL || nv == NULL) {
2056 g_warning("No desktop or namedview specified (in toggle_snap_callback)!");
2057 return;
2058 }
2060 Inkscape::XML::Node *repr = SP_OBJECT_REPR(nv);
2062 if (repr == NULL) {
2063 g_warning("This namedview doesn't have a xml representation attached!");
2064 return;
2065 }
2067 bool saved = sp_document_get_undo_sensitive(doc);
2068 sp_document_set_undo_sensitive(doc, false);
2070 bool v = false;
2071 SPAttributeEnum attr = (SPAttributeEnum) GPOINTER_TO_INT(g_object_get_data(G_OBJECT(act), "SP_ATTR_INKSCAPE"));
2073 switch (attr) {
2074 case SP_ATTR_INKSCAPE_SNAP_GLOBAL:
2075 dt->toggleSnapGlobal();
2076 break;
2077 case SP_ATTR_INKSCAPE_SNAP_BBOX:
2078 v = nv->snap_manager.snapprefs.getSnapModeBBox();
2079 sp_repr_set_boolean(repr, "inkscape:snap-bbox", !v);
2080 break;
2081 case SP_ATTR_INKSCAPE_BBOX_PATHS:
2082 v = nv->snap_manager.snapprefs.getSnapToBBoxPath();
2083 sp_repr_set_boolean(repr, "inkscape:bbox-paths", !v);
2084 break;
2085 case SP_ATTR_INKSCAPE_BBOX_NODES:
2086 v = nv->snap_manager.snapprefs.getSnapToBBoxNode();
2087 sp_repr_set_boolean(repr, "inkscape:bbox-nodes", !v);
2088 break;
2089 case SP_ATTR_INKSCAPE_SNAP_NODES:
2090 v = nv->snap_manager.snapprefs.getSnapModeNode();
2091 sp_repr_set_boolean(repr, "inkscape:snap-nodes", !v);
2092 break;
2093 case SP_ATTR_INKSCAPE_OBJECT_PATHS:
2094 v = nv->snap_manager.snapprefs.getSnapToItemPath();
2095 sp_repr_set_boolean(repr, "inkscape:object-paths", !v);
2096 break;
2097 case SP_ATTR_INKSCAPE_OBJECT_NODES:
2098 v = nv->snap_manager.snapprefs.getSnapToItemNode();
2099 sp_repr_set_boolean(repr, "inkscape:object-nodes", !v);
2100 break;
2101 case SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES:
2102 v = nv->snap_manager.snapprefs.getSnapSmoothNodes();
2103 sp_repr_set_boolean(repr, "inkscape:snap-smooth-nodes", !v);
2104 break;
2105 case SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS:
2106 v = nv->snap_manager.snapprefs.getSnapIntersectionCS();
2107 sp_repr_set_boolean(repr, "inkscape:snap-intersection-paths", !v);
2108 break;
2109 case SP_ATTR_INKSCAPE_SNAP_CENTER:
2110 v = nv->snap_manager.snapprefs.getIncludeItemCenter();
2111 sp_repr_set_boolean(repr, "inkscape:snap-center", !v);
2112 break;
2113 case SP_ATTR_INKSCAPE_SNAP_GRIDS:
2114 v = nv->snap_manager.snapprefs.getSnapToGrids();
2115 sp_repr_set_boolean(repr, "inkscape:snap-grids", !v);
2116 break;
2117 case SP_ATTR_INKSCAPE_SNAP_TO_GUIDES:
2118 v = nv->snap_manager.snapprefs.getSnapToGuides();
2119 sp_repr_set_boolean(repr, "inkscape:snap-to-guides", !v);
2120 break;
2121 case SP_ATTR_INKSCAPE_SNAP_PAGE:
2122 v = nv->snap_manager.snapprefs.getSnapToPageBorder();
2123 sp_repr_set_boolean(repr, "inkscape:snap-page", !v);
2124 break;
2125 /*case SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE:
2126 v = nv->snap_manager.snapprefs.getSnapIntersectionGG();
2127 sp_repr_set_boolean(repr, "inkscape:snap-intersection-grid-guide", !v);
2128 break;*/
2129 case SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS:
2130 v = nv->snap_manager.snapprefs.getSnapLineMidpoints();
2131 sp_repr_set_boolean(repr, "inkscape:snap-midpoints", !v);
2132 break;
2133 case SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS:
2134 v = nv->snap_manager.snapprefs.getSnapObjectMidpoints();
2135 sp_repr_set_boolean(repr, "inkscape:snap-object-midpoints", !v);
2136 break;
2137 case SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS:
2138 v = nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints();
2139 sp_repr_set_boolean(repr, "inkscape:snap-bbox-edge-midpoints", !v);
2140 break;
2141 case SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS:
2142 v = nv->snap_manager.snapprefs.getSnapBBoxMidpoints();
2143 sp_repr_set_boolean(repr, "inkscape:snap-bbox-midpoints", !v);
2144 break;
2145 default:
2146 g_warning("toggle_snap_callback has been called with an ID for which no action has been defined");
2147 break;
2148 }
2150 // The snapping preferences are stored in the document, and therefore toggling makes the document dirty
2151 doc->setModifiedSinceSave();
2153 sp_document_set_undo_sensitive(doc, saved);
2154 }
2156 void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
2157 {
2158 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2160 gchar const * descr =
2161 "<ui>"
2162 " <toolbar name='SnapToolbar'>"
2163 " <toolitem action='ToggleSnapGlobal' />"
2164 " <separator />"
2165 " <toolitem action='ToggleSnapFromBBoxCorner' />"
2166 " <toolitem action='ToggleSnapToBBoxPath' />"
2167 " <toolitem action='ToggleSnapToBBoxNode' />"
2168 " <toolitem action='ToggleSnapToFromBBoxEdgeMidpoints' />"
2169 " <toolitem action='ToggleSnapToFromBBoxCenters' />"
2170 " <separator />"
2171 " <toolitem action='ToggleSnapFromNode' />"
2172 " <toolitem action='ToggleSnapToItemPath' />"
2173 " <toolitem action='ToggleSnapToPathIntersections' />"
2174 " <toolitem action='ToggleSnapToItemNode' />"
2175 " <toolitem action='ToggleSnapToSmoothNodes' />"
2176 " <toolitem action='ToggleSnapToFromLineMidpoints' />"
2177 " <toolitem action='ToggleSnapToFromObjectCenters' />"
2178 " <toolitem action='ToggleSnapToFromRotationCenter' />"
2179 " <separator />"
2180 " <toolitem action='ToggleSnapToPageBorder' />"
2181 " <toolitem action='ToggleSnapToGrids' />"
2182 " <toolitem action='ToggleSnapToGuides' />"
2183 //" <toolitem action='ToggleSnapToGridGuideIntersections' />"
2184 " </toolbar>"
2185 "</ui>";
2187 Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
2189 {
2190 InkToggleAction* act = ink_toggle_action_new("ToggleSnapGlobal",
2191 _("Snap"), _("Enable snapping"), INKSCAPE_ICON_SNAP, secondarySize,
2192 SP_ATTR_INKSCAPE_SNAP_GLOBAL);
2194 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2195 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2196 }
2198 {
2199 InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromBBoxCorner",
2200 _("Bounding box"), _("Snap bounding box corners"), INKSCAPE_ICON_SNAP_BOUNDING_BOX,
2201 secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX);
2203 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2204 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2205 }
2207 {
2208 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxPath",
2209 _("Bounding box edges"), _("Snap to edges of a bounding box"),
2210 INKSCAPE_ICON_SNAP_BOUNDING_BOX_EDGES, secondarySize, SP_ATTR_INKSCAPE_BBOX_PATHS);
2212 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2213 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2214 }
2216 {
2217 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxNode",
2218 _("Bounding box corners"), _("Snap to bounding box corners"),
2219 INKSCAPE_ICON_SNAP_BOUNDING_BOX_CORNERS, secondarySize, SP_ATTR_INKSCAPE_BBOX_NODES);
2221 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2222 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2223 }
2225 {
2226 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxEdgeMidpoints",
2227 _("BBox Edge Midpoints"), _("Snap from and to midpoints of bounding box edges"),
2228 INKSCAPE_ICON_SNAP_BOUNDING_BOX_MIDPOINTS, secondarySize,
2229 SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS);
2231 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2232 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2233 }
2235 {
2236 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxCenters",
2237 _("BBox Centers"), _("Snapping from and to centers of bounding boxes"),
2238 INKSCAPE_ICON_SNAP_BOUNDING_BOX_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS);
2240 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2241 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2242 }
2244 {
2245 InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromNode",
2246 _("Nodes"), _("Snap nodes or handles"), INKSCAPE_ICON_SNAP_NODES, secondarySize, SP_ATTR_INKSCAPE_SNAP_NODES);
2248 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2249 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2250 }
2252 {
2253 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemPath",
2254 _("Paths"), _("Snap to paths"), INKSCAPE_ICON_SNAP_NODES_PATH, secondarySize,
2255 SP_ATTR_INKSCAPE_OBJECT_PATHS);
2257 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2258 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2259 }
2261 {
2262 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPathIntersections",
2263 _("Path intersections"), _("Snap to path intersections"),
2264 INKSCAPE_ICON_SNAP_NODES_INTERSECTION, secondarySize, SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS);
2266 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2267 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2268 }
2270 {
2271 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemNode",
2272 _("To nodes"), _("Snap to cusp nodes"), INKSCAPE_ICON_SNAP_NODES_CUSP, secondarySize,
2273 SP_ATTR_INKSCAPE_OBJECT_NODES);
2275 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2276 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2277 }
2279 {
2280 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToSmoothNodes",
2281 _("Smooth nodes"), _("Snap to smooth nodes"), INKSCAPE_ICON_SNAP_NODES_SMOOTH,
2282 secondarySize, SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES);
2284 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2285 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2286 }
2288 {
2289 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromLineMidpoints",
2290 _("Line Midpoints"), _("Snap from and to midpoints of line segments"),
2291 INKSCAPE_ICON_SNAP_NODES_MIDPOINT, secondarySize, SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS);
2293 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2294 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2295 }
2297 {
2298 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromObjectCenters",
2299 _("Object Centers"), _("Snap from and to centers of objects"),
2300 INKSCAPE_ICON_SNAP_NODES_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS);
2302 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2303 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2304 }
2306 {
2307 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromRotationCenter",
2308 _("Rotation Centers"), _("Snap from and to an item's rotation center"),
2309 INKSCAPE_ICON_SNAP_NODES_ROTATION_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_CENTER);
2311 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2312 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2313 }
2315 {
2316 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPageBorder",
2317 _("Page border"), _("Snap to the page border"), INKSCAPE_ICON_SNAP_PAGE,
2318 secondarySize, SP_ATTR_INKSCAPE_SNAP_PAGE);
2320 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2321 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2322 }
2324 {
2325 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGrids",
2326 _("Grids"), _("Snap to grids"), INKSCAPE_ICON_GRID_RECTANGULAR, secondarySize,
2327 SP_ATTR_INKSCAPE_SNAP_GRIDS);
2329 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2330 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2331 }
2333 {
2334 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGuides",
2335 _("Guides"), _("Snap to guides"), INKSCAPE_ICON_GUIDES, secondarySize,
2336 SP_ATTR_INKSCAPE_SNAP_TO_GUIDES);
2338 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2339 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2340 }
2342 /*{
2343 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGridGuideIntersections",
2344 _("Grid/guide intersections"), _("Snap to intersections of a grid with a guide"),
2345 INKSCAPE_ICON_SNAP_GRID_GUIDE_INTERSECTIONS, secondarySize,
2346 SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE);
2348 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2349 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2350 }*/
2352 setupToolboxCommon( toolbox, desktop, descr,
2353 "/ui/SnapToolbar",
2354 "/toolbox/secondary" );
2355 }
2357 Glib::ustring ToolboxFactory::getToolboxName(GtkWidget* toolbox)
2358 {
2359 Glib::ustring name;
2360 BarId id = static_cast<BarId>( GPOINTER_TO_INT(g_object_get_data(G_OBJECT(toolbox), BAR_ID_KEY)) );
2361 switch(id) {
2362 case BAR_TOOL:
2363 name = "ToolToolbar";
2364 break;
2365 case BAR_AUX:
2366 name = "AuxToolbar";
2367 break;
2368 case BAR_COMMANDS:
2369 name = "CommandsToolbar";
2370 break;
2371 case BAR_SNAP:
2372 name = "SnapToolbar";
2373 break;
2374 }
2376 return name;
2377 }
2379 void ToolboxFactory::updateSnapToolbox(SPDesktop *desktop, SPEventContext * /*eventcontext*/, GtkWidget *toolbox)
2380 {
2381 g_assert(desktop != NULL);
2382 g_assert(toolbox != NULL);
2384 SPNamedView *nv = sp_desktop_namedview(desktop);
2385 if (nv == NULL) {
2386 g_warning("Namedview cannot be retrieved (in updateSnapToolbox)!");
2387 return;
2388 }
2390 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2392 Glib::RefPtr<Gtk::Action> act1 = mainActions->get_action("ToggleSnapGlobal");
2393 Glib::RefPtr<Gtk::Action> act2 = mainActions->get_action("ToggleSnapFromBBoxCorner");
2394 Glib::RefPtr<Gtk::Action> act3 = mainActions->get_action("ToggleSnapToBBoxPath");
2395 Glib::RefPtr<Gtk::Action> act4 = mainActions->get_action("ToggleSnapToBBoxNode");
2396 Glib::RefPtr<Gtk::Action> act4b = mainActions->get_action("ToggleSnapToFromBBoxEdgeMidpoints");
2397 Glib::RefPtr<Gtk::Action> act4c = mainActions->get_action("ToggleSnapToFromBBoxCenters");
2398 Glib::RefPtr<Gtk::Action> act5 = mainActions->get_action("ToggleSnapFromNode");
2399 Glib::RefPtr<Gtk::Action> act6 = mainActions->get_action("ToggleSnapToItemPath");
2400 Glib::RefPtr<Gtk::Action> act6b = mainActions->get_action("ToggleSnapToPathIntersections");
2401 Glib::RefPtr<Gtk::Action> act7 = mainActions->get_action("ToggleSnapToItemNode");
2402 Glib::RefPtr<Gtk::Action> act8 = mainActions->get_action("ToggleSnapToSmoothNodes");
2403 Glib::RefPtr<Gtk::Action> act9 = mainActions->get_action("ToggleSnapToFromLineMidpoints");
2404 Glib::RefPtr<Gtk::Action> act10 = mainActions->get_action("ToggleSnapToFromObjectCenters");
2405 Glib::RefPtr<Gtk::Action> act11 = mainActions->get_action("ToggleSnapToFromRotationCenter");
2406 Glib::RefPtr<Gtk::Action> act12 = mainActions->get_action("ToggleSnapToPageBorder");
2407 //Glib::RefPtr<Gtk::Action> act13 = mainActions->get_action("ToggleSnapToGridGuideIntersections");
2408 Glib::RefPtr<Gtk::Action> act14 = mainActions->get_action("ToggleSnapToGrids");
2409 Glib::RefPtr<Gtk::Action> act15 = mainActions->get_action("ToggleSnapToGuides");
2412 if (!act1) {
2413 return; // The snap actions haven't been defined yet (might be the case during startup)
2414 }
2416 // The ..._set_active calls below will toggle the buttons, but this shouldn't lead to
2417 // changes in our document because we're only updating the UI;
2418 // Setting the "freeze" parameter to true will block the code in toggle_snap_callback()
2419 g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(TRUE));
2421 bool const c1 = nv->snap_manager.snapprefs.getSnapEnabledGlobally();
2422 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act1->gobj()), c1);
2424 bool const c2 = nv->snap_manager.snapprefs.getSnapModeBBox();
2425 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act2->gobj()), c2);
2426 gtk_action_set_sensitive(GTK_ACTION(act2->gobj()), c1);
2428 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act3->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxPath());
2429 gtk_action_set_sensitive(GTK_ACTION(act3->gobj()), c1 && c2);
2430 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxNode());
2431 gtk_action_set_sensitive(GTK_ACTION(act4->gobj()), c1 && c2);
2432 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4b->gobj()), nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints());
2433 gtk_action_set_sensitive(GTK_ACTION(act4b->gobj()), c1 && c2);
2434 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4c->gobj()), nv->snap_manager.snapprefs.getSnapBBoxMidpoints());
2435 gtk_action_set_sensitive(GTK_ACTION(act4c->gobj()), c1 && c2);
2437 bool const c3 = nv->snap_manager.snapprefs.getSnapModeNode();
2438 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act5->gobj()), c3);
2439 gtk_action_set_sensitive(GTK_ACTION(act5->gobj()), c1);
2441 bool const c4 = nv->snap_manager.snapprefs.getSnapToItemPath();
2442 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6->gobj()), c4);
2443 gtk_action_set_sensitive(GTK_ACTION(act6->gobj()), c1 && c3);
2444 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6b->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionCS());
2445 gtk_action_set_sensitive(GTK_ACTION(act6b->gobj()), c1 && c3 && c4);
2446 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act7->gobj()), nv->snap_manager.snapprefs.getSnapToItemNode());
2447 gtk_action_set_sensitive(GTK_ACTION(act7->gobj()), c1 && c3);
2448 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act8->gobj()), nv->snap_manager.snapprefs.getSnapSmoothNodes());
2449 gtk_action_set_sensitive(GTK_ACTION(act8->gobj()), c1 && c3);
2450 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act9->gobj()), nv->snap_manager.snapprefs.getSnapLineMidpoints());
2451 gtk_action_set_sensitive(GTK_ACTION(act9->gobj()), c1 && c3);
2452 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act10->gobj()), nv->snap_manager.snapprefs.getSnapObjectMidpoints());
2453 gtk_action_set_sensitive(GTK_ACTION(act10->gobj()), c1 && c3);
2454 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act11->gobj()), nv->snap_manager.snapprefs.getIncludeItemCenter());
2455 gtk_action_set_sensitive(GTK_ACTION(act11->gobj()), c1 && c3);
2457 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act12->gobj()), nv->snap_manager.snapprefs.getSnapToPageBorder());
2458 gtk_action_set_sensitive(GTK_ACTION(act12->gobj()), c1);
2459 //gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act13->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionGG());
2460 //gtk_action_set_sensitive(GTK_ACTION(act13->gobj()), c1);
2462 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act14->gobj()), nv->snap_manager.snapprefs.getSnapToGrids());
2463 gtk_action_set_sensitive(GTK_ACTION(act14->gobj()), c1);
2464 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act15->gobj()), nv->snap_manager.snapprefs.getSnapToGuides());
2465 gtk_action_set_sensitive(GTK_ACTION(act15->gobj()), c1);
2468 g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(FALSE)); // unfreeze (see above)
2469 }
2471 void ToolboxFactory::showAuxToolbox(GtkWidget *toolbox_toplevel)
2472 {
2473 gtk_widget_show(toolbox_toplevel);
2474 GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
2476 GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
2477 if (!shown_toolbox) {
2478 return;
2479 }
2480 gtk_widget_show(toolbox);
2482 gtk_widget_show_all(shown_toolbox);
2483 }
2485 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop)
2486 {
2487 GtkWidget *tbl = gtk_toolbar_new();
2488 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
2489 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
2491 gtk_widget_show_all(tbl);
2492 sp_set_font_size_smaller (tbl);
2494 return tbl;
2495 }
2497 #define MODE_LABEL_WIDTH 70
2499 //########################
2500 //## Star ##
2501 //########################
2503 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2504 {
2505 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2507 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2508 // do not remember prefs if this call is initiated by an undo change, because undoing object
2509 // creation sets bogus values to its attributes before it is deleted
2510 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2511 prefs->setInt("/tools/shapes/star/magnitude", (gint)adj->value);
2512 }
2514 // quit if run by the attr_changed listener
2515 if (g_object_get_data( dataKludge, "freeze" )) {
2516 return;
2517 }
2519 // in turn, prevent listener from responding
2520 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2522 bool modmade = false;
2524 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2525 GSList const *items = selection->itemList();
2526 for (; items != NULL; items = items->next) {
2527 if (SP_IS_STAR((SPItem *) items->data)) {
2528 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2529 sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
2530 sp_repr_set_svg_double(repr, "sodipodi:arg2",
2531 (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
2532 + M_PI / (gint)adj->value));
2533 SP_OBJECT((SPItem *) items->data)->updateRepr();
2534 modmade = true;
2535 }
2536 }
2537 if (modmade) {
2538 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2539 _("Star: Change number of corners"));
2540 }
2542 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2543 }
2545 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2546 {
2547 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2549 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2550 if (!IS_NAN(adj->value)) {
2551 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2552 prefs->setDouble("/tools/shapes/star/proportion", adj->value);
2553 }
2554 }
2556 // quit if run by the attr_changed listener
2557 if (g_object_get_data( dataKludge, "freeze" )) {
2558 return;
2559 }
2561 // in turn, prevent listener from responding
2562 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2564 bool modmade = false;
2565 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2566 GSList const *items = selection->itemList();
2567 for (; items != NULL; items = items->next) {
2568 if (SP_IS_STAR((SPItem *) items->data)) {
2569 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2571 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2572 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2573 if (r2 < r1) {
2574 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
2575 } else {
2576 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
2577 }
2579 SP_OBJECT((SPItem *) items->data)->updateRepr();
2580 modmade = true;
2581 }
2582 }
2584 if (modmade) {
2585 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2586 _("Star: Change spoke ratio"));
2587 }
2589 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2590 }
2592 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
2593 {
2594 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2595 bool flat = ege_select_one_action_get_active( act ) == 0;
2597 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2598 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2599 prefs->setBool( "/tools/shapes/star/isflatsided", flat);
2600 }
2602 // quit if run by the attr_changed listener
2603 if (g_object_get_data( dataKludge, "freeze" )) {
2604 return;
2605 }
2607 // in turn, prevent listener from responding
2608 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2610 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2611 GSList const *items = selection->itemList();
2612 GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2613 bool modmade = false;
2615 if ( prop_action ) {
2616 gtk_action_set_sensitive( prop_action, !flat );
2617 }
2619 for (; items != NULL; items = items->next) {
2620 if (SP_IS_STAR((SPItem *) items->data)) {
2621 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2622 repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
2623 SP_OBJECT((SPItem *) items->data)->updateRepr();
2624 modmade = true;
2625 }
2626 }
2628 if (modmade) {
2629 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2630 flat ? _("Make polygon") : _("Make star"));
2631 }
2633 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2634 }
2636 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2637 {
2638 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2640 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2641 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2642 prefs->setDouble("/tools/shapes/star/rounded", (gdouble) adj->value);
2643 }
2645 // quit if run by the attr_changed listener
2646 if (g_object_get_data( dataKludge, "freeze" )) {
2647 return;
2648 }
2650 // in turn, prevent listener from responding
2651 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2653 bool modmade = false;
2655 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2656 GSList const *items = selection->itemList();
2657 for (; items != NULL; items = items->next) {
2658 if (SP_IS_STAR((SPItem *) items->data)) {
2659 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2660 sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2661 SP_OBJECT(items->data)->updateRepr();
2662 modmade = true;
2663 }
2664 }
2665 if (modmade) {
2666 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2667 _("Star: Change rounding"));
2668 }
2670 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2671 }
2673 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2674 {
2675 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2677 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2678 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2679 prefs->setDouble("/tools/shapes/star/randomized", (gdouble) adj->value);
2680 }
2682 // quit if run by the attr_changed listener
2683 if (g_object_get_data( dataKludge, "freeze" )) {
2684 return;
2685 }
2687 // in turn, prevent listener from responding
2688 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2690 bool modmade = false;
2692 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2693 GSList const *items = selection->itemList();
2694 for (; items != NULL; items = items->next) {
2695 if (SP_IS_STAR((SPItem *) items->data)) {
2696 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2697 sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2698 SP_OBJECT(items->data)->updateRepr();
2699 modmade = true;
2700 }
2701 }
2702 if (modmade) {
2703 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2704 _("Star: Change randomization"));
2705 }
2707 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2708 }
2711 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2712 gchar const * /*old_value*/, gchar const * /*new_value*/,
2713 bool /*is_interactive*/, gpointer data)
2714 {
2715 GtkWidget *tbl = GTK_WIDGET(data);
2717 // quit if run by the _changed callbacks
2718 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2719 return;
2720 }
2722 // in turn, prevent callbacks from responding
2723 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2725 GtkAdjustment *adj = 0;
2727 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2728 bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2730 if (!strcmp(name, "inkscape:randomized")) {
2731 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2732 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2733 } else if (!strcmp(name, "inkscape:rounded")) {
2734 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2735 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2736 } else if (!strcmp(name, "inkscape:flatsided")) {
2737 GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2738 char const *flatsides = repr->attribute("inkscape:flatsided");
2739 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2740 if ( flatsides && !strcmp(flatsides,"false") ) {
2741 ege_select_one_action_set_active( flat_action, 1 );
2742 gtk_action_set_sensitive( prop_action, TRUE );
2743 } else {
2744 ege_select_one_action_set_active( flat_action, 0 );
2745 gtk_action_set_sensitive( prop_action, FALSE );
2746 }
2747 } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2748 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2749 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2750 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2751 if (r2 < r1) {
2752 gtk_adjustment_set_value(adj, r2/r1);
2753 } else {
2754 gtk_adjustment_set_value(adj, r1/r2);
2755 }
2756 } else if (!strcmp(name, "sodipodi:sides")) {
2757 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2758 gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2759 }
2761 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2762 }
2765 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2766 {
2767 NULL, /* child_added */
2768 NULL, /* child_removed */
2769 star_tb_event_attr_changed,
2770 NULL, /* content_changed */
2771 NULL /* order_changed */
2772 };
2775 /**
2776 * \param selection Should not be NULL.
2777 */
2778 static void
2779 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2780 {
2781 int n_selected = 0;
2782 Inkscape::XML::Node *repr = NULL;
2784 purge_repr_listener( tbl, tbl );
2786 for (GSList const *items = selection->itemList();
2787 items != NULL;
2788 items = items->next)
2789 {
2790 if (SP_IS_STAR((SPItem *) items->data)) {
2791 n_selected++;
2792 repr = SP_OBJECT_REPR((SPItem *) items->data);
2793 }
2794 }
2796 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2798 if (n_selected == 0) {
2799 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2800 } else if (n_selected == 1) {
2801 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2803 if (repr) {
2804 g_object_set_data( tbl, "repr", repr );
2805 Inkscape::GC::anchor(repr);
2806 sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2807 sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2808 }
2809 } else {
2810 // FIXME: implement averaging of all parameters for multiple selected stars
2811 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2812 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2813 }
2814 }
2817 static void sp_stb_defaults( GtkWidget * /*widget*/, GObject *dataKludge )
2818 {
2819 // FIXME: in this and all other _default functions, set some flag telling the value_changed
2820 // callbacks to lump all the changes for all selected objects in one undo step
2822 GtkAdjustment *adj = 0;
2824 // fixme: make settable in prefs!
2825 gint mag = 5;
2826 gdouble prop = 0.5;
2827 gboolean flat = FALSE;
2828 gdouble randomized = 0;
2829 gdouble rounded = 0;
2831 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2832 ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2834 GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2835 gtk_action_set_sensitive( sb2, !flat );
2837 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2838 gtk_adjustment_set_value(adj, mag);
2839 gtk_adjustment_value_changed(adj);
2841 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2842 gtk_adjustment_set_value(adj, prop);
2843 gtk_adjustment_value_changed(adj);
2845 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2846 gtk_adjustment_set_value(adj, rounded);
2847 gtk_adjustment_value_changed(adj);
2849 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2850 gtk_adjustment_set_value(adj, randomized);
2851 gtk_adjustment_value_changed(adj);
2852 }
2855 // public:
2856 void sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2857 {
2858 GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2859 if (wide) {
2860 gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2861 }
2862 GtkWidget *l = gtk_label_new(NULL);
2863 gtk_label_set_markup(GTK_LABEL(l), title);
2864 gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2865 if ( GTK_IS_TOOLBAR(tbl) ) {
2866 gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2867 } else {
2868 gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2869 }
2870 gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2871 }
2874 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2875 {
2876 Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
2878 {
2879 EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2880 ege_output_action_set_use_markup( act, TRUE );
2881 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2882 g_object_set_data( holder, "mode_action", act );
2883 }
2885 {
2886 EgeAdjustmentAction* eact = 0;
2887 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2888 bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2890 /* Flatsided checkbox */
2891 {
2892 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2894 GtkTreeIter iter;
2895 gtk_list_store_append( model, &iter );
2896 gtk_list_store_set( model, &iter,
2897 0, _("Polygon"),
2898 1, _("Regular polygon (with one handle) instead of a star"),
2899 2, INKSCAPE_ICON_DRAW_POLYGON,
2900 -1 );
2902 gtk_list_store_append( model, &iter );
2903 gtk_list_store_set( model, &iter,
2904 0, _("Star"),
2905 1, _("Star instead of a regular polygon (with one handle)"),
2906 2, INKSCAPE_ICON_DRAW_STAR,
2907 -1 );
2909 EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2910 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2911 g_object_set_data( holder, "flat_action", act );
2913 ege_select_one_action_set_appearance( act, "full" );
2914 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2915 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2916 ege_select_one_action_set_icon_column( act, 2 );
2917 ege_select_one_action_set_icon_size( act, secondarySize );
2918 ege_select_one_action_set_tooltip_column( act, 1 );
2920 ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2921 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2922 }
2924 /* Magnitude */
2925 {
2926 gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2927 gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2928 eact = create_adjustment_action( "MagnitudeAction",
2929 _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2930 "/tools/shapes/star/magnitude", 3,
2931 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2932 3, 1024, 1, 5,
2933 labels, values, G_N_ELEMENTS(labels),
2934 sp_stb_magnitude_value_changed,
2935 1.0, 0 );
2936 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2937 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2938 }
2940 /* Spoke ratio */
2941 {
2942 gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2943 gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2944 eact = create_adjustment_action( "SpokeAction",
2945 _("Spoke ratio"), _("Spoke ratio:"),
2946 // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2947 // Base radius is the same for the closest handle.
2948 _("Base radius to tip radius ratio"),
2949 "/tools/shapes/star/proportion", 0.5,
2950 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2951 0.01, 1.0, 0.01, 0.1,
2952 labels, values, G_N_ELEMENTS(labels),
2953 sp_stb_proportion_value_changed );
2954 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2955 g_object_set_data( holder, "prop_action", eact );
2956 }
2958 if ( !isFlatSided ) {
2959 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2960 } else {
2961 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2962 }
2964 /* Roundedness */
2965 {
2966 gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2967 gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2968 eact = create_adjustment_action( "RoundednessAction",
2969 _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2970 "/tools/shapes/star/rounded", 0.0,
2971 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2972 -10.0, 10.0, 0.01, 0.1,
2973 labels, values, G_N_ELEMENTS(labels),
2974 sp_stb_rounded_value_changed );
2975 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2976 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2977 }
2979 /* Randomization */
2980 {
2981 gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2982 gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2983 eact = create_adjustment_action( "RandomizationAction",
2984 _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2985 "/tools/shapes/star/randomized", 0.0,
2986 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2987 -10.0, 10.0, 0.001, 0.01,
2988 labels, values, G_N_ELEMENTS(labels),
2989 sp_stb_randomized_value_changed, 0.1, 3 );
2990 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2991 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2992 }
2993 }
2995 {
2996 /* Reset */
2997 {
2998 GtkAction* act = gtk_action_new( "StarResetAction",
2999 _("Defaults"),
3000 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3001 GTK_STOCK_CLEAR );
3002 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
3003 gtk_action_group_add_action( mainActions, act );
3004 gtk_action_set_sensitive( act, TRUE );
3005 }
3006 }
3008 sigc::connection *connection = new sigc::connection(
3009 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
3010 );
3011 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3012 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3013 }
3016 //########################
3017 //## Rect ##
3018 //########################
3020 static void sp_rtb_sensitivize( GObject *tbl )
3021 {
3022 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
3023 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
3024 GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
3026 if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
3027 gtk_action_set_sensitive( not_rounded, FALSE );
3028 } else {
3029 gtk_action_set_sensitive( not_rounded, TRUE );
3030 }
3031 }
3034 static void sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
3035 void (*setter)(SPRect *, gdouble))
3036 {
3037 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3039 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
3040 SPUnit const *unit = tracker->getActiveUnit();
3042 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3043 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3044 prefs->setDouble(Glib::ustring("/tools/shapes/rect/") + value_name, sp_units_get_pixels(adj->value, *unit));
3045 }
3047 // quit if run by the attr_changed listener
3048 if (g_object_get_data( tbl, "freeze" )) {
3049 return;
3050 }
3052 // in turn, prevent listener from responding
3053 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
3055 bool modmade = false;
3056 Inkscape::Selection *selection = sp_desktop_selection(desktop);
3057 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
3058 if (SP_IS_RECT(items->data)) {
3059 if (adj->value != 0) {
3060 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
3061 } else {
3062 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
3063 }
3064 modmade = true;
3065 }
3066 }
3068 sp_rtb_sensitivize( tbl );
3070 if (modmade) {
3071 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
3072 _("Change rectangle"));
3073 }
3075 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3076 }
3078 static void sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
3079 {
3080 sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
3081 }
3083 static void sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
3084 {
3085 sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
3086 }
3088 static void sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
3089 {
3090 sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
3091 }
3093 static void sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
3094 {
3095 sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
3096 }
3100 static void sp_rtb_defaults( GtkWidget * /*widget*/, GObject *obj)
3101 {
3102 GtkAdjustment *adj = 0;
3104 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
3105 gtk_adjustment_set_value(adj, 0.0);
3106 // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
3107 gtk_adjustment_value_changed(adj);
3109 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
3110 gtk_adjustment_set_value(adj, 0.0);
3111 gtk_adjustment_value_changed(adj);
3113 sp_rtb_sensitivize( obj );
3114 }
3116 static void rect_tb_event_attr_changed(Inkscape::XML::Node * /*repr*/, gchar const * /*name*/,
3117 gchar const * /*old_value*/, gchar const * /*new_value*/,
3118 bool /*is_interactive*/, gpointer data)
3119 {
3120 GObject *tbl = G_OBJECT(data);
3122 // quit if run by the _changed callbacks
3123 if (g_object_get_data( tbl, "freeze" )) {
3124 return;
3125 }
3127 // in turn, prevent callbacks from responding
3128 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3130 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
3131 SPUnit const *unit = tracker->getActiveUnit();
3133 gpointer item = g_object_get_data( tbl, "item" );
3134 if (item && SP_IS_RECT(item)) {
3135 {
3136 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
3137 gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
3138 gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
3139 }
3141 {
3142 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
3143 gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
3144 gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
3145 }
3147 {
3148 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
3149 gdouble width = sp_rect_get_visible_width (SP_RECT(item));
3150 gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
3151 }
3153 {
3154 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
3155 gdouble height = sp_rect_get_visible_height (SP_RECT(item));
3156 gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
3157 }
3158 }
3160 sp_rtb_sensitivize( tbl );
3162 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3163 }
3166 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
3167 NULL, /* child_added */
3168 NULL, /* child_removed */
3169 rect_tb_event_attr_changed,
3170 NULL, /* content_changed */
3171 NULL /* order_changed */
3172 };
3174 /**
3175 * \param selection should not be NULL.
3176 */
3177 static void sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3178 {
3179 int n_selected = 0;
3180 Inkscape::XML::Node *repr = NULL;
3181 SPItem *item = NULL;
3183 if ( g_object_get_data( tbl, "repr" ) ) {
3184 g_object_set_data( tbl, "item", NULL );
3185 }
3186 purge_repr_listener( tbl, tbl );
3188 for (GSList const *items = selection->itemList();
3189 items != NULL;
3190 items = items->next) {
3191 if (SP_IS_RECT((SPItem *) items->data)) {
3192 n_selected++;
3193 item = (SPItem *) items->data;
3194 repr = SP_OBJECT_REPR(item);
3195 }
3196 }
3198 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3200 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3202 if (n_selected == 0) {
3203 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3205 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3206 gtk_action_set_sensitive(w, FALSE);
3207 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3208 gtk_action_set_sensitive(h, FALSE);
3210 } else if (n_selected == 1) {
3211 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3212 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3214 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3215 gtk_action_set_sensitive(w, TRUE);
3216 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3217 gtk_action_set_sensitive(h, TRUE);
3219 if (repr) {
3220 g_object_set_data( tbl, "repr", repr );
3221 g_object_set_data( tbl, "item", item );
3222 Inkscape::GC::anchor(repr);
3223 sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
3224 sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
3225 }
3226 } else {
3227 // FIXME: implement averaging of all parameters for multiple selected
3228 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3229 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3230 sp_rtb_sensitivize( tbl );
3231 }
3232 }
3235 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3236 {
3237 EgeAdjustmentAction* eact = 0;
3238 Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
3240 {
3241 EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
3242 ege_output_action_set_use_markup( act, TRUE );
3243 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3244 g_object_set_data( holder, "mode_action", act );
3245 }
3247 // rx/ry units menu: create
3248 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
3249 //tracker->addUnit( SP_UNIT_PERCENT, 0 );
3250 // fixme: add % meaning per cent of the width/height
3251 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
3252 g_object_set_data( holder, "tracker", tracker );
3254 /* W */
3255 {
3256 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3257 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3258 eact = create_adjustment_action( "RectWidthAction",
3259 _("Width"), _("W:"), _("Width of rectangle"),
3260 "/tools/shapes/rect/width", 0,
3261 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
3262 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3263 labels, values, G_N_ELEMENTS(labels),
3264 sp_rtb_width_value_changed );
3265 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3266 g_object_set_data( holder, "width_action", eact );
3267 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3268 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3269 }
3271 /* H */
3272 {
3273 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3274 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3275 eact = create_adjustment_action( "RectHeightAction",
3276 _("Height"), _("H:"), _("Height of rectangle"),
3277 "/tools/shapes/rect/height", 0,
3278 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3279 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3280 labels, values, G_N_ELEMENTS(labels),
3281 sp_rtb_height_value_changed );
3282 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3283 g_object_set_data( holder, "height_action", eact );
3284 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3285 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3286 }
3288 /* rx */
3289 {
3290 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3291 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3292 eact = create_adjustment_action( "RadiusXAction",
3293 _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
3294 "/tools/shapes/rect/rx", 0,
3295 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3296 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3297 labels, values, G_N_ELEMENTS(labels),
3298 sp_rtb_rx_value_changed);
3299 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3300 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3301 }
3303 /* ry */
3304 {
3305 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3306 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3307 eact = create_adjustment_action( "RadiusYAction",
3308 _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
3309 "/tools/shapes/rect/ry", 0,
3310 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3311 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3312 labels, values, G_N_ELEMENTS(labels),
3313 sp_rtb_ry_value_changed);
3314 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3315 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3316 }
3318 // add the units menu
3319 {
3320 GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
3321 gtk_action_group_add_action( mainActions, act );
3322 }
3324 /* Reset */
3325 {
3326 InkAction* inky = ink_action_new( "RectResetAction",
3327 _("Not rounded"),
3328 _("Make corners sharp"),
3329 INKSCAPE_ICON_RECTANGLE_MAKE_CORNERS_SHARP,
3330 secondarySize );
3331 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
3332 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3333 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3334 g_object_set_data( holder, "not_rounded", inky );
3335 }
3337 g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
3338 sp_rtb_sensitivize( holder );
3340 sigc::connection *connection = new sigc::connection(
3341 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
3342 );
3343 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3344 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3345 }
3347 //########################
3348 //## 3D Box ##
3349 //########################
3351 // normalize angle so that it lies in the interval [0,360]
3352 static double box3d_normalize_angle (double a) {
3353 double angle = a + ((int) (a/360.0))*360;
3354 if (angle < 0) {
3355 angle += 360.0;
3356 }
3357 return angle;
3358 }
3360 static void box3d_set_button_and_adjustment(Persp3D *persp,
3361 Proj::Axis axis,
3362 GtkAdjustment *adj,
3363 GtkAction *act,
3364 GtkToggleAction *tact)
3365 {
3366 // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
3367 // have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
3368 // are reset).
3369 bool is_infinite = !persp3d_VP_is_finite(persp->perspective_impl, axis);
3371 if (is_infinite) {
3372 gtk_toggle_action_set_active(tact, TRUE);
3373 gtk_action_set_sensitive(act, TRUE);
3375 double angle = persp3d_get_infinite_angle(persp, axis);
3376 if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
3377 gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
3378 }
3379 } else {
3380 gtk_toggle_action_set_active(tact, FALSE);
3381 gtk_action_set_sensitive(act, FALSE);
3382 }
3383 }
3385 static void box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data)
3386 {
3387 if (!persp_repr) {
3388 g_print ("No perspective given to box3d_resync_toolbar().\n");
3389 return;
3390 }
3392 GtkWidget *tbl = GTK_WIDGET(data);
3393 GtkAdjustment *adj = 0;
3394 GtkAction *act = 0;
3395 GtkToggleAction *tact = 0;
3396 Persp3D *persp = persp3d_get_from_repr(persp_repr);
3397 if (!persp) {
3398 // Hmm, is it an error if this happens?
3399 return;
3400 }
3401 {
3402 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
3403 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
3404 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
3406 box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
3407 }
3408 {
3409 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
3410 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
3411 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
3413 box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
3414 }
3415 {
3416 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
3417 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
3418 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
3420 box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
3421 }
3422 }
3424 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr,
3425 gchar const * /*name*/,
3426 gchar const * /*old_value*/,
3427 gchar const * /*new_value*/,
3428 bool /*is_interactive*/,
3429 gpointer data)
3430 {
3431 GtkWidget *tbl = GTK_WIDGET(data);
3433 // quit if run by the attr_changed or selection changed listener
3434 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3435 return;
3436 }
3438 // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
3439 // sp_document_maybe_done() when the document is undo insensitive)
3440 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3442 // TODO: Only update the appropriate part of the toolbar
3443 // if (!strcmp(name, "inkscape:vp_z")) {
3444 box3d_resync_toolbar(repr, G_OBJECT(tbl));
3445 // }
3447 Persp3D *persp = persp3d_get_from_repr(repr);
3448 persp3d_update_box_reprs(persp);
3450 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3451 }
3453 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
3454 {
3455 NULL, /* child_added */
3456 NULL, /* child_removed */
3457 box3d_persp_tb_event_attr_changed,
3458 NULL, /* content_changed */
3459 NULL /* order_changed */
3460 };
3462 /**
3463 * \param selection Should not be NULL.
3464 */
3465 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
3466 // Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
3467 static void box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3468 {
3469 // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
3470 // disable the angle entry fields for this direction (otherwise entering a value in them should only
3471 // update the perspectives with infinite VPs and leave the other ones untouched).
3473 Inkscape::XML::Node *persp_repr = NULL;
3474 purge_repr_listener(tbl, tbl);
3476 SPItem *item = selection->singleItem();
3477 if (item && SP_IS_BOX3D(item)) {
3478 // FIXME: Also deal with multiple selected boxes
3479 SPBox3D *box = SP_BOX3D(item);
3480 Persp3D *persp = box3d_get_perspective(box);
3481 persp_repr = SP_OBJECT_REPR(persp);
3482 if (persp_repr) {
3483 g_object_set_data(tbl, "repr", persp_repr);
3484 Inkscape::GC::anchor(persp_repr);
3485 sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
3486 sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
3487 }
3489 inkscape_active_document()->setCurrentPersp3D(persp3d_get_from_repr(persp_repr));
3490 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3491 prefs->setString("/tools/shapes/3dbox/persp", persp_repr->attribute("id"));
3493 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
3494 box3d_resync_toolbar(persp_repr, tbl);
3495 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
3496 }
3497 }
3499 static void box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
3500 {
3501 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3502 SPDocument *document = sp_desktop_document(desktop);
3504 // quit if run by the attr_changed or selection changed listener
3505 if (g_object_get_data( dataKludge, "freeze" )) {
3506 return;
3507 }
3509 // in turn, prevent listener from responding
3510 g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
3512 std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
3513 if (sel_persps.empty()) {
3514 // this can happen when the document is created; we silently ignore it
3515 return;
3516 }
3517 Persp3D *persp = sel_persps.front();
3519 persp->perspective_impl->tmat.set_infinite_direction (axis, adj->value);
3520 SP_OBJECT(persp)->updateRepr();
3522 // TODO: use the correct axis here, too
3523 sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
3525 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
3526 }
3529 static void box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3530 {
3531 box3d_angle_value_changed(adj, dataKludge, Proj::X);
3532 }
3534 static void box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3535 {
3536 box3d_angle_value_changed(adj, dataKludge, Proj::Y);
3537 }
3539 static void box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3540 {
3541 box3d_angle_value_changed(adj, dataKludge, Proj::Z);
3542 }
3545 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction * /*box3d_angle*/, Proj::Axis axis )
3546 {
3547 // TODO: Take all selected perspectives into account
3548 std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
3549 if (sel_persps.empty()) {
3550 // this can happen when the document is created; we silently ignore it
3551 return;
3552 }
3553 Persp3D *persp = sel_persps.front();
3555 bool set_infinite = gtk_toggle_action_get_active(act);
3556 persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
3557 }
3559 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3560 {
3561 box3d_vp_state_changed(act, box3d_angle, Proj::X);
3562 }
3564 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3565 {
3566 box3d_vp_state_changed(act, box3d_angle, Proj::Y);
3567 }
3569 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3570 {
3571 box3d_vp_state_changed(act, box3d_angle, Proj::Z);
3572 }
3574 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3575 {
3576 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3577 EgeAdjustmentAction* eact = 0;
3578 SPDocument *document = sp_desktop_document (desktop);
3579 Persp3DImpl *persp_impl = document->getCurrentPersp3DImpl();
3581 EgeAdjustmentAction* box3d_angle_x = 0;
3582 EgeAdjustmentAction* box3d_angle_y = 0;
3583 EgeAdjustmentAction* box3d_angle_z = 0;
3585 /* Angle X */
3586 {
3587 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3588 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3589 eact = create_adjustment_action( "3DBoxAngleXAction",
3590 _("Angle in X direction"), _("Angle X:"),
3591 // Translators: PL is short for 'perspective line'
3592 _("Angle of PLs in X direction"),
3593 "/tools/shapes/3dbox/box3d_angle_x", 30,
3594 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
3595 -360.0, 360.0, 1.0, 10.0,
3596 labels, values, G_N_ELEMENTS(labels),
3597 box3d_angle_x_value_changed );
3598 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3599 g_object_set_data( holder, "box3d_angle_x_action", eact );
3600 box3d_angle_x = eact;
3601 }
3603 if (!persp_impl || !persp3d_VP_is_finite(persp_impl, Proj::X)) {
3604 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3605 } else {
3606 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3607 }
3610 /* VP X state */
3611 {
3612 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
3613 // Translators: VP is short for 'vanishing point'
3614 _("State of VP in X direction"),
3615 _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
3616 INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3617 Inkscape::ICON_SIZE_DECORATION );
3618 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3619 g_object_set_data( holder, "box3d_vp_x_state_action", act );
3620 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
3621 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3622 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3623 }
3625 /* Angle Y */
3626 {
3627 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3628 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3629 eact = create_adjustment_action( "3DBoxAngleYAction",
3630 _("Angle in Y direction"), _("Angle Y:"),
3631 // Translators: PL is short for 'perspective line'
3632 _("Angle of PLs in Y direction"),
3633 "/tools/shapes/3dbox/box3d_angle_y", 30,
3634 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3635 -360.0, 360.0, 1.0, 10.0,
3636 labels, values, G_N_ELEMENTS(labels),
3637 box3d_angle_y_value_changed );
3638 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3639 g_object_set_data( holder, "box3d_angle_y_action", eact );
3640 box3d_angle_y = eact;
3641 }
3643 if (!persp_impl || !persp3d_VP_is_finite(persp_impl, Proj::Y)) {
3644 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3645 } else {
3646 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3647 }
3649 /* VP Y state */
3650 {
3651 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
3652 // Translators: VP is short for 'vanishing point'
3653 _("State of VP in Y direction"),
3654 _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
3655 INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3656 Inkscape::ICON_SIZE_DECORATION );
3657 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3658 g_object_set_data( holder, "box3d_vp_y_state_action", act );
3659 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
3660 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3661 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3662 }
3664 /* Angle Z */
3665 {
3666 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3667 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3668 eact = create_adjustment_action( "3DBoxAngleZAction",
3669 _("Angle in Z direction"), _("Angle Z:"),
3670 // Translators: PL is short for 'perspective line'
3671 _("Angle of PLs in Z direction"),
3672 "/tools/shapes/3dbox/box3d_angle_z", 30,
3673 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3674 -360.0, 360.0, 1.0, 10.0,
3675 labels, values, G_N_ELEMENTS(labels),
3676 box3d_angle_z_value_changed );
3677 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3678 g_object_set_data( holder, "box3d_angle_z_action", eact );
3679 box3d_angle_z = eact;
3680 }
3682 if (!persp_impl || !persp3d_VP_is_finite(persp_impl, Proj::Z)) {
3683 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3684 } else {
3685 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3686 }
3688 /* VP Z state */
3689 {
3690 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3691 // Translators: VP is short for 'vanishing point'
3692 _("State of VP in Z direction"),
3693 _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3694 INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3695 Inkscape::ICON_SIZE_DECORATION );
3696 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3697 g_object_set_data( holder, "box3d_vp_z_state_action", act );
3698 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3699 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3700 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3701 }
3703 sigc::connection *connection = new sigc::connection(
3704 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3705 );
3706 g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3707 g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3708 }
3710 //########################
3711 //## Spiral ##
3712 //########################
3714 static void sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, Glib::ustring const &value_name)
3715 {
3716 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3718 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3719 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3720 prefs->setDouble("/tools/shapes/spiral/" + value_name, adj->value);
3721 }
3723 // quit if run by the attr_changed listener
3724 if (g_object_get_data( tbl, "freeze" )) {
3725 return;
3726 }
3728 // in turn, prevent listener from responding
3729 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3731 gchar* namespaced_name = g_strconcat("sodipodi:", value_name.data(), NULL);
3733 bool modmade = false;
3734 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3735 items != NULL;
3736 items = items->next)
3737 {
3738 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3739 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3740 sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3741 SP_OBJECT((SPItem *) items->data)->updateRepr();
3742 modmade = true;
3743 }
3744 }
3746 g_free(namespaced_name);
3748 if (modmade) {
3749 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3750 _("Change spiral"));
3751 }
3753 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3754 }
3756 static void sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3757 {
3758 sp_spl_tb_value_changed(adj, tbl, "revolution");
3759 }
3761 static void sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3762 {
3763 sp_spl_tb_value_changed(adj, tbl, "expansion");
3764 }
3766 static void sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3767 {
3768 sp_spl_tb_value_changed(adj, tbl, "t0");
3769 }
3771 static void sp_spl_tb_defaults(GtkWidget * /*widget*/, GtkObject *obj)
3772 {
3773 GtkWidget *tbl = GTK_WIDGET(obj);
3775 GtkAdjustment *adj;
3777 // fixme: make settable
3778 gdouble rev = 5;
3779 gdouble exp = 1.0;
3780 gdouble t0 = 0.0;
3782 adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3783 gtk_adjustment_set_value(adj, rev);
3784 gtk_adjustment_value_changed(adj);
3786 adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3787 gtk_adjustment_set_value(adj, exp);
3788 gtk_adjustment_value_changed(adj);
3790 adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3791 gtk_adjustment_set_value(adj, t0);
3792 gtk_adjustment_value_changed(adj);
3794 spinbutton_defocus(GTK_OBJECT(tbl));
3795 }
3798 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr,
3799 gchar const * /*name*/,
3800 gchar const * /*old_value*/,
3801 gchar const * /*new_value*/,
3802 bool /*is_interactive*/,
3803 gpointer data)
3804 {
3805 GtkWidget *tbl = GTK_WIDGET(data);
3807 // quit if run by the _changed callbacks
3808 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3809 return;
3810 }
3812 // in turn, prevent callbacks from responding
3813 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3815 GtkAdjustment *adj;
3816 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3817 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3819 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3820 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3822 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3823 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3825 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3826 }
3829 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3830 NULL, /* child_added */
3831 NULL, /* child_removed */
3832 spiral_tb_event_attr_changed,
3833 NULL, /* content_changed */
3834 NULL /* order_changed */
3835 };
3837 static void sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3838 {
3839 int n_selected = 0;
3840 Inkscape::XML::Node *repr = NULL;
3842 purge_repr_listener( tbl, tbl );
3844 for (GSList const *items = selection->itemList();
3845 items != NULL;
3846 items = items->next)
3847 {
3848 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3849 n_selected++;
3850 repr = SP_OBJECT_REPR((SPItem *) items->data);
3851 }
3852 }
3854 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3856 if (n_selected == 0) {
3857 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3858 } else if (n_selected == 1) {
3859 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3861 if (repr) {
3862 g_object_set_data( tbl, "repr", repr );
3863 Inkscape::GC::anchor(repr);
3864 sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3865 sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3866 }
3867 } else {
3868 // FIXME: implement averaging of all parameters for multiple selected
3869 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3870 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3871 }
3872 }
3875 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3876 {
3877 EgeAdjustmentAction* eact = 0;
3878 Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
3880 {
3881 EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3882 ege_output_action_set_use_markup( act, TRUE );
3883 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3884 g_object_set_data( holder, "mode_action", act );
3885 }
3887 /* Revolution */
3888 {
3889 gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3890 gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3891 eact = create_adjustment_action( "SpiralRevolutionAction",
3892 _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3893 "/tools/shapes/spiral/revolution", 3.0,
3894 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3895 0.01, 1024.0, 0.1, 1.0,
3896 labels, values, G_N_ELEMENTS(labels),
3897 sp_spl_tb_revolution_value_changed, 1, 2);
3898 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3899 }
3901 /* Expansion */
3902 {
3903 gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3904 gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3905 eact = create_adjustment_action( "SpiralExpansionAction",
3906 _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3907 "/tools/shapes/spiral/expansion", 1.0,
3908 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3909 0.0, 1000.0, 0.01, 1.0,
3910 labels, values, G_N_ELEMENTS(labels),
3911 sp_spl_tb_expansion_value_changed);
3912 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3913 }
3915 /* T0 */
3916 {
3917 gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3918 gdouble values[] = {0, 0.5, 0.9};
3919 eact = create_adjustment_action( "SpiralT0Action",
3920 _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3921 "/tools/shapes/spiral/t0", 0.0,
3922 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3923 0.0, 0.999, 0.01, 1.0,
3924 labels, values, G_N_ELEMENTS(labels),
3925 sp_spl_tb_t0_value_changed);
3926 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3927 }
3929 /* Reset */
3930 {
3931 InkAction* inky = ink_action_new( "SpiralResetAction",
3932 _("Defaults"),
3933 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3934 GTK_STOCK_CLEAR,
3935 secondarySize );
3936 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3937 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3938 }
3941 sigc::connection *connection = new sigc::connection(
3942 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3943 );
3944 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3945 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3946 }
3948 //########################
3949 //## Pen/Pencil ##
3950 //########################
3952 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3953 static Glib::ustring const freehand_tool_name(GObject *dataKludge)
3954 {
3955 SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3956 return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3957 ? "/tools/freehand/pen"
3958 : "/tools/freehand/pencil" );
3959 }
3961 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3962 {
3963 gint mode = ege_select_one_action_get_active(act);
3965 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3966 prefs->setInt(freehand_tool_name(tbl) + "/freehand-mode", mode);
3968 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3970 // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3971 // preparatory work here
3972 if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3973 SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3974 sp_pen_context_set_polyline_mode(pc);
3975 }
3976 }
3978 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3979 {
3980 /* Freehand mode toggle buttons */
3981 {
3982 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3983 guint freehandMode = prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/freehand-mode" : "/tools/freehand/pen/freehand-mode" ), 0);
3984 Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
3986 {
3987 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3989 GtkTreeIter iter;
3990 gtk_list_store_append( model, &iter );
3991 gtk_list_store_set( model, &iter,
3992 0, _("Bezier"),
3993 1, _("Create regular Bezier path"),
3994 2, INKSCAPE_ICON_PATH_MODE_BEZIER,
3995 -1 );
3997 gtk_list_store_append( model, &iter );
3998 gtk_list_store_set( model, &iter,
3999 0, _("Spiro"),
4000 1, _("Create Spiro path"),
4001 2, INKSCAPE_ICON_PATH_MODE_SPIRO,
4002 -1 );
4004 if (!tool_is_pencil) {
4005 gtk_list_store_append( model, &iter );
4006 gtk_list_store_set( model, &iter,
4007 0, _("Zigzag"),
4008 1, _("Create a sequence of straight line segments"),
4009 2, INKSCAPE_ICON_PATH_MODE_POLYLINE,
4010 -1 );
4012 gtk_list_store_append( model, &iter );
4013 gtk_list_store_set( model, &iter,
4014 0, _("Paraxial"),
4015 1, _("Create a sequence of paraxial line segments"),
4016 2, INKSCAPE_ICON_PATH_MODE_POLYLINE_PARAXIAL,
4017 -1 );
4018 }
4020 EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
4021 "FreehandModeActionPencil" :
4022 "FreehandModeActionPen",
4023 (_("Mode:")), (_("Mode of new lines drawn by this tool")), NULL, GTK_TREE_MODEL(model) );
4024 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4026 ege_select_one_action_set_appearance( act, "full" );
4027 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4028 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4029 ege_select_one_action_set_icon_column( act, 2 );
4030 ege_select_one_action_set_icon_size( act, secondarySize );
4031 ege_select_one_action_set_tooltip_column( act, 1 );
4033 ege_select_one_action_set_active( act, freehandMode);
4034 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
4035 }
4036 }
4037 }
4039 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
4040 gint shape = ege_select_one_action_get_active( act );
4041 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4042 prefs->setInt(freehand_tool_name(dataKludge) + "/shape", shape);
4043 }
4045 /**
4046 * \brief Generate the list of freehand advanced shape option entries.
4047 */
4048 static GList * freehand_shape_dropdown_items_list() {
4049 GList *glist = NULL;
4051 glist = g_list_append (glist, _("None"));
4052 glist = g_list_append (glist, _("Triangle in"));
4053 glist = g_list_append (glist, _("Triangle out"));
4054 glist = g_list_append (glist, _("Ellipse"));
4055 glist = g_list_append (glist, _("From clipboard"));
4057 return glist;
4058 }
4060 static void freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
4061 {
4062 /*advanced shape options */
4063 {
4064 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4065 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
4067 GList* items = 0;
4068 gint count = 0;
4069 for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
4070 {
4071 GtkTreeIter iter;
4072 gtk_list_store_append( model, &iter );
4073 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
4074 count++;
4075 }
4076 g_list_free( items );
4077 items = 0;
4078 EgeSelectOneAction* act1 = ege_select_one_action_new(
4079 tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
4080 _("Shape:"), (_("Shape of new paths drawn by this tool")), NULL, GTK_TREE_MODEL(model));
4081 g_object_set( act1, "short_label", _("Shape:"), NULL );
4082 ege_select_one_action_set_appearance( act1, "compact" );
4083 ege_select_one_action_set_active( act1, prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/shape" : "/tools/freehand/pen/shape" ), 0) );
4084 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
4085 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
4086 g_object_set_data( holder, "shape_action", act1 );
4087 }
4088 }
4090 static void sp_pen_toolbox_prep(SPDesktop * /*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4091 {
4092 sp_add_freehand_mode_toggle(mainActions, holder, false);
4093 freehand_add_advanced_shape_options(mainActions, holder, false);
4094 }
4097 static void sp_pencil_tb_defaults(GtkWidget * /*widget*/, GtkObject *obj)
4098 {
4099 GtkWidget *tbl = GTK_WIDGET(obj);
4101 GtkAdjustment *adj;
4103 // fixme: make settable
4104 gdouble tolerance = 4;
4106 adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
4107 gtk_adjustment_set_value(adj, tolerance);
4108 gtk_adjustment_value_changed(adj);
4110 spinbutton_defocus(GTK_OBJECT(tbl));
4111 }
4113 static void sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
4114 {
4115 // quit if run by the attr_changed listener
4116 if (g_object_get_data( tbl, "freeze" )) {
4117 return;
4118 }
4119 // in turn, prevent listener from responding
4120 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4121 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4122 prefs->setDouble("/tools/freehand/pencil/tolerance", adj->value);
4123 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4124 }
4126 /*
4127 class PencilToleranceObserver : public Inkscape::Preferences::Observer {
4128 public:
4129 PencilToleranceObserver(Glib::ustring const &path, GObject *x) : Observer(path), _obj(x)
4130 {
4131 g_object_set_data(_obj, "prefobserver", this);
4132 }
4133 virtual ~PencilToleranceObserver() {
4134 if (g_object_get_data(_obj, "prefobserver") == this) {
4135 g_object_set_data(_obj, "prefobserver", NULL);
4136 }
4137 }
4138 virtual void notify(Inkscape::Preferences::Entry const &val) {
4139 GObject* tbl = _obj;
4140 if (g_object_get_data( tbl, "freeze" )) {
4141 return;
4142 }
4143 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4145 GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl, "tolerance");
4147 double v = val.getDouble(adj->value);
4148 gtk_adjustment_set_value(adj, v);
4149 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4150 }
4151 private:
4152 GObject *_obj;
4153 };
4154 */
4156 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4157 {
4158 sp_add_freehand_mode_toggle(mainActions, holder, true);
4160 EgeAdjustmentAction* eact = 0;
4162 /* Tolerance */
4163 {
4164 gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
4165 gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
4166 eact = create_adjustment_action( "PencilToleranceAction",
4167 _("Smoothing:"), _("Smoothing: "),
4168 _("How much smoothing (simplifying) is applied to the line"),
4169 "/tools/freehand/pencil/tolerance",
4170 3.0,
4171 GTK_WIDGET(desktop->canvas), NULL,
4172 holder, TRUE, "altx-pencil",
4173 1, 100.0, 0.5, 1.0,
4174 labels, values, G_N_ELEMENTS(labels),
4175 sp_pencil_tb_tolerance_value_changed,
4176 1, 2);
4177 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4178 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4179 }
4181 /* advanced shape options */
4182 freehand_add_advanced_shape_options(mainActions, holder, true);
4184 /* Reset */
4185 {
4186 InkAction* inky = ink_action_new( "PencilResetAction",
4187 _("Defaults"),
4188 _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
4189 GTK_STOCK_CLEAR,
4190 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4191 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
4192 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4193 }
4195 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4197 }
4200 //########################
4201 //## Tweak ##
4202 //########################
4204 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject * /*tbl*/ )
4205 {
4206 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4207 prefs->setDouble( "/tools/tweak/width", adj->value * 0.01 );
4208 }
4210 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject * /*tbl*/ )
4211 {
4212 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4213 prefs->setDouble( "/tools/tweak/force", adj->value * 0.01 );
4214 }
4216 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4217 {
4218 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4219 prefs->setBool("/tools/tweak/usepressure", gtk_toggle_action_get_active(act));
4220 }
4222 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4223 {
4224 int mode = ege_select_one_action_get_active( act );
4225 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4226 prefs->setInt("/tools/tweak/mode", mode);
4228 static gchar const* names[] = {"tweak_doh", "tweak_dos", "tweak_dol", "tweak_doo", "tweak_channels_label"};
4229 bool flag = ((mode == TWEAK_MODE_COLORPAINT) || (mode == TWEAK_MODE_COLORJITTER));
4230 for (size_t i = 0; i < G_N_ELEMENTS(names); ++i) {
4231 GtkAction *act = GTK_ACTION(g_object_get_data( tbl, names[i] ));
4232 if (act) {
4233 gtk_action_set_sensitive(act, flag);
4234 }
4235 }
4236 GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
4237 if (fid) {
4238 gtk_action_set_sensitive(fid, !flag);
4239 }
4240 }
4242 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject * /*tbl*/ )
4243 {
4244 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4245 prefs->setDouble( "/tools/tweak/fidelity", adj->value * 0.01 );
4246 }
4248 static void tweak_toggle_doh(GtkToggleAction *act, gpointer /*data*/) {
4249 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4250 prefs->setBool("/tools/tweak/doh", gtk_toggle_action_get_active(act));
4251 }
4252 static void tweak_toggle_dos(GtkToggleAction *act, gpointer /*data*/) {
4253 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4254 prefs->setBool("/tools/tweak/dos", gtk_toggle_action_get_active(act));
4255 }
4256 static void tweak_toggle_dol(GtkToggleAction *act, gpointer /*data*/) {
4257 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4258 prefs->setBool("/tools/tweak/dol", gtk_toggle_action_get_active(act));
4259 }
4260 static void tweak_toggle_doo(GtkToggleAction *act, gpointer /*data*/) {
4261 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4262 prefs->setBool("/tools/tweak/doo", gtk_toggle_action_get_active(act));
4263 }
4265 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4266 {
4267 Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
4268 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4270 {
4271 /* Width */
4272 gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
4273 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4274 EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
4275 _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
4276 "/tools/tweak/width", 15,
4277 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
4278 1, 100, 1.0, 10.0,
4279 labels, values, G_N_ELEMENTS(labels),
4280 sp_tweak_width_value_changed, 0.01, 0, 100 );
4281 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4282 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4283 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4284 }
4287 {
4288 /* Force */
4289 gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
4290 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4291 EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
4292 _("Force"), _("Force:"), _("The force of the tweak action"),
4293 "/tools/tweak/force", 20,
4294 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
4295 1, 100, 1.0, 10.0,
4296 labels, values, G_N_ELEMENTS(labels),
4297 sp_tweak_force_value_changed, 0.01, 0, 100 );
4298 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4299 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4300 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4301 }
4303 /* Mode */
4304 {
4305 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4307 GtkTreeIter iter;
4308 gtk_list_store_append( model, &iter );
4309 gtk_list_store_set( model, &iter,
4310 0, _("Move mode"),
4311 1, _("Move objects in any direction"),
4312 2, INKSCAPE_ICON_OBJECT_TWEAK_PUSH,
4313 -1 );
4315 gtk_list_store_append( model, &iter );
4316 gtk_list_store_set( model, &iter,
4317 0, _("Move in/out mode"),
4318 1, _("Move objects towards cursor; with Shift from cursor"),
4319 2, INKSCAPE_ICON_OBJECT_TWEAK_ATTRACT,
4320 -1 );
4322 gtk_list_store_append( model, &iter );
4323 gtk_list_store_set( model, &iter,
4324 0, _("Move jitter mode"),
4325 1, _("Move objects in random directions"),
4326 2, INKSCAPE_ICON_OBJECT_TWEAK_RANDOMIZE,
4327 -1 );
4329 gtk_list_store_append( model, &iter );
4330 gtk_list_store_set( model, &iter,
4331 0, _("Scale mode"),
4332 1, _("Shrink objects, with Shift enlarge"),
4333 2, INKSCAPE_ICON_OBJECT_TWEAK_SHRINK,
4334 -1 );
4336 gtk_list_store_append( model, &iter );
4337 gtk_list_store_set( model, &iter,
4338 0, _("Rotate mode"),
4339 1, _("Rotate objects, with Shift counterclockwise"),
4340 2, INKSCAPE_ICON_OBJECT_TWEAK_ROTATE,
4341 -1 );
4343 gtk_list_store_append( model, &iter );
4344 gtk_list_store_set( model, &iter,
4345 0, _("Duplicate/delete mode"),
4346 1, _("Duplicate objects, with Shift delete"),
4347 2, INKSCAPE_ICON_OBJECT_TWEAK_DUPLICATE,
4348 -1 );
4350 gtk_list_store_append( model, &iter );
4351 gtk_list_store_set( model, &iter,
4352 0, _("Push mode"),
4353 1, _("Push parts of paths in any direction"),
4354 2, INKSCAPE_ICON_PATH_TWEAK_PUSH,
4355 -1 );
4357 gtk_list_store_append( model, &iter );
4358 gtk_list_store_set( model, &iter,
4359 0, _("Shrink/grow mode"),
4360 1, _("Shrink (inset) parts of paths; with Shift grow (outset)"),
4361 2, INKSCAPE_ICON_PATH_TWEAK_SHRINK,
4362 -1 );
4364 gtk_list_store_append( model, &iter );
4365 gtk_list_store_set( model, &iter,
4366 0, _("Attract/repel mode"),
4367 1, _("Attract parts of paths towards cursor; with Shift from cursor"),
4368 2, INKSCAPE_ICON_PATH_TWEAK_ATTRACT,
4369 -1 );
4371 gtk_list_store_append( model, &iter );
4372 gtk_list_store_set( model, &iter,
4373 0, _("Roughen mode"),
4374 1, _("Roughen parts of paths"),
4375 2, INKSCAPE_ICON_PATH_TWEAK_ROUGHEN,
4376 -1 );
4378 gtk_list_store_append( model, &iter );
4379 gtk_list_store_set( model, &iter,
4380 0, _("Color paint mode"),
4381 1, _("Paint the tool's color upon selected objects"),
4382 2, INKSCAPE_ICON_OBJECT_TWEAK_PAINT,
4383 -1 );
4385 gtk_list_store_append( model, &iter );
4386 gtk_list_store_set( model, &iter,
4387 0, _("Color jitter mode"),
4388 1, _("Jitter the colors of selected objects"),
4389 2, INKSCAPE_ICON_OBJECT_TWEAK_JITTER_COLOR,
4390 -1 );
4392 gtk_list_store_append( model, &iter );
4393 gtk_list_store_set( model, &iter,
4394 0, _("Blur mode"),
4395 1, _("Blur selected objects more; with Shift, blur less"),
4396 2, INKSCAPE_ICON_OBJECT_TWEAK_BLUR,
4397 -1 );
4400 EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4401 g_object_set( act, "short_label", _("Mode:"), NULL );
4402 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4403 g_object_set_data( holder, "mode_action", act );
4405 ege_select_one_action_set_appearance( act, "full" );
4406 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4407 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4408 ege_select_one_action_set_icon_column( act, 2 );
4409 ege_select_one_action_set_icon_size( act, secondarySize );
4410 ege_select_one_action_set_tooltip_column( act, 1 );
4412 gint mode = prefs->getInt("/tools/tweak/mode", 0);
4413 ege_select_one_action_set_active( act, mode );
4414 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
4416 g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
4417 }
4419 guint mode = prefs->getInt("/tools/tweak/mode", 0);
4421 {
4422 EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
4423 ege_output_action_set_use_markup( act, TRUE );
4424 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4425 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER) {
4426 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4427 }
4428 g_object_set_data( holder, "tweak_channels_label", act);
4429 }
4431 {
4432 InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
4433 _("Hue"),
4434 _("In color mode, act on objects' hue"),
4435 NULL,
4436 Inkscape::ICON_SIZE_DECORATION );
4437 //TRANSLATORS: "H" here stands for hue
4438 g_object_set( act, "short_label", _("H"), NULL );
4439 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4440 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
4441 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doh", true) );
4442 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER) {
4443 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4444 }
4445 g_object_set_data( holder, "tweak_doh", act);
4446 }
4447 {
4448 InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
4449 _("Saturation"),
4450 _("In color mode, act on objects' saturation"),
4451 NULL,
4452 Inkscape::ICON_SIZE_DECORATION );
4453 //TRANSLATORS: "S" here stands for Saturation
4454 g_object_set( act, "short_label", _("S"), NULL );
4455 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4456 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
4457 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dos", true) );
4458 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER) {
4459 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4460 }
4461 g_object_set_data( holder, "tweak_dos", act );
4462 }
4463 {
4464 InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
4465 _("Lightness"),
4466 _("In color mode, act on objects' lightness"),
4467 NULL,
4468 Inkscape::ICON_SIZE_DECORATION );
4469 //TRANSLATORS: "L" here stands for Lightness
4470 g_object_set( act, "short_label", _("L"), NULL );
4471 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4472 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
4473 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dol", true) );
4474 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER) {
4475 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4476 }
4477 g_object_set_data( holder, "tweak_dol", act );
4478 }
4479 {
4480 InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
4481 _("Opacity"),
4482 _("In color mode, act on objects' opacity"),
4483 NULL,
4484 Inkscape::ICON_SIZE_DECORATION );
4485 //TRANSLATORS: "O" here stands for Opacity
4486 g_object_set( act, "short_label", _("O"), NULL );
4487 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4488 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
4489 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doo", true) );
4490 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER) {
4491 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4492 }
4493 g_object_set_data( holder, "tweak_doo", act );
4494 }
4496 { /* Fidelity */
4497 gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
4498 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4499 EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
4500 _("Fidelity"), _("Fidelity:"),
4501 _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
4502 "/tools/tweak/fidelity", 50,
4503 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
4504 1, 100, 1.0, 10.0,
4505 labels, values, G_N_ELEMENTS(labels),
4506 sp_tweak_fidelity_value_changed, 0.01, 0, 100 );
4507 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4508 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4509 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
4510 gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
4511 }
4512 g_object_set_data( holder, "tweak_fidelity", eact );
4513 }
4516 /* Use Pressure button */
4517 {
4518 InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
4519 _("Pressure"),
4520 _("Use the pressure of the input device to alter the force of tweak action"),
4521 INKSCAPE_ICON_DRAW_USE_PRESSURE,
4522 Inkscape::ICON_SIZE_DECORATION );
4523 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4524 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
4525 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/usepressure", true) );
4526 }
4528 }
4531 //########################
4532 //## Spray ##
4533 //########################
4535 static void sp_spray_width_value_changed( GtkAdjustment *adj, GObject * /*tbl*/ )
4536 {
4537 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4538 prefs->setDouble( "/tools/spray/width", adj->value );
4539 }
4541 static void sp_spray_mean_value_changed( GtkAdjustment *adj, GObject * /*tbl*/ )
4542 {
4543 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4544 prefs->setDouble( "/tools/spray/mean", adj->value );
4545 }
4547 static void sp_spray_standard_deviation_value_changed( GtkAdjustment *adj, GObject * /*tbl*/ )
4548 {
4549 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4550 prefs->setDouble( "/tools/spray/standard_deviation", adj->value );
4551 }
4553 static void sp_spray_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4554 {
4555 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4556 prefs->setBool("/tools/spray/usepressure", gtk_toggle_action_get_active(act));
4557 }
4559 static void sp_spray_mode_changed( EgeSelectOneAction *act, GObject * /*tbl*/ )
4560 {
4561 int mode = ege_select_one_action_get_active( act );
4562 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4563 prefs->setInt("/tools/spray/mode", mode);
4564 }
4566 static void sp_spray_population_value_changed( GtkAdjustment *adj, GObject * /*tbl*/ )
4567 {
4568 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4569 prefs->setDouble( "/tools/spray/population", adj->value );
4570 }
4572 static void sp_spray_rotation_value_changed( GtkAdjustment *adj, GObject * /*tbl*/ )
4573 {
4574 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4575 prefs->setDouble( "/tools/spray/rotation_variation", adj->value );
4576 }
4578 static void sp_spray_scale_value_changed( GtkAdjustment *adj, GObject * /*tbl*/ )
4579 {
4580 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4581 prefs->setDouble( "/tools/spray/scale_variation", adj->value );
4582 }
4585 static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4586 {
4587 Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
4588 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4590 {
4591 /* Width */
4592 gchar const* labels[] = {_("(narrow spray)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad spray)")};
4593 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4594 EgeAdjustmentAction *eact = create_adjustment_action( "SprayWidthAction",
4595 _("Width"), _("Width:"), _("The width of the spray area (relative to the visible canvas area)"),
4596 "/tools/spray/width", 15,
4597 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spray",
4598 1, 100, 1.0, 10.0,
4599 labels, values, G_N_ELEMENTS(labels),
4600 sp_spray_width_value_changed, 1, 0 );
4601 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4602 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4603 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4604 }
4606 {
4607 /* Mean */
4608 gchar const* labels[] = {_("(minimum mean)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum mean)")};
4609 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4610 EgeAdjustmentAction *eact = create_adjustment_action( "SprayMeanAction",
4611 _("Focus"), _("Focus:"), _("0 to spray a spot. Increase to enlarge the ring radius."),
4612 "/tools/spray/mean", 0,
4613 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-mean",
4614 0, 100, 1.0, 10.0,
4615 labels, values, G_N_ELEMENTS(labels),
4616 sp_spray_mean_value_changed, 1, 0 );
4617 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4618 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4619 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4620 }
4622 {
4623 /* Standard_deviation */
4624 gchar const* labels[] = {_("(minimum scatter)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum scatter)")};
4625 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4626 EgeAdjustmentAction *eact = create_adjustment_action( "SprayStandard_deviationAction",
4627 _("Scatter"), _("Scatter:"), _("Increase to scatter sprayed objects."),
4628 "/tools/spray/standard_deviation", 70,
4629 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-standard_deviation",
4630 1, 100, 1.0, 10.0,
4631 labels, values, G_N_ELEMENTS(labels),
4632 sp_spray_standard_deviation_value_changed, 1, 0 );
4633 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4634 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4635 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4636 }
4638 /* Mode */
4639 {
4640 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4642 GtkTreeIter iter;
4643 gtk_list_store_append( model, &iter );
4644 gtk_list_store_set( model, &iter,
4645 0, _("Spray with copies"),
4646 1, _("Spray copies of the initial selection"),
4647 2, INKSCAPE_ICON_SPRAY_COPY_MODE,
4648 -1 );
4650 gtk_list_store_append( model, &iter );
4651 gtk_list_store_set( model, &iter,
4652 0, _("Spray with clones"),
4653 1, _("Spray clones of the initial selection"),
4654 2, INKSCAPE_ICON_SPRAY_CLONE_MODE,
4655 -1 );
4657 gtk_list_store_append( model, &iter );
4658 gtk_list_store_set( model, &iter,
4659 0, _("Spray single path"),
4660 1, _("Spray objects in a single path"),
4661 2, INKSCAPE_ICON_SPRAY_UNION_MODE,
4662 -1 );
4664 EgeSelectOneAction* act = ege_select_one_action_new( "SprayModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4665 g_object_set( act, "short_label", _("Mode:"), NULL );
4666 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4667 g_object_set_data( holder, "mode_action", act );
4669 ege_select_one_action_set_appearance( act, "full" );
4670 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4671 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4672 ege_select_one_action_set_icon_column( act, 2 );
4673 ege_select_one_action_set_icon_size( act, secondarySize );
4674 ege_select_one_action_set_tooltip_column( act, 1 );
4676 gint mode = prefs->getInt("/tools/spray/mode", 1);
4677 ege_select_one_action_set_active( act, mode );
4678 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_spray_mode_changed), holder );
4680 g_object_set_data( G_OBJECT(holder), "spray_tool_mode", act);
4681 }
4683 { /* Population */
4684 gchar const* labels[] = {_("(low population)"), 0, 0, _("(default)"), 0, 0, _("(high population)")};
4685 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4686 EgeAdjustmentAction *eact = create_adjustment_action( "SprayPopulationAction",
4687 _("Amount"), _("Amount:"),
4688 _("Adjusts the number of items sprayed per clic."),
4689 "/tools/spray/population", 70,
4690 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-population",
4691 1, 100, 1.0, 10.0,
4692 labels, values, G_N_ELEMENTS(labels),
4693 sp_spray_population_value_changed, 1, 0 );
4694 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4695 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4696 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4697 g_object_set_data( holder, "spray_population", eact );
4698 }
4700 /* Use Pressure button */
4701 {
4702 InkToggleAction* act = ink_toggle_action_new( "SprayPressureAction",
4703 _("Pressure"),
4704 _("Use the pressure of the input device to alter the amount of sprayed objects."),
4705 "use_pressure",
4706 Inkscape::ICON_SIZE_DECORATION );
4707 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4708 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_spray_pressure_state_changed), NULL);
4709 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/spray/usepressure", true) );
4710 }
4712 { /* Rotation */
4713 gchar const* labels[] = {_("(low rotation variation)"), 0, 0, _("(default)"), 0, 0, _("(high rotation variation)")};
4714 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4715 EgeAdjustmentAction *eact = create_adjustment_action( "SprayRotationAction",
4716 _("Rotation"), _("Rotation:"),
4717 // xgettext:no-c-format
4718 _("Variation of the rotation of the sprayed objects. 0% for the same rotation than the original object."),
4719 "/tools/spray/rotation_variation", 0,
4720 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-rotation",
4721 0, 100, 1.0, 10.0,
4722 labels, values, G_N_ELEMENTS(labels),
4723 sp_spray_rotation_value_changed, 1, 0 );
4724 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4725 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4726 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4727 g_object_set_data( holder, "spray_rotation", eact );
4728 }
4730 { /* Scale */
4731 gchar const* labels[] = {_("(low scale variation)"), 0, 0, _("(default)"), 0, 0, _("(high scale variation)")};
4732 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4733 EgeAdjustmentAction *eact = create_adjustment_action( "SprayScaleAction",
4734 _("Scale"), _("Scale:"),
4735 // xgettext:no-c-format
4736 _("Variation in the scale of the sprayed objects. 0% for the same scale than the original object."),
4737 "/tools/spray/scale_variation", 0,
4738 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-scale",
4739 0, 100, 1.0, 10.0,
4740 labels, values, G_N_ELEMENTS(labels),
4741 sp_spray_scale_value_changed, 1, 0 );
4742 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4743 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4744 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4745 g_object_set_data( holder, "spray_scale", eact );
4746 }
4750 }
4753 //########################
4754 //## Calligraphy ##
4755 //########################
4756 static void update_presets_list(GObject *tbl)
4757 {
4758 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4759 if (g_object_get_data(tbl, "presets_blocked")) {
4760 return;
4761 }
4763 EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4764 if (!sel) {
4765 // WTF!? This will cause a segfault if ever reached
4766 //ege_select_one_action_set_active(sel, 0);
4767 return;
4768 }
4770 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4772 int ege_index = 1;
4773 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++ege_index) {
4774 bool match = true;
4776 std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(*i);
4777 for (std::vector<Inkscape::Preferences::Entry>::iterator j = preset.begin(); j != preset.end(); ++j) {
4778 Glib::ustring entry_name = j->getEntryName();
4779 if (entry_name == "id" || entry_name == "name") {
4780 continue;
4781 }
4783 void *widget = g_object_get_data(tbl, entry_name.data());
4784 if (widget) {
4785 if (GTK_IS_ADJUSTMENT(widget)) {
4786 double v = j->getDouble();
4787 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4788 //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
4789 if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
4790 match = false;
4791 break;
4792 }
4793 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4794 bool v = j->getBool();
4795 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4796 //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
4797 if ( static_cast<bool>(gtk_toggle_action_get_active(toggle)) != v ) {
4798 match = false;
4799 break;
4800 }
4801 }
4802 }
4803 }
4805 if (match) {
4806 // newly added item is at the same index as the
4807 // save command, so we need to change twice for it to take effect
4808 ege_select_one_action_set_active(sel, 0);
4809 ege_select_one_action_set_active(sel, ege_index); // one-based index
4810 return;
4811 }
4812 }
4814 // no match found
4815 ege_select_one_action_set_active(sel, 0);
4816 }
4818 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
4819 {
4820 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4821 prefs->setDouble( "/tools/calligraphic/mass", adj->value );
4822 update_presets_list(tbl);
4823 }
4825 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
4826 {
4827 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4828 prefs->setDouble( "/tools/calligraphic/wiggle", adj->value );
4829 update_presets_list(tbl);
4830 }
4832 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
4833 {
4834 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4835 prefs->setDouble( "/tools/calligraphic/angle", adj->value );
4836 update_presets_list(tbl);
4837 }
4839 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
4840 {
4841 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4842 prefs->setDouble( "/tools/calligraphic/width", adj->value );
4843 update_presets_list(tbl);
4844 }
4846 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
4847 {
4848 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4849 prefs->setDouble("/tools/calligraphic/thinning", adj->value );
4850 update_presets_list(tbl);
4851 }
4853 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
4854 {
4855 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4856 prefs->setDouble( "/tools/calligraphic/flatness", adj->value );
4857 update_presets_list(tbl);
4858 }
4860 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
4861 {
4862 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4863 prefs->setDouble( "/tools/calligraphic/tremor", adj->value );
4864 update_presets_list(tbl);
4865 }
4867 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
4868 {
4869 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4870 prefs->setDouble( "/tools/calligraphic/cap_rounding", adj->value );
4871 update_presets_list(tbl);
4872 }
4874 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject* tbl )
4875 {
4876 // TODO merge into PrefPusher
4877 GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
4878 if (calligraphy_angle ) {
4879 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
4880 }
4881 }
4884 static gchar const *const widget_names[] = {
4885 "width",
4886 "mass",
4887 "wiggle",
4888 "angle",
4889 "thinning",
4890 "tremor",
4891 "flatness",
4892 "cap_rounding",
4893 "usepressure",
4894 "tracebackground",
4895 "usetilt"
4896 };
4899 static void sp_dcc_build_presets_list(GObject *tbl)
4900 {
4901 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4903 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4904 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4905 gtk_list_store_clear (model);
4907 {
4908 GtkTreeIter iter;
4909 gtk_list_store_append( model, &iter );
4910 gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4911 }
4913 // iterate over all presets to populate the list
4914 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4915 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4916 int ii=1;
4918 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i) {
4919 GtkTreeIter iter;
4920 Glib::ustring preset_name = prefs->getString(*i + "/name");
4921 gtk_list_store_append( model, &iter );
4922 gtk_list_store_set( model, &iter, 0, _(preset_name.data()), 1, ii++, -1 );
4923 }
4925 {
4926 GtkTreeIter iter;
4927 gtk_list_store_append( model, &iter );
4928 gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4929 g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4930 }
4932 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4934 update_presets_list (tbl);
4935 }
4937 static void sp_dcc_save_profile(GtkWidget * /*widget*/, GObject *tbl)
4938 {
4939 using Inkscape::UI::Dialog::CalligraphicProfileRename;
4940 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4941 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4942 if (! desktop) {
4943 return;
4944 }
4946 if (g_object_get_data(tbl, "presets_blocked")) {
4947 return;
4948 }
4950 CalligraphicProfileRename::show(desktop);
4951 if ( !CalligraphicProfileRename::applied()) {
4952 // dialog cancelled
4953 update_presets_list (tbl);
4954 return;
4955 }
4956 Glib::ustring profile_name = CalligraphicProfileRename::getProfileName();
4958 if (profile_name.empty()) {
4959 // empty name entered
4960 update_presets_list (tbl);
4961 return;
4962 }
4964 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4966 // If there's a preset with the given name, find it and set save_path appropriately
4967 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4968 int total_presets = presets.size();
4969 int new_index = -1;
4970 Glib::ustring save_path; // profile pref path without a trailing slash
4972 int temp_index = 0;
4973 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++temp_index) {
4974 Glib::ustring name = prefs->getString(*i + "/name");
4975 if (!name.empty() && profile_name == name) {
4976 new_index = temp_index;
4977 save_path = *i;
4978 break;
4979 }
4980 }
4982 if (new_index == -1) {
4983 // no preset with this name, create
4984 new_index = total_presets + 1;
4985 gchar *profile_id = g_strdup_printf("/dcc%d", new_index);
4986 save_path = Glib::ustring("/tools/calligraphic/preset") + profile_id;
4987 g_free(profile_id);
4988 }
4990 for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4991 gchar const *const widget_name = widget_names[i];
4992 void *widget = g_object_get_data(tbl, widget_name);
4993 if (widget) {
4994 if (GTK_IS_ADJUSTMENT(widget)) {
4995 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4996 prefs->setDouble(save_path + "/" + widget_name, gtk_adjustment_get_value(adj));
4997 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4998 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4999 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
5000 prefs->setBool(save_path + "/" + widget_name, gtk_toggle_action_get_active(toggle));
5001 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
5002 } else {
5003 g_warning("Unknown widget type for preset: %s\n", widget_name);
5004 }
5005 } else {
5006 g_warning("Bad key when writing preset: %s\n", widget_name);
5007 }
5008 }
5009 prefs->setString(save_path + "/name", profile_name);
5011 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
5012 sp_dcc_build_presets_list (tbl);
5013 }
5016 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl)
5017 {
5018 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5020 gint preset_index = ege_select_one_action_get_active( act );
5021 // This is necessary because EgeSelectOneAction spams us with GObject "changed" signal calls
5022 // even when the preset is not changed. It would be good to replace it with something more
5023 // modern. Index 0 means "No preset", so we don't do anything.
5024 if (preset_index == 0) {
5025 return;
5026 }
5028 gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
5030 if (preset_index == save_presets_index) {
5031 // this is the Save command
5032 sp_dcc_save_profile(NULL, tbl);
5033 return;
5034 }
5036 if (g_object_get_data(tbl, "presets_blocked")) {
5037 return;
5038 }
5040 // preset_index is one-based so we subtract 1
5041 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
5042 Glib::ustring preset_path = presets.at(preset_index - 1);
5044 if (!preset_path.empty()) {
5045 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
5047 std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(preset_path);
5049 // Shouldn't this be std::map?
5050 for (std::vector<Inkscape::Preferences::Entry>::iterator i = preset.begin(); i != preset.end(); ++i) {
5051 Glib::ustring entry_name = i->getEntryName();
5052 if (entry_name == "id" || entry_name == "name") {
5053 continue;
5054 }
5055 void *widget = g_object_get_data(tbl, entry_name.data());
5056 if (widget) {
5057 if (GTK_IS_ADJUSTMENT(widget)) {
5058 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
5059 gtk_adjustment_set_value(adj, i->getDouble());
5060 //std::cout << "set adj " << attr_name << " to " << v << "\n";
5061 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
5062 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
5063 gtk_toggle_action_set_active(toggle, i->getBool());
5064 //std::cout << "set toggle " << attr_name << " to " << v << "\n";
5065 } else {
5066 g_warning("Unknown widget type for preset: %s\n", entry_name.data());
5067 }
5068 } else {
5069 g_warning("Bad key found in a preset record: %s\n", entry_name.data());
5070 }
5071 }
5072 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
5073 }
5074 }
5076 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5077 {
5078 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5079 {
5080 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
5082 EgeAdjustmentAction* calligraphy_angle = 0;
5084 {
5085 /* Width */
5086 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5087 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5088 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
5089 _("Pen Width"), _("Width:"),
5090 _("The width of the calligraphic pen (relative to the visible canvas area)"),
5091 "/tools/calligraphic/width", 15,
5092 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
5093 1, 100, 1.0, 10.0,
5094 labels, values, G_N_ELEMENTS(labels),
5095 sp_ddc_width_value_changed, 1, 0 );
5096 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5097 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5098 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5099 }
5101 {
5102 /* Thinning */
5103 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
5104 gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
5105 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
5106 _("Stroke Thinning"), _("Thinning:"),
5107 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
5108 "/tools/calligraphic/thinning", 10,
5109 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5110 -100, 100, 1, 10.0,
5111 labels, values, G_N_ELEMENTS(labels),
5112 sp_ddc_velthin_value_changed, 1, 0);
5113 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5114 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5115 }
5117 {
5118 /* Angle */
5119 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
5120 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
5121 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
5122 _("Pen Angle"), _("Angle:"),
5123 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
5124 "/tools/calligraphic/angle", 30,
5125 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
5126 -90.0, 90.0, 1.0, 10.0,
5127 labels, values, G_N_ELEMENTS(labels),
5128 sp_ddc_angle_value_changed, 1, 0 );
5129 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5130 g_object_set_data( holder, "angle_action", eact );
5131 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5132 calligraphy_angle = eact;
5133 }
5135 {
5136 /* Fixation */
5137 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
5138 gdouble values[] = {0, 20, 40, 60, 90, 100};
5139 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
5140 _("Fixation"), _("Fixation:"),
5141 _("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"),
5142 "/tools/calligraphic/flatness", 90,
5143 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5144 0.0, 100, 1.0, 10.0,
5145 labels, values, G_N_ELEMENTS(labels),
5146 sp_ddc_flatness_value_changed, 1, 0);
5147 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5148 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5149 }
5151 {
5152 /* Cap Rounding */
5153 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
5154 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
5155 // TRANSLATORS: "cap" means "end" (both start and finish) here
5156 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
5157 _("Cap rounding"), _("Caps:"),
5158 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
5159 "/tools/calligraphic/cap_rounding", 0.0,
5160 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5161 0.0, 5.0, 0.01, 0.1,
5162 labels, values, G_N_ELEMENTS(labels),
5163 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
5164 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5165 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5166 }
5168 {
5169 /* Tremor */
5170 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
5171 gdouble values[] = {0, 10, 20, 40, 60, 100};
5172 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
5173 _("Stroke Tremor"), _("Tremor:"),
5174 _("Increase to make strokes rugged and trembling"),
5175 "/tools/calligraphic/tremor", 0.0,
5176 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5177 0.0, 100, 1, 10.0,
5178 labels, values, G_N_ELEMENTS(labels),
5179 sp_ddc_tremor_value_changed, 1, 0);
5181 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5182 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5183 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5184 }
5186 {
5187 /* Wiggle */
5188 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
5189 gdouble values[] = {0, 20, 40, 60, 100};
5190 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
5191 _("Pen Wiggle"), _("Wiggle:"),
5192 _("Increase to make the pen waver and wiggle"),
5193 "/tools/calligraphic/wiggle", 0.0,
5194 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5195 0.0, 100, 1, 10.0,
5196 labels, values, G_N_ELEMENTS(labels),
5197 sp_ddc_wiggle_value_changed, 1, 0);
5198 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5199 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5200 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5201 }
5203 {
5204 /* Mass */
5205 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
5206 gdouble values[] = {0.0, 2, 10, 20, 50, 100};
5207 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
5208 _("Pen Mass"), _("Mass:"),
5209 _("Increase to make the pen drag behind, as if slowed by inertia"),
5210 "/tools/calligraphic/mass", 2.0,
5211 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5212 0.0, 100, 1, 10.0,
5213 labels, values, G_N_ELEMENTS(labels),
5214 sp_ddc_mass_value_changed, 1, 0);
5215 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5216 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5217 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5218 }
5221 /* Trace Background button */
5222 {
5223 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
5224 _("Trace Background"),
5225 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
5226 INKSCAPE_ICON_DRAW_TRACE_BACKGROUND,
5227 Inkscape::ICON_SIZE_DECORATION );
5228 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5229 PrefPusher *pusher = new PrefPusher(GTK_TOGGLE_ACTION(act), "/tools/calligraphic/tracebackground", update_presets_list, holder);
5230 g_signal_connect( holder, "destroy", G_CALLBACK(delete_prefspusher), pusher);
5231 g_object_set_data( holder, "tracebackground", act );
5232 }
5234 /* Use Pressure button */
5235 {
5236 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
5237 _("Pressure"),
5238 _("Use the pressure of the input device to alter the width of the pen"),
5239 INKSCAPE_ICON_DRAW_USE_PRESSURE,
5240 Inkscape::ICON_SIZE_DECORATION );
5241 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5242 PrefPusher *pusher = new PrefPusher(GTK_TOGGLE_ACTION(act), "/tools/calligraphic/usepressure", update_presets_list, holder);
5243 g_signal_connect( holder, "destroy", G_CALLBACK(delete_prefspusher), pusher);
5244 g_object_set_data( holder, "usepressure", act );
5245 }
5247 /* Use Tilt button */
5248 {
5249 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
5250 _("Tilt"),
5251 _("Use the tilt of the input device to alter the angle of the pen's nib"),
5252 INKSCAPE_ICON_DRAW_USE_TILT,
5253 Inkscape::ICON_SIZE_DECORATION );
5254 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5255 PrefPusher *pusher = new PrefPusher(GTK_TOGGLE_ACTION(act), "/tools/calligraphic/usetilt", update_presets_list, holder);
5256 g_signal_connect( holder, "destroy", G_CALLBACK(delete_prefspusher), pusher);
5257 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
5258 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs->getBool("/tools/calligraphic/usetilt", true) );
5259 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usetilt", true) );
5260 g_object_set_data( holder, "usetilt", act );
5261 }
5263 /*calligraphic profile */
5264 {
5265 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5266 EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
5267 ege_select_one_action_set_appearance (act1, "compact");
5268 g_object_set_data (holder, "profile_selector", act1 );
5270 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
5272 sp_dcc_build_presets_list (holder);
5274 g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
5275 gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
5276 }
5277 }
5278 }
5281 //########################
5282 //## Circle / Arc ##
5283 //########################
5285 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
5286 {
5287 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
5288 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
5290 if (v1 == 0 && v2 == 0) {
5291 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
5292 gtk_action_set_sensitive( ocb, FALSE );
5293 gtk_action_set_sensitive( make_whole, FALSE );
5294 }
5295 } else {
5296 gtk_action_set_sensitive( ocb, TRUE );
5297 gtk_action_set_sensitive( make_whole, TRUE );
5298 }
5299 }
5301 static void
5302 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
5303 {
5304 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5306 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5307 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5308 prefs->setDouble(Glib::ustring("/tools/shapes/arc/") + value_name, adj->value);
5309 }
5311 // quit if run by the attr_changed listener
5312 if (g_object_get_data( tbl, "freeze" )) {
5313 return;
5314 }
5316 // in turn, prevent listener from responding
5317 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5319 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
5321 bool modmade = false;
5322 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5323 items != NULL;
5324 items = items->next)
5325 {
5326 SPItem *item = SP_ITEM(items->data);
5328 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
5330 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
5331 SPArc *arc = SP_ARC(item);
5333 if (!strcmp(value_name, "start")) {
5334 ge->start = (adj->value * M_PI)/ 180;
5335 } else {
5336 ge->end = (adj->value * M_PI)/ 180;
5337 }
5339 sp_genericellipse_normalize(ge);
5340 ((SPObject *)arc)->updateRepr();
5341 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
5343 modmade = true;
5344 }
5345 }
5347 g_free(namespaced_name);
5349 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
5351 sp_arctb_sensitivize( tbl, adj->value, other->value );
5353 if (modmade) {
5354 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
5355 _("Arc: Change start/end"));
5356 }
5358 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5359 }
5362 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
5363 {
5364 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
5365 }
5367 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
5368 {
5369 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
5370 }
5373 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
5374 {
5375 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5376 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5377 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5378 prefs->setBool("/tools/shapes/arc/open", ege_select_one_action_get_active(act) != 0);
5379 }
5381 // quit if run by the attr_changed listener
5382 if (g_object_get_data( tbl, "freeze" )) {
5383 return;
5384 }
5386 // in turn, prevent listener from responding
5387 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5389 bool modmade = false;
5391 if ( ege_select_one_action_get_active(act) != 0 ) {
5392 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5393 items != NULL;
5394 items = items->next)
5395 {
5396 if (SP_IS_ARC((SPItem *) items->data)) {
5397 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5398 repr->setAttribute("sodipodi:open", "true");
5399 SP_OBJECT((SPItem *) items->data)->updateRepr();
5400 modmade = true;
5401 }
5402 }
5403 } else {
5404 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5405 items != NULL;
5406 items = items->next)
5407 {
5408 if (SP_IS_ARC((SPItem *) items->data)) {
5409 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5410 repr->setAttribute("sodipodi:open", NULL);
5411 SP_OBJECT((SPItem *) items->data)->updateRepr();
5412 modmade = true;
5413 }
5414 }
5415 }
5417 if (modmade) {
5418 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
5419 _("Arc: Change open/closed"));
5420 }
5422 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5423 }
5425 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
5426 {
5427 GtkAdjustment *adj;
5428 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
5429 gtk_adjustment_set_value(adj, 0.0);
5430 gtk_adjustment_value_changed(adj);
5432 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
5433 gtk_adjustment_set_value(adj, 0.0);
5434 gtk_adjustment_value_changed(adj);
5436 spinbutton_defocus( GTK_OBJECT(obj) );
5437 }
5439 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const * /*name*/,
5440 gchar const * /*old_value*/, gchar const * /*new_value*/,
5441 bool /*is_interactive*/, gpointer data)
5442 {
5443 GObject *tbl = G_OBJECT(data);
5445 // quit if run by the _changed callbacks
5446 if (g_object_get_data( tbl, "freeze" )) {
5447 return;
5448 }
5450 // in turn, prevent callbacks from responding
5451 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5453 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
5454 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
5456 GtkAdjustment *adj1,*adj2;
5457 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
5458 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
5459 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
5460 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
5462 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
5464 char const *openstr = NULL;
5465 openstr = repr->attribute("sodipodi:open");
5466 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
5468 if (openstr) {
5469 ege_select_one_action_set_active( ocb, 1 );
5470 } else {
5471 ege_select_one_action_set_active( ocb, 0 );
5472 }
5474 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5475 }
5477 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
5478 NULL, /* child_added */
5479 NULL, /* child_removed */
5480 arc_tb_event_attr_changed,
5481 NULL, /* content_changed */
5482 NULL /* order_changed */
5483 };
5486 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
5487 {
5488 int n_selected = 0;
5489 Inkscape::XML::Node *repr = NULL;
5491 purge_repr_listener( tbl, tbl );
5493 for (GSList const *items = selection->itemList();
5494 items != NULL;
5495 items = items->next)
5496 {
5497 if (SP_IS_ARC((SPItem *) items->data)) {
5498 n_selected++;
5499 repr = SP_OBJECT_REPR((SPItem *) items->data);
5500 }
5501 }
5503 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
5505 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
5506 if (n_selected == 0) {
5507 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
5508 } else if (n_selected == 1) {
5509 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
5510 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5512 if (repr) {
5513 g_object_set_data( tbl, "repr", repr );
5514 Inkscape::GC::anchor(repr);
5515 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
5516 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
5517 }
5518 } else {
5519 // FIXME: implement averaging of all parameters for multiple selected
5520 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
5521 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5522 sp_arctb_sensitivize( tbl, 1, 0 );
5523 }
5524 }
5527 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5528 {
5529 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5531 EgeAdjustmentAction* eact = 0;
5532 Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
5535 {
5536 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
5537 ege_output_action_set_use_markup( act, TRUE );
5538 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5539 g_object_set_data( holder, "mode_action", act );
5540 }
5542 /* Start */
5543 {
5544 eact = create_adjustment_action( "ArcStartAction",
5545 _("Start"), _("Start:"),
5546 _("The angle (in degrees) from the horizontal to the arc's start point"),
5547 "/tools/shapes/arc/start", 0.0,
5548 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
5549 -360.0, 360.0, 1.0, 10.0,
5550 0, 0, 0,
5551 sp_arctb_start_value_changed);
5552 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5553 }
5555 /* End */
5556 {
5557 eact = create_adjustment_action( "ArcEndAction",
5558 _("End"), _("End:"),
5559 _("The angle (in degrees) from the horizontal to the arc's end point"),
5560 "/tools/shapes/arc/end", 0.0,
5561 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
5562 -360.0, 360.0, 1.0, 10.0,
5563 0, 0, 0,
5564 sp_arctb_end_value_changed);
5565 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5566 }
5568 /* Segments / Pie checkbox */
5569 {
5570 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5572 GtkTreeIter iter;
5573 gtk_list_store_append( model, &iter );
5574 gtk_list_store_set( model, &iter,
5575 0, _("Closed arc"),
5576 1, _("Switch to segment (closed shape with two radii)"),
5577 2, INKSCAPE_ICON_DRAW_ELLIPSE_SEGMENT,
5578 -1 );
5580 gtk_list_store_append( model, &iter );
5581 gtk_list_store_set( model, &iter,
5582 0, _("Open Arc"),
5583 1, _("Switch to arc (unclosed shape)"),
5584 2, INKSCAPE_ICON_DRAW_ELLIPSE_ARC,
5585 -1 );
5587 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5588 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5589 g_object_set_data( holder, "open_action", act );
5591 ege_select_one_action_set_appearance( act, "full" );
5592 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5593 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5594 ege_select_one_action_set_icon_column( act, 2 );
5595 ege_select_one_action_set_icon_size( act, secondarySize );
5596 ege_select_one_action_set_tooltip_column( act, 1 );
5598 bool isClosed = !prefs->getBool("/tools/shapes/arc/open", false);
5599 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
5600 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
5601 }
5603 /* Make Whole */
5604 {
5605 InkAction* inky = ink_action_new( "ArcResetAction",
5606 _("Make whole"),
5607 _("Make the shape a whole ellipse, not arc or segment"),
5608 INKSCAPE_ICON_DRAW_ELLIPSE_WHOLE,
5609 secondarySize );
5610 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
5611 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5612 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
5613 g_object_set_data( holder, "make_whole", inky );
5614 }
5616 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
5617 // sensitivize make whole and open checkbox
5618 {
5619 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
5620 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
5621 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
5622 }
5625 sigc::connection *connection = new sigc::connection(
5626 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
5627 );
5628 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
5629 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
5630 }
5635 // toggle button callbacks and updaters
5637 //########################
5638 //## Dropper ##
5639 //########################
5641 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl )
5642 {
5643 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5644 prefs->setInt( "/tools/dropper/pick", gtk_toggle_action_get_active( act ) );
5645 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
5646 if ( set_action ) {
5647 if ( gtk_toggle_action_get_active( act ) ) {
5648 gtk_action_set_sensitive( set_action, TRUE );
5649 } else {
5650 gtk_action_set_sensitive( set_action, FALSE );
5651 }
5652 }
5654 spinbutton_defocus(GTK_OBJECT(tbl));
5655 }
5657 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl )
5658 {
5659 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5660 prefs->setBool( "/tools/dropper/setalpha", gtk_toggle_action_get_active( act ) );
5661 spinbutton_defocus(GTK_OBJECT(tbl));
5662 }
5665 /**
5666 * Dropper auxiliary toolbar construction and setup.
5667 *
5668 * TODO: Would like to add swatch of current color.
5669 * TODO: Add queue of last 5 or so colors selected with new swatches so that
5670 * can drag and drop places. Will provide a nice mixing palette.
5671 */
5672 static void sp_dropper_toolbox_prep(SPDesktop * /*desktop*/, GtkActionGroup* mainActions, GObject* holder)
5673 {
5674 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5675 gint pickAlpha = prefs->getInt( "/tools/dropper/pick", 1 );
5677 {
5678 EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
5679 ege_output_action_set_use_markup( act, TRUE );
5680 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5681 }
5683 {
5684 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
5685 _("Pick opacity"),
5686 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
5687 NULL,
5688 Inkscape::ICON_SIZE_DECORATION );
5689 g_object_set( act, "short_label", _("Pick"), NULL );
5690 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5691 g_object_set_data( holder, "pick_action", act );
5692 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
5693 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
5694 }
5696 {
5697 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
5698 _("Assign opacity"),
5699 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
5700 NULL,
5701 Inkscape::ICON_SIZE_DECORATION );
5702 g_object_set( act, "short_label", _("Assign"), NULL );
5703 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5704 g_object_set_data( holder, "set_action", act );
5705 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/dropper/setalpha", true) );
5706 // make sure it's disabled if we're not picking alpha
5707 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
5708 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
5709 }
5710 }
5713 //########################
5714 //## LPETool ##
5715 //########################
5717 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
5719 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
5720 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
5721 {
5722 using namespace Inkscape::LivePathEffect;
5724 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
5725 SPEventContext *ec = desktop->event_context;
5726 if (!SP_IS_LPETOOL_CONTEXT(ec)) {
5727 return;
5728 }
5730 // only take action if run by the attr_changed listener
5731 if (!g_object_get_data(tbl, "freeze")) {
5732 // in turn, prevent listener from responding
5733 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5735 gint mode = ege_select_one_action_get_active(act);
5736 EffectType type = lpesubtools[mode].type;
5738 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5739 bool success = lpetool_try_construction(lc, type);
5740 if (success) {
5741 // since the construction was already performed, we set the state back to inactive
5742 ege_select_one_action_set_active(act, 0);
5743 mode = 0;
5744 } else {
5745 // switch to the chosen subtool
5746 SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
5747 }
5749 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5750 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5751 prefs->setInt( "/tools/lpetool/mode", mode );
5752 }
5754 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
5755 }
5756 }
5758 static void sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject * /*tbl*/)
5759 {
5760 SPEventContext *ec = selection->desktop()->event_context;
5761 if (SP_IS_LPETOOL_CONTEXT(ec)) {
5762 lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
5763 }
5764 }
5766 static void sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
5767 {
5768 using namespace Inkscape::LivePathEffect;
5769 SPEventContext *ec = selection->desktop()->event_context;
5770 if (!SP_IS_LPETOOL_CONTEXT(ec)) {
5771 return;
5772 }
5773 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
5775 lpetool_delete_measuring_items(lc);
5776 lpetool_create_measuring_items(lc, selection);
5778 // activate line segment combo box if a single item with LPELineSegment is selected
5779 GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
5780 SPItem *item = selection->singleItem();
5781 if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
5782 SPLPEItem *lpeitem = SP_LPE_ITEM(item);
5783 Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
5784 if (lpe && lpe->effectType() == LINE_SEGMENT) {
5785 LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
5786 g_object_set_data(tbl, "currentlpe", lpe);
5787 g_object_set_data(tbl, "currentlpeitem", lpeitem);
5788 gtk_action_set_sensitive(w, TRUE);
5789 ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
5790 } else {
5791 g_object_set_data(tbl, "currentlpe", NULL);
5792 g_object_set_data(tbl, "currentlpeitem", NULL);
5793 gtk_action_set_sensitive(w, FALSE);
5794 }
5795 } else {
5796 g_object_set_data(tbl, "currentlpe", NULL);
5797 g_object_set_data(tbl, "currentlpeitem", NULL);
5798 gtk_action_set_sensitive(w, FALSE);
5799 }
5800 }
5802 static void lpetool_toggle_show_bbox(GtkToggleAction *act, gpointer data) {
5803 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5804 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5806 bool show = gtk_toggle_action_get_active( act );
5807 prefs->setBool("/tools/lpetool/show_bbox", show);
5809 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5810 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5811 lpetool_context_reset_limiting_bbox(lc);
5812 }
5813 }
5815 static void lpetool_toggle_show_measuring_info(GtkToggleAction *act, GObject *tbl)
5816 {
5817 SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
5818 if (!tools_isactive(desktop, TOOLS_LPETOOL)) {
5819 return;
5820 }
5822 GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
5823 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5824 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5825 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5826 bool show = gtk_toggle_action_get_active( act );
5827 prefs->setBool("/tools/lpetool/show_measuring_info", show);
5828 lpetool_show_measuring_info(lc, show);
5829 gtk_action_set_sensitive(GTK_ACTION(unitact), show);
5830 }
5831 }
5833 static void lpetool_unit_changed(GtkAction* /*act*/, GObject* tbl)
5834 {
5835 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
5836 SPUnit const *unit = tracker->getActiveUnit();
5837 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5838 prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5840 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5841 if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
5842 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5843 lpetool_delete_measuring_items(lc);
5844 lpetool_create_measuring_items(lc);
5845 }
5846 }
5848 static void lpetool_toggle_set_bbox(GtkToggleAction *act, gpointer data)
5849 {
5850 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5851 Inkscape::Selection *selection = desktop->selection;
5853 Geom::OptRect bbox = selection->bounds();
5855 if (bbox) {
5856 Geom::Point A(bbox->min());
5857 Geom::Point B(bbox->max());
5859 A *= desktop->doc2dt();
5860 B *= desktop->doc2dt();
5862 // TODO: should we provide a way to store points in prefs?
5863 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5864 prefs->setDouble("/tools/lpetool/bbox_upperleftx", A[Geom::X]);
5865 prefs->setDouble("/tools/lpetool/bbox_upperlefty", A[Geom::Y]);
5866 prefs->setDouble("/tools/lpetool/bbox_lowerrightx", B[Geom::X]);
5867 prefs->setDouble("/tools/lpetool/bbox_lowerrighty", B[Geom::Y]);
5869 lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
5870 }
5872 gtk_toggle_action_set_active(act, false);
5873 }
5875 static void sp_line_segment_build_list(GObject *tbl)
5876 {
5877 g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
5879 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
5880 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
5881 gtk_list_store_clear (model);
5883 // TODO: we add the entries of rht combo box manually; later this should be done automatically
5884 {
5885 GtkTreeIter iter;
5886 gtk_list_store_append( model, &iter );
5887 gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
5888 gtk_list_store_append( model, &iter );
5889 gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
5890 gtk_list_store_append( model, &iter );
5891 gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
5892 gtk_list_store_append( model, &iter );
5893 gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
5894 }
5896 g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5897 }
5899 static void sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl)
5900 {
5901 using namespace Inkscape::LivePathEffect;
5903 // quit if run by the attr_changed listener
5904 if (g_object_get_data(tbl, "freeze")) {
5905 return;
5906 }
5908 // in turn, prevent listener from responding
5909 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5911 LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
5912 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5913 if (lpeitem) {
5914 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5915 lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
5916 sp_lpe_item_update_patheffect(lpeitem, true, true);
5917 }
5919 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5920 }
5922 static void lpetool_open_lpe_dialog(GtkToggleAction *act, gpointer data)
5923 {
5924 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5926 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5927 sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5928 }
5929 gtk_toggle_action_set_active(act, false);
5930 }
5932 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5933 {
5934 UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5935 tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5936 g_object_set_data(holder, "tracker", tracker);
5937 SPUnit const *unit = tracker->getActiveUnit();
5939 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5940 prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5942 /** Automatically create a list of LPEs that get added to the toolbar **/
5943 {
5944 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5946 GtkTreeIter iter;
5948 // the first toggle button represents the state that no subtool is active (remove this when
5949 // this can be modeled by EgeSelectOneAction or some other action)
5950 gtk_list_store_append( model, &iter );
5951 gtk_list_store_set( model, &iter,
5952 0, _("All inactive"),
5953 1, _("No geometric tool is active"),
5954 2, "draw-geometry-inactive",
5955 -1 );
5957 Inkscape::LivePathEffect::EffectType type;
5958 for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5959 type = lpesubtools[i].type;
5960 gtk_list_store_append( model, &iter );
5961 gtk_list_store_set( model, &iter,
5962 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5963 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5964 2, lpesubtools[i].icon_name,
5965 -1 );
5966 }
5968 EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5969 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5970 g_object_set_data( holder, "lpetool_mode_action", act );
5972 ege_select_one_action_set_appearance( act, "full" );
5973 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5974 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5975 ege_select_one_action_set_icon_column( act, 2 );
5976 ege_select_one_action_set_tooltip_column( act, 1 );
5978 gint lpeToolMode = prefs->getInt("/tools/lpetool/mode", 0);
5979 ege_select_one_action_set_active( act, lpeToolMode );
5980 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5981 }
5983 /* Show limiting bounding box */
5984 {
5985 InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5986 _("Show limiting bounding box"),
5987 _("Show bounding box (used to cut infinite lines)"),
5988 "show-bounding-box",
5989 Inkscape::ICON_SIZE_DECORATION );
5990 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5991 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5992 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_bbox", true ) );
5993 }
5995 /* Set limiting bounding box to bbox of current selection */
5996 {
5997 InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5998 _("Get limiting bounding box from selection"),
5999 _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
6000 "draw-geometry-set-bounding-box",
6001 Inkscape::ICON_SIZE_DECORATION );
6002 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6003 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
6004 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
6005 }
6008 /* Combo box to choose line segment type */
6009 {
6010 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
6011 EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
6012 ege_select_one_action_set_appearance (act, "compact");
6013 g_object_set_data (holder, "lpetool_line_segment_action", act );
6015 g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
6017 sp_line_segment_build_list (holder);
6019 g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
6020 gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
6021 gtk_action_group_add_action(mainActions, GTK_ACTION(act));
6022 }
6024 /* Display measuring info for selected items */
6025 {
6026 InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
6027 _("Display measuring info"),
6028 _("Display measuring info for selected items"),
6029 "draw-geometry-show-measuring-info",
6030 Inkscape::ICON_SIZE_DECORATION );
6031 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6032 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
6033 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_measuring_info", true ) );
6034 }
6036 // add the units menu
6037 {
6038 GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
6039 gtk_action_group_add_action( mainActions, act );
6040 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
6041 g_object_set_data(holder, "lpetool_units_action", act);
6042 gtk_action_set_sensitive(act, prefs->getBool("/tools/lpetool/show_measuring_info", true));
6043 }
6045 /* Open LPE dialog (to adapt parameters numerically) */
6046 {
6047 InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
6048 _("Open LPE dialog"),
6049 _("Open LPE dialog (to adapt parameters numerically)"),
6050 "dialog-geometry",
6051 Inkscape::ICON_SIZE_DECORATION );
6052 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6053 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
6054 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
6055 }
6057 //watch selection
6058 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
6060 sigc::connection *c_selection_modified =
6061 new sigc::connection (sp_desktop_selection (desktop)->connectModified
6062 (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
6063 pool->add_connection ("selection-modified", c_selection_modified);
6065 sigc::connection *c_selection_changed =
6066 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
6067 (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
6068 pool->add_connection ("selection-changed", c_selection_changed);
6069 }
6071 //########################
6072 //## Eraser ##
6073 //########################
6075 static void sp_erc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
6076 {
6077 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6078 prefs->setDouble( "/tools/eraser/width", adj->value );
6079 update_presets_list(tbl);
6080 }
6082 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
6083 {
6084 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
6085 bool eraserMode = ege_select_one_action_get_active( act ) != 0;
6086 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
6087 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6088 prefs->setBool( "/tools/eraser/mode", eraserMode );
6089 }
6091 // only take action if run by the attr_changed listener
6092 if (!g_object_get_data( tbl, "freeze" )) {
6093 // in turn, prevent listener from responding
6094 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6096 if ( eraserMode != 0 ) {
6097 } else {
6098 }
6099 // TODO finish implementation
6101 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6102 }
6103 }
6105 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6106 {
6107 {
6108 /* Width */
6109 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
6110 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
6111 EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
6112 _("Pen Width"), _("Width:"),
6113 _("The width of the eraser pen (relative to the visible canvas area)"),
6114 "/tools/eraser/width", 15,
6115 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
6116 1, 100, 1.0, 10.0,
6117 labels, values, G_N_ELEMENTS(labels),
6118 sp_erc_width_value_changed, 1, 0);
6119 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6120 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6121 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
6122 }
6124 {
6125 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
6127 GtkTreeIter iter;
6128 gtk_list_store_append( model, &iter );
6129 gtk_list_store_set( model, &iter,
6130 0, _("Delete"),
6131 1, _("Delete objects touched by the eraser"),
6132 2, INKSCAPE_ICON_DRAW_ERASER_DELETE_OBJECTS,
6133 -1 );
6135 gtk_list_store_append( model, &iter );
6136 gtk_list_store_set( model, &iter,
6137 0, _("Cut"),
6138 1, _("Cut out from objects"),
6139 2, INKSCAPE_ICON_PATH_DIFFERENCE,
6140 -1 );
6142 EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
6143 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
6144 g_object_set_data( holder, "eraser_mode_action", act );
6146 ege_select_one_action_set_appearance( act, "full" );
6147 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
6148 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
6149 ege_select_one_action_set_icon_column( act, 2 );
6150 ege_select_one_action_set_tooltip_column( act, 1 );
6152 /// @todo Convert to boolean?
6153 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6154 gint eraserMode = prefs->getBool("/tools/eraser/mode") ? 1 : 0;
6155 ege_select_one_action_set_active( act, eraserMode );
6156 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
6157 }
6159 }
6161 //########################
6162 //## Text Toolbox ##
6163 //########################
6164 /*
6165 static void sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
6166 {
6167 //Call back for letter sizing spinbutton
6168 }
6170 static void sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
6171 {
6172 //Call back for line height spinbutton
6173 }
6175 static void sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
6176 {
6177 //Call back for horizontal kerning spinbutton
6178 }
6180 static void sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
6181 {
6182 //Call back for vertical kerning spinbutton
6183 }
6185 static void sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
6186 {
6187 //Call back for letter rotation spinbutton
6188 }*/
6190 namespace {
6192 static void sp_text_toolbox_selection_changed(Inkscape::Selection * /*selection*/, GObject *tbl)
6193 {
6194 // quit if run by the _changed callbacks
6195 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6196 return;
6197 }
6199 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6201 SPStyle *query =
6202 sp_style_new (SP_ACTIVE_DOCUMENT);
6204 int result_family =
6205 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6207 int result_style =
6208 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6210 int result_numbers =
6211 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6213 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6215 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6216 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
6217 // there are no texts in selection, read from prefs
6219 sp_style_read_from_prefs(query, "/tools/text");
6221 if (g_object_get_data(tbl, "text_style_from_prefs")) {
6222 // do not reset the toolbar style from prefs if we already did it last time
6223 sp_style_unref(query);
6224 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6225 return;
6226 }
6227 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
6228 } else {
6229 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
6230 }
6232 if (query->text)
6233 {
6234 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
6235 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6236 gtk_entry_set_text (GTK_ENTRY (entry), "");
6238 } else if (query->text->font_specification.value || query->text->font_family.value) {
6240 Gtk::ComboBoxEntry *combo = (Gtk::ComboBoxEntry *) (g_object_get_data (G_OBJECT (tbl), "family-entry-combo"));
6241 GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6243 // Get the font that corresponds
6244 Glib::ustring familyName;
6246 font_instance * font = font_factory::Default()->FaceFromStyle(query);
6247 if (font) {
6248 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
6249 font->Unref();
6250 font = NULL;
6251 }
6253 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
6255 Gtk::TreeIter iter;
6256 try {
6257 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
6258 Glib::RefPtr<Gtk::TreeModel> model = combo->get_model();
6259 iter = model->get_iter(path);
6260 } catch (...) {
6261 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
6262 sp_style_unref(query);
6263 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6264 return;
6265 }
6267 combo->set_active (iter);
6268 }
6270 //Size
6271 {
6272 GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
6273 gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
6274 gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
6275 g_free(str);
6276 }
6278 //Anchor
6279 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
6280 {
6281 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
6282 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6283 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6284 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6285 } else {
6286 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
6287 {
6288 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
6289 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6290 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6291 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6292 } else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE) {
6293 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
6294 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6295 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6296 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6297 } else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END) {
6298 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
6299 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6300 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6301 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6302 }
6303 }
6305 //Style
6306 {
6307 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
6309 gboolean active = gtk_toggle_button_get_active (button);
6310 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));
6312 if (active != check)
6313 {
6314 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6315 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6316 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6317 }
6318 }
6320 {
6321 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
6323 gboolean active = gtk_toggle_button_get_active (button);
6324 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
6326 if (active != check)
6327 {
6328 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6329 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6330 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6331 }
6332 }
6334 //Orientation
6335 //locking both buttons, changing one affect all group (both)
6336 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
6337 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6339 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
6340 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
6342 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB) {
6343 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6344 } else {
6345 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
6346 }
6347 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6348 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
6349 }
6351 sp_style_unref(query);
6353 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6354 }
6356 static void sp_text_toolbox_selection_modified(Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
6357 {
6358 sp_text_toolbox_selection_changed (selection, tbl);
6359 }
6361 static void sp_text_toolbox_subselection_changed(gpointer /*tc*/, GObject *tbl)
6362 {
6363 sp_text_toolbox_selection_changed (NULL, tbl);
6364 }
6366 static void sp_text_toolbox_family_changed(GtkComboBoxEntry *,
6367 GObject *tbl)
6368 {
6369 // quit if run by the _changed callbacks
6370 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6371 return;
6372 }
6374 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6376 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6377 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6378 const gchar* family = gtk_entry_get_text (GTK_ENTRY (entry));
6380 //g_print ("family changed to: %s\n", family);
6382 SPStyle *query =
6383 sp_style_new (SP_ACTIVE_DOCUMENT);
6385 int result_fontspec =
6386 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6388 SPCSSAttr *css = sp_repr_css_attr_new ();
6390 // First try to get the font spec from the stored value
6391 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
6393 if (fontSpec.empty()) {
6394 // Construct a new font specification if it does not yet exist
6395 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6396 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6397 fontFromStyle->Unref();
6398 }
6400 if (!fontSpec.empty()) {
6402 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
6404 if (!newFontSpec.empty()) {
6406 if (fontSpec != newFontSpec) {
6408 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
6410 if (font) {
6411 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6413 // Set all the these just in case they were altered when finding the best
6414 // match for the new family and old style...
6416 gchar c[256];
6418 font->Family(c, 256);
6420 sp_repr_css_set_property (css, "font-family", c);
6422 font->Attribute( "weight", c, 256);
6423 sp_repr_css_set_property (css, "font-weight", c);
6425 font->Attribute("style", c, 256);
6426 sp_repr_css_set_property (css, "font-style", c);
6428 font->Attribute("stretch", c, 256);
6429 sp_repr_css_set_property (css, "font-stretch", c);
6431 font->Attribute("variant", c, 256);
6432 sp_repr_css_set_property (css, "font-variant", c);
6434 font->Unref();
6435 }
6436 }
6438 } else {
6439 // If the old font on selection (or default) was not existing on the system,
6440 // ReplaceFontSpecificationFamily does not work. In that case we fall back to blindly
6441 // setting the family reported by the family chooser.
6443 //g_print ("fallback setting family: %s\n", family);
6444 sp_repr_css_set_property (css, "-inkscape-font-specification", family);
6445 sp_repr_css_set_property (css, "font-family", family);
6446 }
6447 }
6449 // If querying returned nothing, set the default style of the tool (for new texts)
6450 if (result_fontspec == QUERY_STYLE_NOTHING) {
6451 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6452 prefs->mergeStyle("/tools/text/style", css);
6453 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
6454 } else {
6455 sp_desktop_set_style (desktop, css, true, true);
6456 }
6458 sp_style_unref(query);
6460 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6461 _("Text: Change font family"));
6462 sp_repr_css_attr_unref (css);
6464 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6466 // unfreeze
6467 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6469 // focus to canvas
6470 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6471 }
6474 static void sp_text_toolbox_anchoring_toggled(GtkRadioButton *button,
6475 gpointer data)
6476 {
6477 if (g_object_get_data (G_OBJECT (button), "block")) {
6478 return;
6479 }
6480 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) {
6481 return;
6482 }
6483 int prop = GPOINTER_TO_INT(data);
6485 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6487 // move the x of all texts to preserve the same bbox
6488 Inkscape::Selection *selection = sp_desktop_selection(desktop);
6489 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
6490 if (SP_IS_TEXT((SPItem *) items->data)) {
6491 SPItem *item = SP_ITEM(items->data);
6493 unsigned writing_mode = SP_OBJECT_STYLE(item)->writing_mode.value;
6494 // below, variable names suggest horizontal move, but we check the writing direction
6495 // and move in the corresponding axis
6496 int axis;
6497 if (writing_mode == SP_CSS_WRITING_MODE_LR_TB || writing_mode == SP_CSS_WRITING_MODE_RL_TB) {
6498 axis = NR::X;
6499 } else {
6500 axis = NR::Y;
6501 }
6503 Geom::OptRect bbox
6504 = item->getBounds(Geom::identity(), SPItem::GEOMETRIC_BBOX);
6505 if (!bbox) {
6506 continue;
6507 }
6508 double width = bbox->dimensions()[axis];
6509 // If you want to align within some frame, other than the text's own bbox, calculate
6510 // the left and right (or top and bottom for tb text) slacks of the text inside that
6511 // frame (currently unused)
6512 double left_slack = 0;
6513 double right_slack = 0;
6514 unsigned old_align = SP_OBJECT_STYLE(item)->text_align.value;
6515 double move = 0;
6516 if (old_align == SP_CSS_TEXT_ALIGN_START || old_align == SP_CSS_TEXT_ALIGN_LEFT) {
6517 switch (prop) {
6518 case 0:
6519 move = -left_slack;
6520 break;
6521 case 1:
6522 move = width/2 + (right_slack - left_slack)/2;
6523 break;
6524 case 2:
6525 move = width + right_slack;
6526 break;
6527 }
6528 } else if (old_align == SP_CSS_TEXT_ALIGN_CENTER) {
6529 switch (prop) {
6530 case 0:
6531 move = -width/2 - left_slack;
6532 break;
6533 case 1:
6534 move = (right_slack - left_slack)/2;
6535 break;
6536 case 2:
6537 move = width/2 + right_slack;
6538 break;
6539 }
6540 } else if (old_align == SP_CSS_TEXT_ALIGN_END || old_align == SP_CSS_TEXT_ALIGN_RIGHT) {
6541 switch (prop) {
6542 case 0:
6543 move = -width - left_slack;
6544 break;
6545 case 1:
6546 move = -width/2 + (right_slack - left_slack)/2;
6547 break;
6548 case 2:
6549 move = right_slack;
6550 break;
6551 }
6552 }
6553 Geom::Point XY = SP_TEXT(item)->attributes.firstXY();
6554 if (axis == NR::X) {
6555 XY = XY + Geom::Point (move, 0);
6556 } else {
6557 XY = XY + Geom::Point (0, move);
6558 }
6559 SP_TEXT(item)->attributes.setFirstXY(XY);
6560 SP_OBJECT(item)->updateRepr();
6561 SP_OBJECT(item)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
6562 }
6563 }
6565 SPCSSAttr *css = sp_repr_css_attr_new ();
6566 switch (prop)
6567 {
6568 case 0:
6569 {
6570 sp_repr_css_set_property (css, "text-anchor", "start");
6571 sp_repr_css_set_property (css, "text-align", "start");
6572 break;
6573 }
6574 case 1:
6575 {
6576 sp_repr_css_set_property (css, "text-anchor", "middle");
6577 sp_repr_css_set_property (css, "text-align", "center");
6578 break;
6579 }
6581 case 2:
6582 {
6583 sp_repr_css_set_property (css, "text-anchor", "end");
6584 sp_repr_css_set_property (css, "text-align", "end");
6585 break;
6586 }
6588 case 3:
6589 {
6590 sp_repr_css_set_property (css, "text-anchor", "start");
6591 sp_repr_css_set_property (css, "text-align", "justify");
6592 break;
6593 }
6594 }
6596 SPStyle *query =
6597 sp_style_new (SP_ACTIVE_DOCUMENT);
6598 int result_numbers =
6599 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6601 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6602 if (result_numbers == QUERY_STYLE_NOTHING)
6603 {
6604 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6605 prefs->mergeStyle("/tools/text/style", css);
6606 }
6608 sp_style_unref(query);
6610 sp_desktop_set_style (desktop, css, true, true);
6611 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6612 _("Text: Change alignment"));
6613 sp_repr_css_attr_unref (css);
6615 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6616 }
6618 static void sp_text_toolbox_style_toggled(GtkToggleButton *button,
6619 gpointer data)
6620 {
6621 if (g_object_get_data (G_OBJECT (button), "block")) {
6622 return;
6623 }
6625 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6626 SPCSSAttr *css = sp_repr_css_attr_new ();
6627 int prop = GPOINTER_TO_INT(data);
6628 bool active = gtk_toggle_button_get_active (button);
6630 SPStyle *query =
6631 sp_style_new (SP_ACTIVE_DOCUMENT);
6633 int result_fontspec =
6634 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6636 //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6637 //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6638 //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6640 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
6641 Glib::ustring newFontSpec = "";
6643 if (fontSpec.empty()) {
6644 // Construct a new font specification if it does not yet exist
6645 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6646 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6647 fontFromStyle->Unref();
6648 }
6650 bool nochange = true;
6651 switch (prop)
6652 {
6653 case 0:
6654 {
6655 if (!fontSpec.empty()) {
6656 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
6657 if (!newFontSpec.empty()) {
6658 // Don't even set the bold if the font didn't exist on the system
6659 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
6660 nochange = false;
6661 }
6662 }
6663 // set or reset the button according
6664 if(nochange) {
6665 gboolean check = gtk_toggle_button_get_active (button);
6667 if (active != check)
6668 {
6669 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6670 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6671 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6672 }
6673 }
6675 break;
6676 }
6678 case 1:
6679 {
6680 if (!fontSpec.empty()) {
6681 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
6682 if (!newFontSpec.empty()) {
6683 // Don't even set the italic if the font didn't exist on the system
6684 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
6685 nochange = false;
6686 }
6687 }
6688 if(nochange) {
6689 gboolean check = gtk_toggle_button_get_active (button);
6691 if (active != check)
6692 {
6693 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6694 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6695 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6696 }
6697 }
6698 break;
6699 }
6700 }
6702 if (!newFontSpec.empty()) {
6703 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6704 }
6706 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6707 if (result_fontspec == QUERY_STYLE_NOTHING)
6708 {
6709 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6710 prefs->mergeStyle("/tools/text/style", css);
6711 }
6713 sp_style_unref(query);
6715 sp_desktop_set_style (desktop, css, true, true);
6716 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6717 _("Text: Change font style"));
6718 sp_repr_css_attr_unref (css);
6720 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6721 }
6723 static void sp_text_toolbox_orientation_toggled(GtkRadioButton *button,
6724 gpointer data)
6725 {
6726 if (g_object_get_data (G_OBJECT (button), "block")) {
6727 return;
6728 }
6730 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6731 SPCSSAttr *css = sp_repr_css_attr_new ();
6732 int prop = GPOINTER_TO_INT(data);
6734 switch (prop)
6735 {
6736 case 0:
6737 {
6738 sp_repr_css_set_property (css, "writing-mode", "lr");
6739 break;
6740 }
6742 case 1:
6743 {
6744 sp_repr_css_set_property (css, "writing-mode", "tb");
6745 break;
6746 }
6747 }
6749 SPStyle *query =
6750 sp_style_new (SP_ACTIVE_DOCUMENT);
6751 int result_numbers =
6752 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6754 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6755 if (result_numbers == QUERY_STYLE_NOTHING)
6756 {
6757 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6758 prefs->mergeStyle("/tools/text/style", css);
6759 }
6761 sp_desktop_set_style (desktop, css, true, true);
6762 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6763 _("Text: Change orientation"));
6764 sp_repr_css_attr_unref (css);
6766 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6767 }
6769 static gboolean sp_text_toolbox_family_keypress(GtkWidget * /*w*/, GdkEventKey *event, GObject *tbl)
6770 {
6771 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6772 if (!desktop) {
6773 return FALSE;
6774 }
6776 switch (get_group0_keyval (event)) {
6777 case GDK_KP_Enter: // chosen
6778 case GDK_Return:
6779 // unfreeze and update, which will defocus
6780 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6781 sp_text_toolbox_family_changed (NULL, tbl);
6782 return TRUE; // I consumed the event
6783 break;
6784 case GDK_Escape:
6785 // defocus
6786 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6787 return TRUE; // I consumed the event
6788 break;
6789 }
6790 return FALSE;
6791 }
6793 static gboolean sp_text_toolbox_family_list_keypress(GtkWidget * /*w*/, GdkEventKey *event, GObject * /*tbl*/)
6794 {
6795 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6796 if (!desktop) {
6797 return FALSE;
6798 }
6800 switch (get_group0_keyval (event)) {
6801 case GDK_KP_Enter:
6802 case GDK_Return:
6803 case GDK_Escape: // defocus
6804 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6805 return TRUE; // I consumed the event
6806 break;
6807 case GDK_w:
6808 case GDK_W:
6809 if (event->state & GDK_CONTROL_MASK) {
6810 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6811 return TRUE; // I consumed the event
6812 }
6813 break;
6814 }
6815 return FALSE;
6816 }
6819 static void sp_text_toolbox_size_changed(GtkComboBox *cbox,
6820 GObject *tbl)
6821 {
6822 // quit if run by the _changed callbacks
6823 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6824 return;
6825 }
6827 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6829 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6831 // If this is not from selecting a size in the list (in which case get_active will give the
6832 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
6833 // process this event. This fixes GTK's stupid insistence on sending an activate change every
6834 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
6835 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed")) {
6836 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6837 return;
6838 }
6840 gdouble value = -1;
6841 {
6842 gchar *endptr;
6843 gchar *const text = gtk_combo_box_get_active_text(cbox);
6844 if (text) {
6845 value = g_strtod(text, &endptr);
6846 if (endptr == text) { // Conversion failed, non-numeric input.
6847 value = -1;
6848 }
6849 g_free(text);
6850 }
6851 }
6852 if (value <= 0) {
6853 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6854 return; // could not parse value
6855 }
6857 SPCSSAttr *css = sp_repr_css_attr_new ();
6858 Inkscape::CSSOStringStream osfs;
6859 osfs << value;
6860 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
6862 SPStyle *query =
6863 sp_style_new (SP_ACTIVE_DOCUMENT);
6864 int result_numbers =
6865 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6867 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6868 if (result_numbers == QUERY_STYLE_NOTHING)
6869 {
6870 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6871 prefs->mergeStyle("/tools/text/style", css);
6872 }
6874 sp_style_unref(query);
6876 sp_desktop_set_style (desktop, css, true, true);
6877 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
6878 _("Text: Change font size"));
6879 sp_repr_css_attr_unref (css);
6881 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6883 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6884 }
6886 static gboolean sp_text_toolbox_size_focusout(GtkWidget * /*w*/, GdkEventFocus * /*event*/, GObject *tbl)
6887 {
6888 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6889 if (!desktop) {
6890 return FALSE;
6891 }
6893 if (!g_object_get_data (tbl, "esc-pressed")) {
6894 g_object_set_data (tbl, "enter-pressed", gpointer(1));
6895 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6896 sp_text_toolbox_size_changed (cbox, tbl);
6897 g_object_set_data (tbl, "enter-pressed", gpointer(0));
6898 }
6899 return FALSE; // I consumed the event
6900 }
6903 static gboolean sp_text_toolbox_size_keypress(GtkWidget * /*w*/, GdkEventKey *event, GObject *tbl)
6904 {
6905 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6906 if (!desktop) {
6907 return FALSE;
6908 }
6910 switch (get_group0_keyval (event)) {
6911 case GDK_Escape: // defocus
6912 g_object_set_data (tbl, "esc-pressed", gpointer(1));
6913 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6914 g_object_set_data (tbl, "esc-pressed", gpointer(0));
6915 return TRUE; // I consumed the event
6916 break;
6917 case GDK_Return: // defocus
6918 case GDK_KP_Enter:
6919 g_object_set_data (tbl, "enter-pressed", gpointer(1));
6920 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6921 sp_text_toolbox_size_changed (cbox, tbl);
6922 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6923 g_object_set_data (tbl, "enter-pressed", gpointer(0));
6924 return TRUE; // I consumed the event
6925 break;
6926 }
6927 return FALSE;
6928 }
6930 // While editing font name in the entry, disable family_changed by freezing, otherwise completion
6931 // does not work!
6932 static gboolean sp_text_toolbox_entry_focus_in(GtkWidget *entry,
6933 GdkEventFocus * /*event*/,
6934 GObject *tbl)
6935 {
6936 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6937 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1); // select all
6938 return FALSE;
6939 }
6941 static gboolean sp_text_toolbox_entry_focus_out(GtkWidget *entry,
6942 GdkEventFocus * /*event*/,
6943 GObject *tbl)
6944 {
6945 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6946 gtk_entry_select_region (GTK_ENTRY (entry), 0, 0); // deselect
6947 return FALSE;
6948 }
6950 static void cell_data_func(GtkCellLayout * /*cell_layout*/,
6951 GtkCellRenderer *cell,
6952 GtkTreeModel *tree_model,
6953 GtkTreeIter *iter,
6954 gpointer /*data*/)
6955 {
6956 gchar *family;
6957 gtk_tree_model_get(tree_model, iter, 0, &family, -1);
6958 gchar *const family_escaped = g_markup_escape_text(family, -1);
6960 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6961 int show_sample = prefs->getInt("/tools/text/show_sample_in_list", 1);
6962 if (show_sample) {
6964 Glib::ustring sample = prefs->getString("/tools/text/font_sample");
6965 gchar *const sample_escaped = g_markup_escape_text(sample.data(), -1);
6967 std::stringstream markup;
6968 markup << family_escaped << " <span foreground='darkgray' font_family='"
6969 << family_escaped << "'>" << sample_escaped << "</span>";
6970 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6972 g_free(sample_escaped);
6973 } else {
6974 g_object_set (G_OBJECT (cell), "markup", family_escaped, NULL);
6975 }
6977 g_free(family);
6978 g_free(family_escaped);
6979 }
6981 static gboolean text_toolbox_completion_match_selected(GtkEntryCompletion * /*widget*/,
6982 GtkTreeModel *model,
6983 GtkTreeIter *iter,
6984 GObject *tbl)
6985 {
6986 // We intercept this signal so as to fire family_changed at once (without it, you'd have to
6987 // press Enter again after choosing a completion)
6988 gchar *family = 0;
6989 gtk_tree_model_get(model, iter, 0, &family, -1);
6991 GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6992 gtk_entry_set_text (GTK_ENTRY (entry), family);
6994 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6995 sp_text_toolbox_family_changed (NULL, tbl);
6996 return TRUE;
6997 }
7000 static void cbe_add_completion(GtkComboBoxEntry *cbe, GObject *tbl)
7001 {
7002 GtkEntry *entry;
7003 GtkEntryCompletion *completion;
7004 GtkTreeModel *model;
7006 entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(cbe)));
7007 completion = gtk_entry_completion_new();
7008 model = gtk_combo_box_get_model(GTK_COMBO_BOX(cbe));
7009 gtk_entry_completion_set_model(completion, model);
7010 gtk_entry_completion_set_text_column(completion, 0);
7011 gtk_entry_completion_set_inline_completion(completion, FALSE);
7012 gtk_entry_completion_set_inline_selection(completion, FALSE);
7013 gtk_entry_completion_set_popup_completion(completion, TRUE);
7014 gtk_entry_set_completion(entry, completion);
7016 g_signal_connect (G_OBJECT (completion), "match-selected", G_CALLBACK (text_toolbox_completion_match_selected), tbl);
7018 g_object_unref(completion);
7019 }
7021 static void sp_text_toolbox_family_popnotify(GtkComboBox *widget,
7022 void * /*property*/,
7023 GObject *tbl)
7024 {
7025 // while the drop-down is open, we disable font family changing, reenabling it only when it closes
7027 gboolean shown;
7028 g_object_get (G_OBJECT(widget), "popup-shown", &shown, NULL);
7029 if (shown) {
7030 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7031 //g_print("POP: notify: SHOWN\n");
7032 } else {
7033 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7035 // stupid GTK doesn't let us attach to events in the drop-down window, so we peek here to
7036 // find out if the drop down was closed by Enter and if so, manually update (only
7037 // necessary on Windows, on Linux it updates itself - what a mess, but we'll manage)
7038 GdkEvent *ev = gtk_get_current_event();
7039 if (ev) {
7040 //g_print ("ev type: %d\n", ev->type);
7041 if (ev->type == GDK_KEY_PRESS) {
7042 switch (get_group0_keyval ((GdkEventKey *) ev)) {
7043 case GDK_KP_Enter: // chosen
7044 case GDK_Return:
7045 {
7046 // make sure the chosen one is inserted into the entry
7047 GtkComboBox *combo = GTK_COMBO_BOX (((Gtk::ComboBox *) (g_object_get_data (tbl, "family-entry-combo")))->gobj());
7048 GtkTreeModel *model = gtk_combo_box_get_model(combo);
7049 GtkTreeIter iter;
7050 gboolean has_active = gtk_combo_box_get_active_iter (combo, &iter);
7051 if (has_active) {
7052 gchar *family;
7053 gtk_tree_model_get(model, &iter, 0, &family, -1);
7054 GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
7055 gtk_entry_set_text (GTK_ENTRY (entry), family);
7056 }
7058 // update
7059 sp_text_toolbox_family_changed (NULL, tbl);
7060 break;
7061 }
7062 }
7063 }
7064 }
7066 // regardless of whether we updated, defocus the widget
7067 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
7068 if (desktop) {
7069 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
7070 }
7071 //g_print("POP: notify: HIDDEN\n");
7072 }
7073 }
7075 GtkWidget *sp_text_toolbox_new(SPDesktop *desktop)
7076 {
7077 GtkToolbar *tbl = GTK_TOOLBAR(gtk_toolbar_new());
7078 GtkIconSize secondarySize = static_cast<GtkIconSize>(ToolboxFactory::prefToSize("/toolbox/secondary", 1));
7080 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
7081 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
7083 GtkTooltips *tt = gtk_tooltips_new();
7085 ////////////Family
7086 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
7087 Gtk::ComboBoxEntry *font_sel = Gtk::manage(new Gtk::ComboBoxEntry(store));
7089 gtk_rc_parse_string (
7090 "style \"dropdown-as-list-style\"\n"
7091 "{\n"
7092 " GtkComboBox::appears-as-list = 1\n"
7093 "}\n"
7094 "widget \"*.toolbox-fontfamily-list\" style \"dropdown-as-list-style\"");
7095 gtk_widget_set_name(GTK_WIDGET (font_sel->gobj()), "toolbox-fontfamily-list");
7096 gtk_tooltips_set_tip (tt, GTK_WIDGET (font_sel->gobj()), _("Select font family (Alt+X to access)"), "");
7098 g_signal_connect (G_OBJECT (font_sel->gobj()), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
7100 cbe_add_completion(font_sel->gobj(), G_OBJECT(tbl));
7102 gtk_toolbar_append_widget( tbl, (GtkWidget*) font_sel->gobj(), "", "");
7103 g_object_set_data (G_OBJECT (tbl), "family-entry-combo", font_sel);
7105 // expand the field a bit so as to view more of the previews in the drop-down
7106 GtkRequisition req;
7107 gtk_widget_size_request (GTK_WIDGET (font_sel->gobj()), &req);
7108 gtk_widget_set_size_request (GTK_WIDGET (font_sel->gobj()), MIN(req.width + 50, 500), -1);
7110 GtkWidget* entry = (GtkWidget*) font_sel->get_entry()->gobj();
7111 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
7113 g_signal_connect (G_OBJECT (font_sel->gobj()), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
7114 g_signal_connect (G_OBJECT (font_sel->gobj()), "notify::popup-shown",
7115 G_CALLBACK (sp_text_toolbox_family_popnotify), tbl);
7116 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
7117 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
7118 g_signal_connect (G_OBJECT (entry), "focus-out-event", G_CALLBACK (sp_text_toolbox_entry_focus_out), tbl);
7120 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
7121 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
7123 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
7124 gtk_cell_layout_clear( GTK_CELL_LAYOUT(font_sel->gobj()) );
7125 gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(font_sel->gobj()) , cell , TRUE );
7126 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT(font_sel->gobj()), cell, GtkCellLayoutDataFunc (cell_data_func), NULL, NULL);
7128 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
7129 GtkWidget *box = gtk_event_box_new ();
7130 gtk_container_add (GTK_CONTAINER (box), image);
7131 gtk_toolbar_append_widget( tbl, box, "", "");
7132 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
7133 gtk_tooltips_set_tip (tt, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
7134 gtk_widget_hide (GTK_WIDGET (box));
7135 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
7137 ////////////Size
7138 gchar const *const sizes[] = {
7139 "4", "6", "8", "9", "10", "11", "12", "13", "14",
7140 "16", "18", "20", "22", "24", "28",
7141 "32", "36", "40", "48", "56", "64", "72", "144"
7142 };
7144 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
7145 for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
7146 gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
7147 }
7148 gtk_widget_set_size_request (cbox, 80, -1);
7149 gtk_toolbar_append_widget( tbl, cbox, "", "");
7150 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
7151 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
7152 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
7153 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
7155 ////////////Text anchor
7156 GtkWidget *group = gtk_radio_button_new (NULL);
7157 GtkWidget *row = gtk_hbox_new (FALSE, 4);
7158 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
7160 // left
7161 GtkWidget *rbutton = group;
7162 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7163 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
7164 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7166 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7167 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
7168 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
7169 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
7171 // center
7172 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7173 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7174 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
7175 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7177 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7178 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
7179 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
7180 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
7182 // right
7183 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7184 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7185 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
7186 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7188 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7189 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
7190 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
7191 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
7193 // fill
7194 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7195 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7196 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
7197 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7199 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7200 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
7201 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
7202 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
7204 gtk_toolbar_append_widget( tbl, row, "", "");
7206 //spacer
7207 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
7209 ////////////Text style
7210 row = gtk_hbox_new (FALSE, 4);
7212 // bold
7213 rbutton = gtk_toggle_button_new ();
7214 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7215 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
7216 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7217 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
7219 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7220 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
7221 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
7223 // italic
7224 rbutton = gtk_toggle_button_new ();
7225 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7226 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
7227 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7228 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
7230 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7231 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
7232 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
7234 gtk_toolbar_append_widget( tbl, row, "", "");
7236 //spacer
7237 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
7239 // Text orientation
7240 group = gtk_radio_button_new (NULL);
7241 row = gtk_hbox_new (FALSE, 4);
7242 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
7244 // horizontal
7245 rbutton = group;
7246 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7247 gtk_container_add (GTK_CONTAINER (rbutton),
7248 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_HORIZONTAL));
7249 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7250 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
7252 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7253 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
7254 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
7256 // vertical
7257 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7258 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7259 gtk_container_add (GTK_CONTAINER (rbutton),
7260 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_VERTICAL));
7261 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7262 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
7264 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7265 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
7266 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
7267 gtk_toolbar_append_widget( tbl, row, "", "" );
7270 //watch selection
7271 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
7273 sigc::connection *c_selection_changed =
7274 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
7275 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
7276 pool->add_connection ("selection-changed", c_selection_changed);
7278 sigc::connection *c_selection_modified =
7279 new sigc::connection (sp_desktop_selection (desktop)->connectModified
7280 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
7281 pool->add_connection ("selection-modified", c_selection_modified);
7283 sigc::connection *c_subselection_changed =
7284 new sigc::connection (desktop->connectToolSubselectionChanged
7285 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
7286 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
7288 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
7291 gtk_widget_show_all( GTK_WIDGET(tbl) );
7293 return GTK_WIDGET(tbl);
7294 } // end of sp_text_toolbox_new()
7296 }//<unnamed> namespace
7299 //#########################
7300 //## Connector ##
7301 //#########################
7303 static void sp_connector_mode_toggled( GtkToggleAction* act, GtkObject * /*tbl*/ )
7304 {
7305 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7306 prefs->setBool("/tools/connector/mode",
7307 gtk_toggle_action_get_active( act ));
7308 }
7310 static void sp_connector_path_set_avoid(void)
7311 {
7312 cc_selection_set_avoid(true);
7313 }
7316 static void sp_connector_path_set_ignore(void)
7317 {
7318 cc_selection_set_avoid(false);
7319 }
7321 static void sp_connector_orthogonal_toggled( GtkToggleAction* act, GObject *tbl )
7322 {
7323 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7324 Inkscape::Selection * selection = sp_desktop_selection(desktop);
7325 SPDocument *doc = sp_desktop_document(desktop);
7327 if (!sp_document_get_undo_sensitive(doc)) {
7328 return;
7329 }
7332 // quit if run by the _changed callbacks
7333 if (g_object_get_data( tbl, "freeze" )) {
7334 return;
7335 }
7337 // in turn, prevent callbacks from responding
7338 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7340 bool is_orthog = gtk_toggle_action_get_active( act );
7341 gchar orthog_str[] = "orthogonal";
7342 gchar polyline_str[] = "polyline";
7343 gchar *value = is_orthog ? orthog_str : polyline_str ;
7345 bool modmade = false;
7346 GSList *l = (GSList *) selection->itemList();
7347 while (l) {
7348 SPItem *item = (SPItem *) l->data;
7350 if (cc_item_is_connector(item)) {
7351 sp_object_setAttribute(item, "inkscape:connector-type",
7352 value, false);
7353 item->avoidRef->handleSettingChange();
7354 modmade = true;
7355 }
7356 l = l->next;
7357 }
7359 if (!modmade) {
7360 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7361 prefs->setBool("/tools/connector/orthogonal", is_orthog);
7362 }
7364 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7365 is_orthog ? _("Set connector type: orthogonal"): _("Set connector type: polyline"));
7367 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7368 }
7370 static void connector_curvature_changed(GtkAdjustment *adj, GObject* tbl)
7371 {
7372 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7373 Inkscape::Selection * selection = sp_desktop_selection(desktop);
7374 SPDocument *doc = sp_desktop_document(desktop);
7376 if (!sp_document_get_undo_sensitive(doc)) {
7377 return;
7378 }
7381 // quit if run by the _changed callbacks
7382 if (g_object_get_data( tbl, "freeze" )) {
7383 return;
7384 }
7386 // in turn, prevent callbacks from responding
7387 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7389 gdouble newValue = gtk_adjustment_get_value(adj);
7390 gchar value[G_ASCII_DTOSTR_BUF_SIZE];
7391 g_ascii_dtostr(value, G_ASCII_DTOSTR_BUF_SIZE, newValue);
7393 bool modmade = false;
7394 GSList *l = (GSList *) selection->itemList();
7395 while (l) {
7396 SPItem *item = (SPItem *) l->data;
7398 if (cc_item_is_connector(item)) {
7399 sp_object_setAttribute(item, "inkscape:connector-curvature",
7400 value, false);
7401 item->avoidRef->handleSettingChange();
7402 modmade = true;
7403 }
7404 l = l->next;
7405 }
7407 if (!modmade) {
7408 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7409 prefs->setDouble(Glib::ustring("/tools/connector/curvature"), newValue);
7410 }
7412 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7413 _("Change connector curvature"));
7415 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7416 }
7419 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
7420 {
7421 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7422 SPDocument *doc = sp_desktop_document(desktop);
7424 if (!sp_document_get_undo_sensitive(doc)) {
7425 return;
7426 }
7428 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7430 if ( !repr->attribute("inkscape:connector-spacing") &&
7431 ( adj->value == defaultConnSpacing )) {
7432 // Don't need to update the repr if the attribute doesn't
7433 // exist and it is being set to the default value -- as will
7434 // happen at startup.
7435 return;
7436 }
7438 // quit if run by the attr_changed listener
7439 if (g_object_get_data( tbl, "freeze" )) {
7440 return;
7441 }
7443 // in turn, prevent listener from responding
7444 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
7446 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
7447 SP_OBJECT(desktop->namedview)->updateRepr();
7449 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
7450 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
7451 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
7452 Geom::Matrix m = Geom::identity();
7453 avoid_item_move(&m, item);
7454 }
7456 if (items) {
7457 g_slist_free(items);
7458 }
7460 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7461 _("Change connector spacing"));
7463 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7464 }
7466 static void sp_connector_graph_layout(void)
7467 {
7468 if (!SP_ACTIVE_DESKTOP) {
7469 return;
7470 }
7471 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7473 // hack for clones, see comment in align-and-distribute.cpp
7474 int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7475 prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7477 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
7479 prefs->setInt("/options/clonecompensation/value", saved_compensation);
7481 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
7482 }
7484 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject * /*tbl*/ )
7485 {
7486 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7487 prefs->setBool("/tools/connector/directedlayout",
7488 gtk_toggle_action_get_active( act ));
7489 }
7491 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject * /*tbl*/ )
7492 {
7493 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7494 prefs->setBool("/tools/connector/avoidoverlaplayout",
7495 gtk_toggle_action_get_active( act ));
7496 }
7499 static void connector_length_changed(GtkAdjustment *adj, GObject* /*tbl*/)
7500 {
7501 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7502 prefs->setDouble("/tools/connector/length", adj->value);
7503 }
7505 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
7506 gchar const *name, gchar const * /*old_value*/, gchar const * /*new_value*/,
7507 bool /*is_interactive*/, gpointer data)
7508 {
7509 GtkWidget *tbl = GTK_WIDGET(data);
7511 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
7512 return;
7513 }
7514 if (strcmp(name, "inkscape:connector-spacing") == 0)
7515 {
7516 GtkAdjustment *adj = (GtkAdjustment*)
7517 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
7518 gdouble spacing = defaultConnSpacing;
7519 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
7521 gtk_adjustment_set_value(adj, spacing);
7522 gtk_adjustment_value_changed(adj);
7523 }
7525 spinbutton_defocus(GTK_OBJECT(tbl));
7526 }
7528 static void sp_connector_new_connection_point(GtkWidget *, GObject *tbl)
7529 {
7530 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7531 SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7533 if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE) {
7534 cc_create_connection_point(cc);
7535 }
7536 }
7538 static void sp_connector_remove_connection_point(GtkWidget *, GObject *tbl)
7539 {
7540 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7541 SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7543 if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE) {
7544 cc_remove_connection_point(cc);
7545 }
7546 }
7548 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
7549 NULL, /* child_added */
7550 NULL, /* child_removed */
7551 connector_tb_event_attr_changed,
7552 NULL, /* content_changed */
7553 NULL /* order_changed */
7554 };
7556 static void sp_connector_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
7557 {
7558 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "curvature" ) );
7559 GtkToggleAction *act = GTK_TOGGLE_ACTION( g_object_get_data( tbl, "orthogonal" ) );
7560 SPItem *item = selection->singleItem();
7561 if (SP_IS_PATH(item))
7562 {
7563 gdouble curvature = SP_PATH(item)->connEndPair.getCurvature();
7564 bool is_orthog = SP_PATH(item)->connEndPair.isOrthogonal();
7565 gtk_toggle_action_set_active(act, is_orthog);
7566 gtk_adjustment_set_value(adj, curvature);
7567 }
7569 }
7571 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
7572 {
7573 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7574 Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
7576 // Editing mode toggle button
7577 {
7578 InkToggleAction* act = ink_toggle_action_new( "ConnectorEditModeAction",
7579 _("EditMode"),
7580 _("Switch between connection point editing and connector drawing mode"),
7581 INKSCAPE_ICON_CONNECTOR_EDIT,
7582 Inkscape::ICON_SIZE_DECORATION );
7583 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7585 bool tbuttonstate = prefs->getBool("/tools/connector/mode");
7586 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7587 g_object_set_data( holder, "mode", act );
7588 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_mode_toggled), holder );
7589 }
7592 {
7593 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
7594 _("Avoid"),
7595 _("Make connectors avoid selected objects"),
7596 INKSCAPE_ICON_CONNECTOR_AVOID,
7597 secondarySize );
7598 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
7599 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7600 }
7602 {
7603 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
7604 _("Ignore"),
7605 _("Make connectors ignore selected objects"),
7606 INKSCAPE_ICON_CONNECTOR_IGNORE,
7607 secondarySize );
7608 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
7609 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7610 }
7612 // Orthogonal connectors toggle button
7613 {
7614 InkToggleAction* act = ink_toggle_action_new( "ConnectorOrthogonalAction",
7615 _("Orthogonal"),
7616 _("Make connector orthogonal or polyline"),
7617 INKSCAPE_ICON_CONNECTOR_ORTHOGONAL,
7618 Inkscape::ICON_SIZE_DECORATION );
7619 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7621 bool tbuttonstate = prefs->getBool("/tools/connector/orthogonal");
7622 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7623 g_object_set_data( holder, "orthogonal", act );
7624 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_orthogonal_toggled), holder );
7625 }
7627 EgeAdjustmentAction* eact = 0;
7628 // Curvature spinbox
7629 eact = create_adjustment_action( "ConnectorCurvatureAction",
7630 _("Connector Curvature"), _("Curvature:"),
7631 _("The amount of connectors curvature"),
7632 "/tools/connector/curvature", defaultConnCurvature,
7633 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-curvature",
7634 0, 100, 1.0, 10.0,
7635 0, 0, 0,
7636 connector_curvature_changed, 1, 0 );
7637 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7639 // Spacing spinbox
7640 eact = create_adjustment_action( "ConnectorSpacingAction",
7641 _("Connector Spacing"), _("Spacing:"),
7642 _("The amount of space left around objects by auto-routing connectors"),
7643 "/tools/connector/spacing", defaultConnSpacing,
7644 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
7645 0, 100, 1.0, 10.0,
7646 0, 0, 0,
7647 connector_spacing_changed, 1, 0 );
7648 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7650 // Graph (connector network) layout
7651 {
7652 InkAction* inky = ink_action_new( "ConnectorGraphAction",
7653 _("Graph"),
7654 _("Nicely arrange selected connector network"),
7655 INKSCAPE_ICON_DISTRIBUTE_GRAPH,
7656 secondarySize );
7657 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
7658 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7659 }
7661 // Default connector length spinbox
7662 eact = create_adjustment_action( "ConnectorLengthAction",
7663 _("Connector Length"), _("Length:"),
7664 _("Ideal length for connectors when layout is applied"),
7665 "/tools/connector/length", 100,
7666 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
7667 10, 1000, 10.0, 100.0,
7668 0, 0, 0,
7669 connector_length_changed, 1, 0 );
7670 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7673 // Directed edges toggle button
7674 {
7675 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
7676 _("Downwards"),
7677 _("Make connectors with end-markers (arrows) point downwards"),
7678 INKSCAPE_ICON_DISTRIBUTE_GRAPH_DIRECTED,
7679 Inkscape::ICON_SIZE_DECORATION );
7680 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7682 bool tbuttonstate = prefs->getBool("/tools/connector/directedlayout");
7683 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7685 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
7686 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_connector_toolbox_selection_changed), (GObject *)holder));
7687 }
7689 // Avoid overlaps toggle button
7690 {
7691 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
7692 _("Remove overlaps"),
7693 _("Do not allow overlapping shapes"),
7694 INKSCAPE_ICON_DISTRIBUTE_REMOVE_OVERLAPS,
7695 Inkscape::ICON_SIZE_DECORATION );
7696 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7698 bool tbuttonstate = prefs->getBool("/tools/connector/avoidoverlaplayout");
7699 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), (tbuttonstate ? TRUE : FALSE ));
7701 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
7702 }
7705 // New connection point button
7706 {
7707 InkAction* inky = ink_action_new( "ConnectorNewConnPointAction",
7708 _("New connection point"),
7709 _("Add a new connection point to the currently selected item"),
7710 INKSCAPE_ICON_CONNECTOR_NEW_CONNPOINT,
7711 secondarySize );
7712 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_new_connection_point), holder );
7713 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7714 }
7716 // Remove selected connection point button
7718 {
7719 InkAction* inky = ink_action_new( "ConnectorRemoveConnPointAction",
7720 _("Remove connection point"),
7721 _("Remove the currently selected connection point"),
7722 INKSCAPE_ICON_CONNECTOR_REMOVE_CONNPOINT,
7723 secondarySize );
7724 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_remove_connection_point), holder );
7725 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7726 }
7729 // Code to watch for changes to the connector-spacing attribute in
7730 // the XML.
7731 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7732 g_assert(repr != NULL);
7734 purge_repr_listener( holder, holder );
7736 if (repr) {
7737 g_object_set_data( holder, "repr", repr );
7738 Inkscape::GC::anchor(repr);
7739 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
7740 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
7741 }
7742 } // end of sp_connector_toolbox_prep()
7745 //#########################
7746 //## Paintbucket ##
7747 //#########################
7749 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
7750 {
7751 gint channels = ege_select_one_action_get_active( act );
7752 flood_channels_set_channels( channels );
7753 }
7755 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject * /*tbl*/)
7756 {
7757 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7758 prefs->setInt("/tools/paintbucket/threshold", (gint)adj->value);
7759 }
7761 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject * /*tbl*/)
7762 {
7763 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7764 prefs->setBool("/tools/paintbucket/autogap", ege_select_one_action_get_active( act ));
7765 }
7767 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
7768 {
7769 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
7770 SPUnit const *unit = tracker->getActiveUnit();
7771 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7773 prefs->setDouble("/tools/paintbucket/offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
7774 prefs->setString("/tools/paintbucket/offsetunits", sp_unit_get_abbreviation(unit));
7775 }
7777 static void paintbucket_defaults(GtkWidget *, GObject *tbl)
7778 {
7779 // FIXME: make defaults settable via Inkscape Options
7780 struct KeyValue {
7781 char const *key;
7782 double value;
7783 } const key_values[] = {
7784 {"threshold", 15},
7785 {"offset", 0.0}
7786 };
7788 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
7789 KeyValue const &kv = key_values[i];
7790 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
7791 if ( adj ) {
7792 gtk_adjustment_set_value(adj, kv.value);
7793 }
7794 }
7796 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
7797 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
7798 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
7799 ege_select_one_action_set_active( autogap_action, 0 );
7800 }
7802 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
7803 {
7804 EgeAdjustmentAction* eact = 0;
7805 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7807 {
7808 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7810 GList* items = 0;
7811 gint count = 0;
7812 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
7813 {
7814 GtkTreeIter iter;
7815 gtk_list_store_append( model, &iter );
7816 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7817 count++;
7818 }
7819 g_list_free( items );
7820 items = 0;
7821 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
7822 g_object_set( act1, "short_label", _("Fill by:"), NULL );
7823 ege_select_one_action_set_appearance( act1, "compact" );
7824 ege_select_one_action_set_active( act1, prefs->getInt("/tools/paintbucket/channels", 0) );
7825 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
7826 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
7827 g_object_set_data( holder, "channels_action", act1 );
7828 }
7830 // Spacing spinbox
7831 {
7832 eact = create_adjustment_action(
7833 "ThresholdAction",
7834 _("Fill Threshold"), _("Threshold:"),
7835 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
7836 "/tools/paintbucket/threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
7837 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
7838 0, 0, 0,
7839 paintbucket_threshold_changed, 1, 0 );
7841 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
7842 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7843 }
7845 // Create the units menu.
7846 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
7847 Glib::ustring stored_unit = prefs->getString("/tools/paintbucket/offsetunits");
7848 if (!stored_unit.empty()) {
7849 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit.data()));
7850 }
7851 g_object_set_data( holder, "tracker", tracker );
7852 {
7853 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
7854 gtk_action_group_add_action( mainActions, act );
7855 }
7857 // Offset spinbox
7858 {
7859 eact = create_adjustment_action(
7860 "OffsetAction",
7861 _("Grow/shrink by"), _("Grow/shrink by:"),
7862 _("The amount to grow (positive) or shrink (negative) the created fill path"),
7863 "/tools/paintbucket/offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
7864 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
7865 0, 0, 0,
7866 paintbucket_offset_changed, 1, 2);
7867 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
7869 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7870 }
7872 /* Auto Gap */
7873 {
7874 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7876 GList* items = 0;
7877 gint count = 0;
7878 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
7879 {
7880 GtkTreeIter iter;
7881 gtk_list_store_append( model, &iter );
7882 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7883 count++;
7884 }
7885 g_list_free( items );
7886 items = 0;
7887 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
7888 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
7889 ege_select_one_action_set_appearance( act2, "compact" );
7890 ege_select_one_action_set_active( act2, prefs->getBool("/tools/paintbucket/autogap") );
7891 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
7892 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
7893 g_object_set_data( holder, "autogap_action", act2 );
7894 }
7896 /* Reset */
7897 {
7898 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
7899 _("Defaults"),
7900 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
7901 GTK_STOCK_CLEAR );
7902 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
7903 gtk_action_group_add_action( mainActions, act );
7904 gtk_action_set_sensitive( act, TRUE );
7905 }
7907 }
7909 /*
7910 Local Variables:
7911 mode:c++
7912 c-file-style:"stroustrup"
7913 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
7914 indent-tabs-mode:nil
7915 fill-column:99
7916 End:
7917 */
7918 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :