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