49cbbe0104366ab731e62b2ad3793504cc1fba71
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);
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 *toolBar = gtk_toolbar_new();
864 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
865 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
867 return toolboxNewCommon( toolBar, GTK_POS_TOP );
868 }
870 GtkWidget *sp_aux_toolbox_new()
871 {
872 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
874 g_object_set_data(G_OBJECT(tb), "MarkForChild", const_cast<gchar *>("MarkForChild"));
876 return toolboxNewCommon( tb, GTK_POS_LEFT );
877 }
879 //####################################
880 //# Commands Bar
881 //####################################
883 GtkWidget *sp_commands_toolbox_new()
884 {
885 GtkWidget *tb = gtk_toolbar_new();
887 return toolboxNewCommon( tb, GTK_POS_LEFT );
888 }
890 GtkWidget *sp_snap_toolbox_new()
891 {
892 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
894 return toolboxNewCommon( tb, GTK_POS_LEFT );
895 }
897 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
898 gchar const *label, gchar const *shortLabel, gchar const *tooltip,
899 Glib::ustring const &path, gdouble def,
900 GtkWidget *focusTarget,
901 GtkWidget *us,
902 GObject *dataKludge,
903 gboolean altx, gchar const *altx_mark,
904 gdouble lower, gdouble upper, gdouble step, gdouble page,
905 gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
906 void (*callback)(GtkAdjustment *, GObject *),
907 gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
908 {
909 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
910 GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs->getDouble(path, def) * factor,
911 lower, upper, step, page, 0 ) );
912 if (us) {
913 sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
914 }
916 gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
918 EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
919 if ( shortLabel ) {
920 g_object_set( act, "short_label", shortLabel, NULL );
921 }
923 if ( (descrCount > 0) && descrLabels && descrValues ) {
924 ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
925 }
927 if ( focusTarget ) {
928 ege_adjustment_action_set_focuswidget( act, focusTarget );
929 }
931 if ( altx && altx_mark ) {
932 g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
933 }
935 if ( dataKludge ) {
936 // Rather lame, but it's the only place where we need to get the entry name
937 // but we don't have an Entry
938 g_object_set_data( dataKludge, prefs->getEntry(path).getEntryName().data(), adj );
939 }
941 // Using a cast just to make sure we pass in the right kind of function pointer
942 g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
944 return act;
945 }
948 //####################################
949 //# node editing callbacks
950 //####################################
952 /**
953 * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
954 */
955 static ShapeEditor *get_current_shape_editor()
956 {
957 if (!SP_ACTIVE_DESKTOP) {
958 return NULL;
959 }
961 SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
963 if (!SP_IS_NODE_CONTEXT(event_context)) {
964 return NULL;
965 }
967 return event_context->shape_editor;
968 }
971 void
972 sp_node_path_edit_add(void)
973 {
974 ShapeEditor *shape_editor = get_current_shape_editor();
975 if (shape_editor) shape_editor->add_node();
976 }
978 void
979 sp_node_path_edit_delete(void)
980 {
981 ShapeEditor *shape_editor = get_current_shape_editor();
982 if (shape_editor) shape_editor->delete_nodes_preserving_shape();
983 }
985 void
986 sp_node_path_edit_delete_segment(void)
987 {
988 ShapeEditor *shape_editor = get_current_shape_editor();
989 if (shape_editor) shape_editor->delete_segment();
990 }
992 void
993 sp_node_path_edit_break(void)
994 {
995 ShapeEditor *shape_editor = get_current_shape_editor();
996 if (shape_editor) shape_editor->break_at_nodes();
997 }
999 void
1000 sp_node_path_edit_join(void)
1001 {
1002 ShapeEditor *shape_editor = get_current_shape_editor();
1003 if (shape_editor) shape_editor->join_nodes();
1004 }
1006 void
1007 sp_node_path_edit_join_segment(void)
1008 {
1009 ShapeEditor *shape_editor = get_current_shape_editor();
1010 if (shape_editor) shape_editor->join_segments();
1011 }
1013 void
1014 sp_node_path_edit_toline(void)
1015 {
1016 ShapeEditor *shape_editor = get_current_shape_editor();
1017 if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1018 }
1020 void
1021 sp_node_path_edit_tocurve(void)
1022 {
1023 ShapeEditor *shape_editor = get_current_shape_editor();
1024 if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1025 }
1027 void
1028 sp_node_path_edit_cusp(void)
1029 {
1030 ShapeEditor *shape_editor = get_current_shape_editor();
1031 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1032 }
1034 void
1035 sp_node_path_edit_smooth(void)
1036 {
1037 ShapeEditor *shape_editor = get_current_shape_editor();
1038 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1039 }
1041 void
1042 sp_node_path_edit_symmetrical(void)
1043 {
1044 ShapeEditor *shape_editor = get_current_shape_editor();
1045 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1046 }
1048 void
1049 sp_node_path_edit_auto(void)
1050 {
1051 ShapeEditor *shape_editor = get_current_shape_editor();
1052 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_AUTO);
1053 }
1055 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1056 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1057 bool show = gtk_toggle_action_get_active( act );
1058 prefs->setBool("/tools/nodes/show_handles", show);
1059 ShapeEditor *shape_editor = get_current_shape_editor();
1060 if (shape_editor) shape_editor->show_handles(show);
1061 }
1063 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1064 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1065 bool show = gtk_toggle_action_get_active( act );
1066 prefs->setBool("/tools/nodes/show_helperpath", show);
1067 ShapeEditor *shape_editor = get_current_shape_editor();
1068 if (shape_editor) shape_editor->show_helperpath(show);
1069 }
1071 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1072 sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1073 }
1075 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1076 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1077 }
1079 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1080 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1081 }
1083 /* is called when the node selection is modified */
1084 static void
1085 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1086 {
1087 GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1088 GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1089 GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1090 GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1092 // quit if run by the attr_changed listener
1093 if (g_object_get_data( tbl, "freeze" )) {
1094 return;
1095 }
1097 // in turn, prevent listener from responding
1098 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1100 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1101 SPUnit const *unit = tracker->getActiveUnit();
1103 ShapeEditor *shape_editor = get_current_shape_editor();
1104 if (shape_editor && shape_editor->has_nodepath()) {
1105 Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1106 int n_selected = 0;
1107 if (nodepath) {
1108 n_selected = nodepath->numSelected();
1109 }
1111 if (n_selected == 0) {
1112 gtk_action_set_sensitive(xact, FALSE);
1113 gtk_action_set_sensitive(yact, FALSE);
1114 } else {
1115 gtk_action_set_sensitive(xact, TRUE);
1116 gtk_action_set_sensitive(yact, TRUE);
1117 Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1118 Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1120 if (n_selected == 1) {
1121 Geom::Point sel_node = nodepath->singleSelectedCoords();
1122 if (oldx != sel_node[Geom::X] || oldy != sel_node[Geom::Y]) {
1123 gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[Geom::X], *unit));
1124 gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[Geom::Y], *unit));
1125 }
1126 } else {
1127 boost::optional<Geom::Coord> x = sp_node_selected_common_coord(nodepath, Geom::X);
1128 boost::optional<Geom::Coord> y = sp_node_selected_common_coord(nodepath, Geom::Y);
1129 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1130 /* Note: Currently x and y will always have a value, even if the coordinates of the
1131 selected nodes don't coincide (in this case we use the coordinates of the center
1132 of the bounding box). So the entries are never set to zero. */
1133 // FIXME: Maybe we should clear the entry if several nodes are selected
1134 // instead of providing a kind of average value
1135 gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1136 gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1137 }
1138 }
1139 }
1140 } else {
1141 // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1142 gtk_action_set_sensitive(xact, FALSE);
1143 gtk_action_set_sensitive(yact, FALSE);
1144 }
1146 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1147 }
1149 static void
1150 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1151 {
1152 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1153 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1155 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1156 SPUnit const *unit = tracker->getActiveUnit();
1158 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1159 prefs->setDouble(Glib::ustring("/tools/nodes/") + value_name, sp_units_get_pixels(adj->value, *unit));
1160 }
1162 // quit if run by the attr_changed listener
1163 if (g_object_get_data( tbl, "freeze" )) {
1164 return;
1165 }
1167 // in turn, prevent listener from responding
1168 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1170 ShapeEditor *shape_editor = get_current_shape_editor();
1171 if (shape_editor && shape_editor->has_nodepath()) {
1172 double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1173 if (!strcmp(value_name, "x")) {
1174 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::X);
1175 }
1176 if (!strcmp(value_name, "y")) {
1177 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::Y);
1178 }
1179 }
1181 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1182 }
1184 static void
1185 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1186 {
1187 sp_node_path_value_changed(adj, tbl, "x");
1188 }
1190 static void
1191 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1192 {
1193 sp_node_path_value_changed(adj, tbl, "y");
1194 }
1196 void
1197 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1198 {
1199 {
1200 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1201 SPItem *item = selection->singleItem();
1202 if (item && SP_IS_LPE_ITEM(item)) {
1203 if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1204 gtk_action_set_sensitive(w, TRUE);
1205 } else {
1206 gtk_action_set_sensitive(w, FALSE);
1207 }
1208 } else {
1209 gtk_action_set_sensitive(w, FALSE);
1210 }
1211 }
1213 {
1214 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1215 SPItem *item = selection->singleItem();
1216 if (item && item->clip_ref && item->clip_ref->getObject()) {
1217 gtk_action_set_sensitive(w, TRUE);
1218 } else {
1219 gtk_action_set_sensitive(w, FALSE);
1220 }
1221 }
1223 {
1224 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1225 SPItem *item = selection->singleItem();
1226 if (item && item->mask_ref && item->mask_ref->getObject()) {
1227 gtk_action_set_sensitive(w, TRUE);
1228 } else {
1229 gtk_action_set_sensitive(w, FALSE);
1230 }
1231 }
1232 }
1234 void
1235 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1236 {
1237 sp_node_toolbox_sel_changed (selection, tbl);
1238 }
1242 //################################
1243 //## Node Editing Toolbox ##
1244 //################################
1246 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1247 {
1248 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1249 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1250 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1251 g_object_set_data( holder, "tracker", tracker );
1253 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
1255 {
1256 InkAction* inky = ink_action_new( "NodeInsertAction",
1257 _("Insert node"),
1258 _("Insert new nodes into selected segments"),
1259 INKSCAPE_ICON_NODE_ADD,
1260 secondarySize );
1261 g_object_set( inky, "short_label", _("Insert"), NULL );
1262 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1263 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1264 }
1266 {
1267 InkAction* inky = ink_action_new( "NodeDeleteAction",
1268 _("Delete node"),
1269 _("Delete selected nodes"),
1270 INKSCAPE_ICON_NODE_DELETE,
1271 secondarySize );
1272 g_object_set( inky, "short_label", _("Delete"), NULL );
1273 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1274 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1275 }
1277 {
1278 InkAction* inky = ink_action_new( "NodeJoinAction",
1279 _("Join endnodes"),
1280 _("Join selected endnodes"),
1281 INKSCAPE_ICON_NODE_JOIN,
1282 secondarySize );
1283 g_object_set( inky, "short_label", _("Join"), NULL );
1284 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1285 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1286 }
1288 {
1289 InkAction* inky = ink_action_new( "NodeBreakAction",
1290 _("Break nodes"),
1291 _("Break path at selected nodes"),
1292 INKSCAPE_ICON_NODE_BREAK,
1293 secondarySize );
1294 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1295 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1296 }
1299 {
1300 InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1301 _("Join with segment"),
1302 _("Join selected endnodes with a new segment"),
1303 INKSCAPE_ICON_NODE_JOIN_SEGMENT,
1304 secondarySize );
1305 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1306 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1307 }
1309 {
1310 InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1311 _("Delete segment"),
1312 _("Delete segment between two non-endpoint nodes"),
1313 INKSCAPE_ICON_NODE_DELETE_SEGMENT,
1314 secondarySize );
1315 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1316 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1317 }
1319 {
1320 InkAction* inky = ink_action_new( "NodeCuspAction",
1321 _("Node Cusp"),
1322 _("Make selected nodes corner"),
1323 INKSCAPE_ICON_NODE_TYPE_CUSP,
1324 secondarySize );
1325 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1326 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1327 }
1329 {
1330 InkAction* inky = ink_action_new( "NodeSmoothAction",
1331 _("Node Smooth"),
1332 _("Make selected nodes smooth"),
1333 INKSCAPE_ICON_NODE_TYPE_SMOOTH,
1334 secondarySize );
1335 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1336 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1337 }
1339 {
1340 InkAction* inky = ink_action_new( "NodeSymmetricAction",
1341 _("Node Symmetric"),
1342 _("Make selected nodes symmetric"),
1343 INKSCAPE_ICON_NODE_TYPE_SYMMETRIC,
1344 secondarySize );
1345 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1346 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1347 }
1349 {
1350 InkAction* inky = ink_action_new( "NodeAutoAction",
1351 _("Node Auto"),
1352 _("Make selected nodes auto-smooth"),
1353 INKSCAPE_ICON_NODE_TYPE_AUTO_SMOOTH,
1354 secondarySize );
1355 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_auto), 0 );
1356 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1357 }
1359 {
1360 InkAction* inky = ink_action_new( "NodeLineAction",
1361 _("Node Line"),
1362 _("Make selected segments lines"),
1363 INKSCAPE_ICON_NODE_SEGMENT_LINE,
1364 secondarySize );
1365 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1366 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1367 }
1369 {
1370 InkAction* inky = ink_action_new( "NodeCurveAction",
1371 _("Node Curve"),
1372 _("Make selected segments curves"),
1373 INKSCAPE_ICON_NODE_SEGMENT_CURVE,
1374 secondarySize );
1375 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1376 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1377 }
1379 {
1380 InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1381 _("Show Handles"),
1382 _("Show the Bezier handles of selected nodes"),
1383 INKSCAPE_ICON_SHOW_NODE_HANDLES,
1384 Inkscape::ICON_SIZE_DECORATION );
1385 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1386 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1387 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_handles", true) );
1388 }
1390 {
1391 InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1392 _("Show Outline"),
1393 _("Show the outline of the path"),
1394 INKSCAPE_ICON_SHOW_PATH_OUTLINE,
1395 Inkscape::ICON_SIZE_DECORATION );
1396 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1397 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1398 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_helperpath", false) );
1399 }
1401 {
1402 InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1403 _("Next path effect parameter"),
1404 _("Show next path effect parameter for editing"),
1405 INKSCAPE_ICON_PATH_EFFECT_PARAMETER_NEXT,
1406 Inkscape::ICON_SIZE_DECORATION );
1407 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1408 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1409 g_object_set_data( holder, "nodes_lpeedit", inky);
1410 }
1412 {
1413 InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1414 _("Edit clipping path"),
1415 _("Edit the clipping path of the object"),
1416 INKSCAPE_ICON_PATH_CLIP_EDIT,
1417 Inkscape::ICON_SIZE_DECORATION );
1418 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1419 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1420 g_object_set_data( holder, "nodes_clippathedit", inky);
1421 }
1423 {
1424 InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1425 _("Edit mask path"),
1426 _("Edit the mask of the object"),
1427 INKSCAPE_ICON_PATH_MASK_EDIT,
1428 Inkscape::ICON_SIZE_DECORATION );
1429 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1430 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1431 g_object_set_data( holder, "nodes_maskedit", inky);
1432 }
1434 /* X coord of selected node(s) */
1435 {
1436 EgeAdjustmentAction* eact = 0;
1437 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1438 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1439 eact = create_adjustment_action( "NodeXAction",
1440 _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1441 "/tools/nodes/Xcoord", 0,
1442 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1443 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1444 labels, values, G_N_ELEMENTS(labels),
1445 sp_node_path_x_value_changed );
1446 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1447 g_object_set_data( holder, "nodes_x_action", eact );
1448 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1449 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1450 }
1452 /* Y coord of selected node(s) */
1453 {
1454 EgeAdjustmentAction* eact = 0;
1455 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1456 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1457 eact = create_adjustment_action( "NodeYAction",
1458 _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1459 "/tools/nodes/Ycoord", 0,
1460 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1461 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1462 labels, values, G_N_ELEMENTS(labels),
1463 sp_node_path_y_value_changed );
1464 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1465 g_object_set_data( holder, "nodes_y_action", eact );
1466 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1467 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1468 }
1470 // add the units menu
1471 {
1472 GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1473 gtk_action_group_add_action( mainActions, act );
1474 }
1477 sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1479 //watch selection
1480 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1482 sigc::connection *c_selection_changed =
1483 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1484 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1485 pool->add_connection ("selection-changed", c_selection_changed);
1487 sigc::connection *c_selection_modified =
1488 new sigc::connection (sp_desktop_selection (desktop)->connectModified
1489 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1490 pool->add_connection ("selection-modified", c_selection_modified);
1492 sigc::connection *c_subselection_changed =
1493 new sigc::connection (desktop->connectToolSubselectionChanged
1494 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1495 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1497 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1499 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1500 } // end of sp_node_toolbox_prep()
1503 //########################
1504 //## Zoom Toolbox ##
1505 //########################
1507 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1508 {
1509 // no custom GtkAction setup needed
1510 } // end of sp_zoom_toolbox_prep()
1512 void
1513 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1514 {
1515 toolbox_set_desktop(toolbox,
1516 desktop,
1517 setup_tool_toolbox,
1518 update_tool_toolbox);
1519 }
1522 void
1523 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1524 {
1525 toolbox_set_desktop(toolbox,
1526 desktop,
1527 setup_aux_toolbox,
1528 update_aux_toolbox);
1529 }
1531 void
1532 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1533 {
1534 toolbox_set_desktop(toolbox,
1535 desktop,
1536 setup_commands_toolbox,
1537 update_commands_toolbox);
1538 }
1540 void
1541 sp_snap_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1542 {
1543 toolbox_set_desktop(toolbox,
1544 desktop,
1545 setup_snap_toolbox,
1546 update_snap_toolbox);
1547 }
1550 static void
1551 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func)
1552 {
1553 sigc::connection *conn = static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1554 "event_context_connection"));
1555 {
1556 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1557 if (g_object_get_data(G_OBJECT(child), "MarkForChild")) {
1558 toolbox = child;
1559 }
1560 }
1561 gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1562 SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1564 if (old_desktop) {
1565 GList *children, *iter;
1567 children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1568 for ( iter = children ; iter ; iter = iter->next ) {
1569 gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1570 }
1571 g_list_free(children);
1572 }
1574 g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1576 if (desktop) {
1577 gtk_widget_set_sensitive(toolbox, TRUE);
1578 setup_func(toolbox, desktop);
1579 update_func(desktop, desktop->event_context, toolbox);
1580 *conn = desktop->connectEventContextChanged
1581 (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1582 } else {
1583 gtk_widget_set_sensitive(toolbox, FALSE);
1584 }
1586 } // end of toolbox_set_desktop()
1589 static void setupToolboxCommon( GtkWidget *toolbox,
1590 SPDesktop *desktop,
1591 gchar const *descr,
1592 gchar const* toolbarName,
1593 gchar const* sizePref,
1594 GtkOrientation orientation )
1595 {
1596 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1597 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1599 GtkUIManager* mgr = gtk_ui_manager_new();
1600 GError* errVal = 0;
1602 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1603 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1605 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, toolbarName );
1606 if ( prefs->getBool("/toolbox/icononly", true) ) {
1607 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1608 }
1610 Inkscape::IconSize toolboxSize = prefToSize(sizePref);
1611 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1613 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), orientation);
1614 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1616 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1618 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1619 if ( child ) {
1620 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1621 }
1623 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1624 }
1626 static void
1627 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1628 {
1629 gchar const * descr =
1630 "<ui>"
1631 " <toolbar name='ToolToolbar'>"
1632 " <toolitem action='ToolSelector' />"
1633 " <toolitem action='ToolNode' />"
1634 " <toolitem action='ToolTweak' />"
1635 " <toolitem action='ToolSpray' />"
1636 " <toolitem action='ToolZoom' />"
1637 " <toolitem action='ToolRect' />"
1638 " <toolitem action='Tool3DBox' />"
1639 " <toolitem action='ToolArc' />"
1640 " <toolitem action='ToolStar' />"
1641 " <toolitem action='ToolSpiral' />"
1642 " <toolitem action='ToolPencil' />"
1643 " <toolitem action='ToolPen' />"
1644 " <toolitem action='ToolCalligraphic' />"
1645 " <toolitem action='ToolEraser' />"
1646 // " <toolitem action='ToolLPETool' />"
1647 " <toolitem action='ToolPaintBucket' />"
1648 " <toolitem action='ToolText' />"
1649 " <toolitem action='ToolConnector' />"
1650 " <toolitem action='ToolGradient' />"
1651 " <toolitem action='ToolDropper' />"
1652 " </toolbar>"
1653 "</ui>";
1655 setupToolboxCommon( toolbox, desktop, descr,
1656 "/ui/ToolToolbar",
1657 "/toolbox/tools/small",
1658 GTK_ORIENTATION_VERTICAL );
1659 }
1661 static void
1662 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1663 {
1664 gchar const *const tname = ( eventcontext
1665 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1666 : NULL );
1667 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1669 for (int i = 0 ; tools[i].type_name ; i++ ) {
1670 Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1671 if ( act ) {
1672 bool setActive = tname && !strcmp(tname, tools[i].type_name);
1673 Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1674 if ( verbAct ) {
1675 verbAct->set_active(setActive);
1676 }
1677 }
1678 }
1679 }
1681 static void
1682 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1683 {
1684 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1685 GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1686 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1687 GtkUIManager* mgr = gtk_ui_manager_new();
1688 GError* errVal = 0;
1689 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1690 gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1692 std::map<std::string, GtkWidget*> dataHolders;
1694 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1695 if ( aux_toolboxes[i].prep_func ) {
1696 // converted to GtkActions and UIManager
1698 GtkWidget* kludge = gtk_toolbar_new();
1699 g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1700 g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1701 dataHolders[aux_toolboxes[i].type_name] = kludge;
1702 aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1703 } else {
1705 GtkWidget *sub_toolbox = 0;
1706 if (aux_toolboxes[i].create_func == NULL) {
1707 sub_toolbox = sp_empty_toolbox_new(desktop);
1708 } else {
1709 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1710 }
1712 gtk_size_group_add_widget( grouper, sub_toolbox );
1714 gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1715 g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1717 }
1718 }
1720 // Second pass to create toolbars *after* all GtkActions are created
1721 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1722 if ( aux_toolboxes[i].prep_func ) {
1723 // converted to GtkActions and UIManager
1725 GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1727 GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1728 gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1730 gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1731 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1732 g_free( tmp );
1733 tmp = 0;
1735 if ( prefs->getBool( "/toolbox/icononly", true) ) {
1736 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1737 }
1739 Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
1740 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1742 gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1744 if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1745 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1746 swatch->setDesktop( desktop );
1747 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1748 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1749 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1750 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 );
1751 }
1753 gtk_widget_show_all( holder );
1754 sp_set_font_size_smaller( holder );
1756 gtk_size_group_add_widget( grouper, holder );
1758 gtk_container_add( GTK_CONTAINER(toolbox), holder );
1759 g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1760 }
1761 }
1763 g_object_unref( G_OBJECT(grouper) );
1764 }
1766 static void
1767 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1768 {
1769 gchar const *tname = ( eventcontext
1770 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1771 : NULL );
1772 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1773 GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1774 if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1775 gtk_widget_show_all(sub_toolbox);
1776 g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1777 } else {
1778 gtk_widget_hide(sub_toolbox);
1779 }
1780 }
1781 }
1783 static void
1784 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1785 {
1786 gchar const * descr =
1787 "<ui>"
1788 " <toolbar name='CommandsToolbar'>"
1789 " <toolitem action='FileNew' />"
1790 " <toolitem action='FileOpen' />"
1791 " <toolitem action='FileSave' />"
1792 " <toolitem action='FilePrint' />"
1793 " <separator />"
1794 " <toolitem action='FileImport' />"
1795 " <toolitem action='FileExport' />"
1796 " <separator />"
1797 " <toolitem action='EditUndo' />"
1798 " <toolitem action='EditRedo' />"
1799 " <separator />"
1800 " <toolitem action='EditCopy' />"
1801 " <toolitem action='EditCut' />"
1802 " <toolitem action='EditPaste' />"
1803 " <separator />"
1804 " <toolitem action='ZoomSelection' />"
1805 " <toolitem action='ZoomDrawing' />"
1806 " <toolitem action='ZoomPage' />"
1807 " <separator />"
1808 " <toolitem action='EditDuplicate' />"
1809 " <toolitem action='EditClone' />"
1810 " <toolitem action='EditUnlinkClone' />"
1811 " <separator />"
1812 " <toolitem action='SelectionGroup' />"
1813 " <toolitem action='SelectionUnGroup' />"
1814 " <separator />"
1815 " <toolitem action='DialogFillStroke' />"
1816 " <toolitem action='DialogText' />"
1817 " <toolitem action='DialogLayers' />"
1818 " <toolitem action='DialogXMLEditor' />"
1819 " <toolitem action='DialogAlignDistribute' />"
1820 " <separator />"
1821 " <toolitem action='DialogPreferences' />"
1822 " <toolitem action='DialogDocumentProperties' />"
1823 " </toolbar>"
1824 "</ui>";
1826 setupToolboxCommon( toolbox, desktop, descr,
1827 "/ui/CommandsToolbar",
1828 "/toolbox/small",
1829 GTK_ORIENTATION_HORIZONTAL );
1830 }
1832 static void
1833 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1834 {
1835 }
1837 void toggle_snap_callback (GtkToggleAction *act, gpointer data) { //data points to the toolbox
1839 if (g_object_get_data(G_OBJECT(data), "freeze" )) {
1840 return;
1841 }
1843 gpointer ptr = g_object_get_data(G_OBJECT(data), "desktop");
1844 g_assert(ptr != NULL);
1846 SPDesktop *dt = reinterpret_cast<SPDesktop*>(ptr);
1847 SPNamedView *nv = sp_desktop_namedview(dt);
1848 SPDocument *doc = SP_OBJECT_DOCUMENT(nv);
1850 if (dt == NULL || nv == NULL) {
1851 g_warning("No desktop or namedview specified (in toggle_snap_callback)!");
1852 return;
1853 }
1855 Inkscape::XML::Node *repr = SP_OBJECT_REPR(nv);
1857 if (repr == NULL) {
1858 g_warning("This namedview doesn't have a xml representation attached!");
1859 return;
1860 }
1862 bool saved = sp_document_get_undo_sensitive(doc);
1863 sp_document_set_undo_sensitive(doc, false);
1865 bool v = false;
1866 SPAttributeEnum attr = (SPAttributeEnum) GPOINTER_TO_INT(g_object_get_data(G_OBJECT(act), "SP_ATTR_INKSCAPE"));
1868 switch (attr) {
1869 case SP_ATTR_INKSCAPE_SNAP_GLOBAL:
1870 dt->toggleSnapGlobal();
1871 break;
1872 case SP_ATTR_INKSCAPE_SNAP_BBOX:
1873 v = nv->snap_manager.snapprefs.getSnapModeBBox();
1874 sp_repr_set_boolean(repr, "inkscape:snap-bbox", !v);
1875 break;
1876 case SP_ATTR_INKSCAPE_BBOX_PATHS:
1877 v = nv->snap_manager.snapprefs.getSnapToBBoxPath();
1878 sp_repr_set_boolean(repr, "inkscape:bbox-paths", !v);
1879 break;
1880 case SP_ATTR_INKSCAPE_BBOX_NODES:
1881 v = nv->snap_manager.snapprefs.getSnapToBBoxNode();
1882 sp_repr_set_boolean(repr, "inkscape:bbox-nodes", !v);
1883 break;
1884 case SP_ATTR_INKSCAPE_SNAP_NODES:
1885 v = nv->snap_manager.snapprefs.getSnapModeNode();
1886 sp_repr_set_boolean(repr, "inkscape:snap-nodes", !v);
1887 break;
1888 case SP_ATTR_INKSCAPE_OBJECT_PATHS:
1889 v = nv->snap_manager.snapprefs.getSnapToItemPath();
1890 sp_repr_set_boolean(repr, "inkscape:object-paths", !v);
1891 break;
1892 case SP_ATTR_INKSCAPE_OBJECT_NODES:
1893 v = nv->snap_manager.snapprefs.getSnapToItemNode();
1894 sp_repr_set_boolean(repr, "inkscape:object-nodes", !v);
1895 break;
1896 case SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES:
1897 v = nv->snap_manager.snapprefs.getSnapSmoothNodes();
1898 sp_repr_set_boolean(repr, "inkscape:snap-smooth-nodes", !v);
1899 break;
1900 case SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS:
1901 v = nv->snap_manager.snapprefs.getSnapIntersectionCS();
1902 sp_repr_set_boolean(repr, "inkscape:snap-intersection-paths", !v);
1903 break;
1904 case SP_ATTR_INKSCAPE_SNAP_CENTER:
1905 v = nv->snap_manager.snapprefs.getIncludeItemCenter();
1906 sp_repr_set_boolean(repr, "inkscape:snap-center", !v);
1907 break;
1908 case SP_ATTR_INKSCAPE_SNAP_GRIDS:
1909 v = nv->snap_manager.snapprefs.getSnapToGrids();
1910 sp_repr_set_boolean(repr, "inkscape:snap-grids", !v);
1911 break;
1912 case SP_ATTR_INKSCAPE_SNAP_TO_GUIDES:
1913 v = nv->snap_manager.snapprefs.getSnapToGuides();
1914 sp_repr_set_boolean(repr, "inkscape:snap-to-guides", !v);
1915 break;
1916 case SP_ATTR_INKSCAPE_SNAP_PAGE:
1917 v = nv->snap_manager.snapprefs.getSnapToPageBorder();
1918 sp_repr_set_boolean(repr, "inkscape:snap-page", !v);
1919 break;
1920 /*case SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE:
1921 v = nv->snap_manager.snapprefs.getSnapIntersectionGG();
1922 sp_repr_set_boolean(repr, "inkscape:snap-intersection-grid-guide", !v);
1923 break;*/
1924 case SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS:
1925 v = nv->snap_manager.snapprefs.getSnapLineMidpoints();
1926 sp_repr_set_boolean(repr, "inkscape:snap-midpoints", !v);
1927 break;
1928 case SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS:
1929 v = nv->snap_manager.snapprefs.getSnapObjectMidpoints();
1930 sp_repr_set_boolean(repr, "inkscape:snap-object-midpoints", !v);
1931 break;
1932 case SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS:
1933 v = nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints();
1934 sp_repr_set_boolean(repr, "inkscape:snap-bbox-edge-midpoints", !v);
1935 break;
1936 case SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS:
1937 v = nv->snap_manager.snapprefs.getSnapBBoxMidpoints();
1938 sp_repr_set_boolean(repr, "inkscape:snap-bbox-midpoints", !v);
1939 break;
1940 default:
1941 g_warning("toggle_snap_callback has been called with an ID for which no action has been defined");
1942 break;
1943 }
1945 // The snapping preferences are stored in the document, and therefore toggling makes the document dirty
1946 doc->setModifiedSinceSave();
1948 sp_document_set_undo_sensitive(doc, saved);
1949 }
1951 void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1952 {
1953 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
1955 gchar const * descr =
1956 "<ui>"
1957 " <toolbar name='SnapToolbar'>"
1958 " <toolitem action='ToggleSnapGlobal' />"
1959 " <separator />"
1960 " <toolitem action='ToggleSnapFromBBoxCorner' />"
1961 " <toolitem action='ToggleSnapToBBoxPath' />"
1962 " <toolitem action='ToggleSnapToBBoxNode' />"
1963 " <toolitem action='ToggleSnapToFromBBoxEdgeMidpoints' />"
1964 " <toolitem action='ToggleSnapToFromBBoxCenters' />"
1965 " <separator />"
1966 " <toolitem action='ToggleSnapFromNode' />"
1967 " <toolitem action='ToggleSnapToItemPath' />"
1968 " <toolitem action='ToggleSnapToPathIntersections' />"
1969 " <toolitem action='ToggleSnapToItemNode' />"
1970 " <toolitem action='ToggleSnapToSmoothNodes' />"
1971 " <toolitem action='ToggleSnapToFromLineMidpoints' />"
1972 " <toolitem action='ToggleSnapToFromObjectCenters' />"
1973 " <toolitem action='ToggleSnapToFromRotationCenter' />"
1974 " <separator />"
1975 " <toolitem action='ToggleSnapToPageBorder' />"
1976 " <toolitem action='ToggleSnapToGrids' />"
1977 " <toolitem action='ToggleSnapToGuides' />"
1978 //" <toolitem action='ToggleSnapToGridGuideIntersections' />"
1979 " </toolbar>"
1980 "</ui>";
1982 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
1984 {
1985 InkToggleAction* act = ink_toggle_action_new("ToggleSnapGlobal",
1986 _("Snap"), _("Enable snapping"), INKSCAPE_ICON_SNAP, secondarySize,
1987 SP_ATTR_INKSCAPE_SNAP_GLOBAL);
1989 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
1990 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
1991 }
1993 {
1994 InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromBBoxCorner",
1995 _("Bounding box"), _("Snap bounding box corners"), INKSCAPE_ICON_SNAP_BOUNDING_BOX,
1996 secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX);
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("ToggleSnapToBBoxPath",
2004 _("Bounding box edges"), _("Snap to edges of a bounding box"),
2005 INKSCAPE_ICON_SNAP_BOUNDING_BOX_EDGES, secondarySize, SP_ATTR_INKSCAPE_BBOX_PATHS);
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("ToggleSnapToBBoxNode",
2013 _("Bounding box corners"), _("Snap to bounding box corners"),
2014 INKSCAPE_ICON_SNAP_BOUNDING_BOX_CORNERS, secondarySize, SP_ATTR_INKSCAPE_BBOX_NODES);
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("ToggleSnapToFromBBoxEdgeMidpoints",
2022 _("BBox Edge Midpoints"), _("Snap from and to midpoints of bounding box edges"),
2023 INKSCAPE_ICON_SNAP_BOUNDING_BOX_MIDPOINTS, secondarySize,
2024 SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS);
2026 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2027 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2028 }
2030 {
2031 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxCenters",
2032 _("BBox Centers"), _("Snapping from and to centers of bounding boxes"),
2033 INKSCAPE_ICON_SNAP_BOUNDING_BOX_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX_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("ToggleSnapFromNode",
2041 _("Nodes"), _("Snap nodes or handles"), INKSCAPE_ICON_SNAP_NODES, secondarySize, SP_ATTR_INKSCAPE_SNAP_NODES);
2043 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2044 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2045 }
2047 {
2048 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemPath",
2049 _("Paths"), _("Snap to paths"), INKSCAPE_ICON_SNAP_NODES_PATH, secondarySize,
2050 SP_ATTR_INKSCAPE_OBJECT_PATHS);
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("ToggleSnapToPathIntersections",
2058 _("Path intersections"), _("Snap to path intersections"),
2059 INKSCAPE_ICON_SNAP_NODES_INTERSECTION, secondarySize, SP_ATTR_INKSCAPE_SNAP_INTERS_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("ToggleSnapToItemNode",
2067 _("To nodes"), _("Snap to cusp nodes"), INKSCAPE_ICON_SNAP_NODES_CUSP, secondarySize,
2068 SP_ATTR_INKSCAPE_OBJECT_NODES);
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("ToggleSnapToSmoothNodes",
2076 _("Smooth nodes"), _("Snap to smooth nodes"), INKSCAPE_ICON_SNAP_NODES_SMOOTH,
2077 secondarySize, SP_ATTR_INKSCAPE_SNAP_SMOOTH_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("ToggleSnapToFromLineMidpoints",
2085 _("Line Midpoints"), _("Snap from and to midpoints of line segments"),
2086 INKSCAPE_ICON_SNAP_NODES_MIDPOINT, secondarySize, SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS);
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("ToggleSnapToFromObjectCenters",
2094 _("Object Centers"), _("Snap from and to centers of objects"),
2095 INKSCAPE_ICON_SNAP_NODES_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_OBJECT_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("ToggleSnapToFromRotationCenter",
2103 _("Rotation Centers"), _("Snap from and to an item's rotation center"),
2104 INKSCAPE_ICON_SNAP_NODES_ROTATION_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_CENTER);
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("ToggleSnapToPageBorder",
2112 _("Page border"), _("Snap to the page border"), INKSCAPE_ICON_SNAP_PAGE,
2113 secondarySize, SP_ATTR_INKSCAPE_SNAP_PAGE);
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("ToggleSnapToGrids",
2121 _("Grids"), _("Snap to grids"), INKSCAPE_ICON_GRID_RECTANGULAR, secondarySize,
2122 SP_ATTR_INKSCAPE_SNAP_GRIDS);
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("ToggleSnapToGuides",
2130 _("Guides"), _("Snap to guides"), INKSCAPE_ICON_GUIDES, secondarySize,
2131 SP_ATTR_INKSCAPE_SNAP_TO_GUIDES);
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("ToggleSnapToGridGuideIntersections",
2139 _("Grid/guide intersections"), _("Snap to intersections of a grid with a guide"),
2140 INKSCAPE_ICON_SNAP_GRID_GUIDE_INTERSECTIONS, secondarySize,
2141 SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE);
2143 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2144 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2145 }*/
2147 setupToolboxCommon( toolbox, desktop, descr,
2148 "/ui/SnapToolbar",
2149 "/toolbox/secondary",
2150 GTK_ORIENTATION_HORIZONTAL );
2151 }
2153 void update_snap_toolbox(SPDesktop *desktop, SPEventContext */*eventcontext*/, GtkWidget *toolbox)
2154 {
2155 g_assert(desktop != NULL);
2156 g_assert(toolbox != NULL);
2158 SPNamedView *nv = sp_desktop_namedview(desktop);
2159 if (nv == NULL) {
2160 g_warning("Namedview cannot be retrieved (in update_snap_toolbox)!");
2161 return;
2162 }
2164 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2166 Glib::RefPtr<Gtk::Action> act1 = mainActions->get_action("ToggleSnapGlobal");
2167 Glib::RefPtr<Gtk::Action> act2 = mainActions->get_action("ToggleSnapFromBBoxCorner");
2168 Glib::RefPtr<Gtk::Action> act3 = mainActions->get_action("ToggleSnapToBBoxPath");
2169 Glib::RefPtr<Gtk::Action> act4 = mainActions->get_action("ToggleSnapToBBoxNode");
2170 Glib::RefPtr<Gtk::Action> act4b = mainActions->get_action("ToggleSnapToFromBBoxEdgeMidpoints");
2171 Glib::RefPtr<Gtk::Action> act4c = mainActions->get_action("ToggleSnapToFromBBoxCenters");
2172 Glib::RefPtr<Gtk::Action> act5 = mainActions->get_action("ToggleSnapFromNode");
2173 Glib::RefPtr<Gtk::Action> act6 = mainActions->get_action("ToggleSnapToItemPath");
2174 Glib::RefPtr<Gtk::Action> act6b = mainActions->get_action("ToggleSnapToPathIntersections");
2175 Glib::RefPtr<Gtk::Action> act7 = mainActions->get_action("ToggleSnapToItemNode");
2176 Glib::RefPtr<Gtk::Action> act8 = mainActions->get_action("ToggleSnapToSmoothNodes");
2177 Glib::RefPtr<Gtk::Action> act9 = mainActions->get_action("ToggleSnapToFromLineMidpoints");
2178 Glib::RefPtr<Gtk::Action> act10 = mainActions->get_action("ToggleSnapToFromObjectCenters");
2179 Glib::RefPtr<Gtk::Action> act11 = mainActions->get_action("ToggleSnapToFromRotationCenter");
2180 Glib::RefPtr<Gtk::Action> act12 = mainActions->get_action("ToggleSnapToPageBorder");
2181 //Glib::RefPtr<Gtk::Action> act13 = mainActions->get_action("ToggleSnapToGridGuideIntersections");
2182 Glib::RefPtr<Gtk::Action> act14 = mainActions->get_action("ToggleSnapToGrids");
2183 Glib::RefPtr<Gtk::Action> act15 = mainActions->get_action("ToggleSnapToGuides");
2186 if (!act1) {
2187 return; // The snap actions haven't been defined yet (might be the case during startup)
2188 }
2190 // The ..._set_active calls below will toggle the buttons, but this shouldn't lead to
2191 // changes in our document because we're only updating the UI;
2192 // Setting the "freeze" parameter to true will block the code in toggle_snap_callback()
2193 g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(TRUE));
2195 bool const c1 = nv->snap_manager.snapprefs.getSnapEnabledGlobally();
2196 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act1->gobj()), c1);
2198 bool const c2 = nv->snap_manager.snapprefs.getSnapModeBBox();
2199 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act2->gobj()), c2);
2200 gtk_action_set_sensitive(GTK_ACTION(act2->gobj()), c1);
2202 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act3->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxPath());
2203 gtk_action_set_sensitive(GTK_ACTION(act3->gobj()), c1 && c2);
2204 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxNode());
2205 gtk_action_set_sensitive(GTK_ACTION(act4->gobj()), c1 && c2);
2206 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4b->gobj()), nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints());
2207 gtk_action_set_sensitive(GTK_ACTION(act4b->gobj()), c1 && c2);
2208 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4c->gobj()), nv->snap_manager.snapprefs.getSnapBBoxMidpoints());
2209 gtk_action_set_sensitive(GTK_ACTION(act4c->gobj()), c1 && c2);
2211 bool const c3 = nv->snap_manager.snapprefs.getSnapModeNode();
2212 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act5->gobj()), c3);
2213 gtk_action_set_sensitive(GTK_ACTION(act5->gobj()), c1);
2215 bool const c4 = nv->snap_manager.snapprefs.getSnapToItemPath();
2216 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6->gobj()), c4);
2217 gtk_action_set_sensitive(GTK_ACTION(act6->gobj()), c1 && c3);
2218 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6b->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionCS());
2219 gtk_action_set_sensitive(GTK_ACTION(act6b->gobj()), c1 && c3 && c4);
2220 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act7->gobj()), nv->snap_manager.snapprefs.getSnapToItemNode());
2221 gtk_action_set_sensitive(GTK_ACTION(act7->gobj()), c1 && c3);
2222 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act8->gobj()), nv->snap_manager.snapprefs.getSnapSmoothNodes());
2223 gtk_action_set_sensitive(GTK_ACTION(act8->gobj()), c1 && c3);
2224 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act9->gobj()), nv->snap_manager.snapprefs.getSnapLineMidpoints());
2225 gtk_action_set_sensitive(GTK_ACTION(act9->gobj()), c1 && c3);
2226 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act10->gobj()), nv->snap_manager.snapprefs.getSnapObjectMidpoints());
2227 gtk_action_set_sensitive(GTK_ACTION(act10->gobj()), c1 && c3);
2228 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act11->gobj()), nv->snap_manager.snapprefs.getIncludeItemCenter());
2229 gtk_action_set_sensitive(GTK_ACTION(act11->gobj()), c1 && c3);
2231 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act12->gobj()), nv->snap_manager.snapprefs.getSnapToPageBorder());
2232 gtk_action_set_sensitive(GTK_ACTION(act12->gobj()), c1);
2233 //gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act13->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionGG());
2234 //gtk_action_set_sensitive(GTK_ACTION(act13->gobj()), c1);
2236 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act14->gobj()), nv->snap_manager.snapprefs.getSnapToGrids());
2237 gtk_action_set_sensitive(GTK_ACTION(act14->gobj()), c1);
2238 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act15->gobj()), nv->snap_manager.snapprefs.getSnapToGuides());
2239 gtk_action_set_sensitive(GTK_ACTION(act15->gobj()), c1);
2242 g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(FALSE)); // unfreeze (see above)
2243 }
2245 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
2246 {
2247 gtk_widget_show(toolbox_toplevel);
2248 GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
2250 GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
2251 if (!shown_toolbox) {
2252 return;
2253 }
2254 gtk_widget_show(toolbox);
2256 gtk_widget_show_all(shown_toolbox);
2257 }
2259 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop)
2260 {
2261 GtkWidget *tbl = gtk_toolbar_new();
2262 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
2263 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
2265 gtk_widget_show_all(tbl);
2266 sp_set_font_size_smaller (tbl);
2268 return tbl;
2269 }
2271 #define MODE_LABEL_WIDTH 70
2273 //########################
2274 //## Star ##
2275 //########################
2277 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2278 {
2279 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2281 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2282 // do not remember prefs if this call is initiated by an undo change, because undoing object
2283 // creation sets bogus values to its attributes before it is deleted
2284 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2285 prefs->setInt("/tools/shapes/star/magnitude", (gint)adj->value);
2286 }
2288 // quit if run by the attr_changed listener
2289 if (g_object_get_data( dataKludge, "freeze" )) {
2290 return;
2291 }
2293 // in turn, prevent listener from responding
2294 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2296 bool modmade = false;
2298 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2299 GSList const *items = selection->itemList();
2300 for (; items != NULL; items = items->next) {
2301 if (SP_IS_STAR((SPItem *) items->data)) {
2302 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2303 sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
2304 sp_repr_set_svg_double(repr, "sodipodi:arg2",
2305 (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
2306 + M_PI / (gint)adj->value));
2307 SP_OBJECT((SPItem *) items->data)->updateRepr();
2308 modmade = true;
2309 }
2310 }
2311 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2312 _("Star: Change number of corners"));
2314 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2315 }
2317 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2318 {
2319 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2321 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2322 if (!IS_NAN(adj->value)) {
2323 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2324 prefs->setDouble("/tools/shapes/star/proportion", adj->value);
2325 }
2326 }
2328 // quit if run by the attr_changed listener
2329 if (g_object_get_data( dataKludge, "freeze" )) {
2330 return;
2331 }
2333 // in turn, prevent listener from responding
2334 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2336 bool modmade = false;
2337 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2338 GSList const *items = selection->itemList();
2339 for (; items != NULL; items = items->next) {
2340 if (SP_IS_STAR((SPItem *) items->data)) {
2341 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2343 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2344 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2345 if (r2 < r1) {
2346 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
2347 } else {
2348 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
2349 }
2351 SP_OBJECT((SPItem *) items->data)->updateRepr();
2352 modmade = true;
2353 }
2354 }
2356 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2357 _("Star: Change spoke ratio"));
2359 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2360 }
2362 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
2363 {
2364 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2365 bool flat = ege_select_one_action_get_active( act ) == 0;
2367 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2368 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2369 prefs->setBool( "/tools/shapes/star/isflatsided", flat);
2370 }
2372 // quit if run by the attr_changed listener
2373 if (g_object_get_data( dataKludge, "freeze" )) {
2374 return;
2375 }
2377 // in turn, prevent listener from responding
2378 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2380 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2381 GSList const *items = selection->itemList();
2382 GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2383 bool modmade = false;
2385 if ( prop_action ) {
2386 gtk_action_set_sensitive( prop_action, !flat );
2387 }
2389 for (; items != NULL; items = items->next) {
2390 if (SP_IS_STAR((SPItem *) items->data)) {
2391 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2392 repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
2393 SP_OBJECT((SPItem *) items->data)->updateRepr();
2394 modmade = true;
2395 }
2396 }
2398 if (modmade) {
2399 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2400 flat ? _("Make polygon") : _("Make star"));
2401 }
2403 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2404 }
2406 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2407 {
2408 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2410 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2411 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2412 prefs->setDouble("/tools/shapes/star/rounded", (gdouble) adj->value);
2413 }
2415 // quit if run by the attr_changed listener
2416 if (g_object_get_data( dataKludge, "freeze" )) {
2417 return;
2418 }
2420 // in turn, prevent listener from responding
2421 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2423 bool modmade = false;
2425 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2426 GSList const *items = selection->itemList();
2427 for (; items != NULL; items = items->next) {
2428 if (SP_IS_STAR((SPItem *) items->data)) {
2429 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2430 sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2431 SP_OBJECT(items->data)->updateRepr();
2432 modmade = true;
2433 }
2434 }
2435 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2436 _("Star: Change rounding"));
2438 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2439 }
2441 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2442 {
2443 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2445 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2446 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2447 prefs->setDouble("/tools/shapes/star/randomized", (gdouble) adj->value);
2448 }
2450 // quit if run by the attr_changed listener
2451 if (g_object_get_data( dataKludge, "freeze" )) {
2452 return;
2453 }
2455 // in turn, prevent listener from responding
2456 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2458 bool modmade = false;
2460 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2461 GSList const *items = selection->itemList();
2462 for (; items != NULL; items = items->next) {
2463 if (SP_IS_STAR((SPItem *) items->data)) {
2464 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2465 sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2466 SP_OBJECT(items->data)->updateRepr();
2467 modmade = true;
2468 }
2469 }
2470 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2471 _("Star: Change randomization"));
2473 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2474 }
2477 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2478 gchar const */*old_value*/, gchar const */*new_value*/,
2479 bool /*is_interactive*/, gpointer data)
2480 {
2481 GtkWidget *tbl = GTK_WIDGET(data);
2483 // quit if run by the _changed callbacks
2484 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2485 return;
2486 }
2488 // in turn, prevent callbacks from responding
2489 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2491 GtkAdjustment *adj = 0;
2493 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2494 bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2496 if (!strcmp(name, "inkscape:randomized")) {
2497 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2498 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2499 } else if (!strcmp(name, "inkscape:rounded")) {
2500 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2501 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2502 } else if (!strcmp(name, "inkscape:flatsided")) {
2503 GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2504 char const *flatsides = repr->attribute("inkscape:flatsided");
2505 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2506 if ( flatsides && !strcmp(flatsides,"false") ) {
2507 ege_select_one_action_set_active( flat_action, 1 );
2508 gtk_action_set_sensitive( prop_action, TRUE );
2509 } else {
2510 ege_select_one_action_set_active( flat_action, 0 );
2511 gtk_action_set_sensitive( prop_action, FALSE );
2512 }
2513 } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2514 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2515 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2516 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2517 if (r2 < r1) {
2518 gtk_adjustment_set_value(adj, r2/r1);
2519 } else {
2520 gtk_adjustment_set_value(adj, r1/r2);
2521 }
2522 } else if (!strcmp(name, "sodipodi:sides")) {
2523 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2524 gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2525 }
2527 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2528 }
2531 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2532 {
2533 NULL, /* child_added */
2534 NULL, /* child_removed */
2535 star_tb_event_attr_changed,
2536 NULL, /* content_changed */
2537 NULL /* order_changed */
2538 };
2541 /**
2542 * \param selection Should not be NULL.
2543 */
2544 static void
2545 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2546 {
2547 int n_selected = 0;
2548 Inkscape::XML::Node *repr = NULL;
2550 purge_repr_listener( tbl, tbl );
2552 for (GSList const *items = selection->itemList();
2553 items != NULL;
2554 items = items->next)
2555 {
2556 if (SP_IS_STAR((SPItem *) items->data)) {
2557 n_selected++;
2558 repr = SP_OBJECT_REPR((SPItem *) items->data);
2559 }
2560 }
2562 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2564 if (n_selected == 0) {
2565 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2566 } else if (n_selected == 1) {
2567 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2569 if (repr) {
2570 g_object_set_data( tbl, "repr", repr );
2571 Inkscape::GC::anchor(repr);
2572 sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2573 sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2574 }
2575 } else {
2576 // FIXME: implement averaging of all parameters for multiple selected stars
2577 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2578 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2579 }
2580 }
2583 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2584 {
2585 // FIXME: in this and all other _default functions, set some flag telling the value_changed
2586 // callbacks to lump all the changes for all selected objects in one undo step
2588 GtkAdjustment *adj = 0;
2590 // fixme: make settable in prefs!
2591 gint mag = 5;
2592 gdouble prop = 0.5;
2593 gboolean flat = FALSE;
2594 gdouble randomized = 0;
2595 gdouble rounded = 0;
2597 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2598 ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2600 GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2601 gtk_action_set_sensitive( sb2, !flat );
2603 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2604 gtk_adjustment_set_value(adj, mag);
2605 gtk_adjustment_value_changed(adj);
2607 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2608 gtk_adjustment_set_value(adj, prop);
2609 gtk_adjustment_value_changed(adj);
2611 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2612 gtk_adjustment_set_value(adj, rounded);
2613 gtk_adjustment_value_changed(adj);
2615 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2616 gtk_adjustment_set_value(adj, randomized);
2617 gtk_adjustment_value_changed(adj);
2618 }
2621 void
2622 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2623 {
2624 GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2625 if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2626 GtkWidget *l = gtk_label_new(NULL);
2627 gtk_label_set_markup(GTK_LABEL(l), title);
2628 gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2629 if ( GTK_IS_TOOLBAR(tbl) ) {
2630 gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2631 } else {
2632 gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2633 }
2634 gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2635 }
2638 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2639 {
2640 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
2642 {
2643 EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2644 ege_output_action_set_use_markup( act, TRUE );
2645 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2646 g_object_set_data( holder, "mode_action", act );
2647 }
2649 {
2650 EgeAdjustmentAction* eact = 0;
2651 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2652 bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2654 /* Flatsided checkbox */
2655 {
2656 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2658 GtkTreeIter iter;
2659 gtk_list_store_append( model, &iter );
2660 gtk_list_store_set( model, &iter,
2661 0, _("Polygon"),
2662 1, _("Regular polygon (with one handle) instead of a star"),
2663 2, INKSCAPE_ICON_DRAW_POLYGON,
2664 -1 );
2666 gtk_list_store_append( model, &iter );
2667 gtk_list_store_set( model, &iter,
2668 0, _("Star"),
2669 1, _("Star instead of a regular polygon (with one handle)"),
2670 2, INKSCAPE_ICON_DRAW_STAR,
2671 -1 );
2673 EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2674 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2675 g_object_set_data( holder, "flat_action", act );
2677 ege_select_one_action_set_appearance( act, "full" );
2678 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2679 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2680 ege_select_one_action_set_icon_column( act, 2 );
2681 ege_select_one_action_set_icon_size( act, secondarySize );
2682 ege_select_one_action_set_tooltip_column( act, 1 );
2684 ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2685 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2686 }
2688 /* Magnitude */
2689 {
2690 gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2691 gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2692 eact = create_adjustment_action( "MagnitudeAction",
2693 _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2694 "/tools/shapes/star/magnitude", 3,
2695 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2696 3, 1024, 1, 5,
2697 labels, values, G_N_ELEMENTS(labels),
2698 sp_stb_magnitude_value_changed,
2699 1.0, 0 );
2700 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2701 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2702 }
2704 /* Spoke ratio */
2705 {
2706 gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2707 gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2708 eact = create_adjustment_action( "SpokeAction",
2709 _("Spoke ratio"), _("Spoke ratio:"),
2710 // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2711 // Base radius is the same for the closest handle.
2712 _("Base radius to tip radius ratio"),
2713 "/tools/shapes/star/proportion", 0.5,
2714 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2715 0.01, 1.0, 0.01, 0.1,
2716 labels, values, G_N_ELEMENTS(labels),
2717 sp_stb_proportion_value_changed );
2718 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2719 g_object_set_data( holder, "prop_action", eact );
2720 }
2722 if ( !isFlatSided ) {
2723 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2724 } else {
2725 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2726 }
2728 /* Roundedness */
2729 {
2730 gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2731 gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2732 eact = create_adjustment_action( "RoundednessAction",
2733 _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2734 "/tools/shapes/star/rounded", 0.0,
2735 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2736 -10.0, 10.0, 0.01, 0.1,
2737 labels, values, G_N_ELEMENTS(labels),
2738 sp_stb_rounded_value_changed );
2739 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2740 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2741 }
2743 /* Randomization */
2744 {
2745 gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2746 gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2747 eact = create_adjustment_action( "RandomizationAction",
2748 _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2749 "/tools/shapes/star/randomized", 0.0,
2750 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2751 -10.0, 10.0, 0.001, 0.01,
2752 labels, values, G_N_ELEMENTS(labels),
2753 sp_stb_randomized_value_changed, 0.1, 3 );
2754 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2755 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2756 }
2757 }
2759 {
2760 /* Reset */
2761 {
2762 GtkAction* act = gtk_action_new( "StarResetAction",
2763 _("Defaults"),
2764 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2765 GTK_STOCK_CLEAR );
2766 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2767 gtk_action_group_add_action( mainActions, act );
2768 gtk_action_set_sensitive( act, TRUE );
2769 }
2770 }
2772 sigc::connection *connection = new sigc::connection(
2773 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2774 );
2775 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2776 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2777 }
2780 //########################
2781 //## Rect ##
2782 //########################
2784 static void sp_rtb_sensitivize( GObject *tbl )
2785 {
2786 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2787 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2788 GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2790 if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2791 gtk_action_set_sensitive( not_rounded, FALSE );
2792 } else {
2793 gtk_action_set_sensitive( not_rounded, TRUE );
2794 }
2795 }
2798 static void
2799 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2800 void (*setter)(SPRect *, gdouble))
2801 {
2802 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2804 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2805 SPUnit const *unit = tracker->getActiveUnit();
2807 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2808 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2809 prefs->setDouble(Glib::ustring("/tools/shapes/rect/") + value_name, sp_units_get_pixels(adj->value, *unit));
2810 }
2812 // quit if run by the attr_changed listener
2813 if (g_object_get_data( tbl, "freeze" )) {
2814 return;
2815 }
2817 // in turn, prevent listener from responding
2818 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2820 bool modmade = false;
2821 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2822 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2823 if (SP_IS_RECT(items->data)) {
2824 if (adj->value != 0) {
2825 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2826 } else {
2827 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2828 }
2829 modmade = true;
2830 }
2831 }
2833 sp_rtb_sensitivize( tbl );
2835 if (modmade) {
2836 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2837 _("Change rectangle"));
2838 }
2840 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2841 }
2843 static void
2844 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2845 {
2846 sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2847 }
2849 static void
2850 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2851 {
2852 sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2853 }
2855 static void
2856 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2857 {
2858 sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2859 }
2861 static void
2862 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2863 {
2864 sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2865 }
2869 static void
2870 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2871 {
2872 GtkAdjustment *adj = 0;
2874 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2875 gtk_adjustment_set_value(adj, 0.0);
2876 // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2877 gtk_adjustment_value_changed(adj);
2879 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2880 gtk_adjustment_set_value(adj, 0.0);
2881 gtk_adjustment_value_changed(adj);
2883 sp_rtb_sensitivize( obj );
2884 }
2886 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2887 gchar const */*old_value*/, gchar const */*new_value*/,
2888 bool /*is_interactive*/, gpointer data)
2889 {
2890 GObject *tbl = G_OBJECT(data);
2892 // quit if run by the _changed callbacks
2893 if (g_object_get_data( tbl, "freeze" )) {
2894 return;
2895 }
2897 // in turn, prevent callbacks from responding
2898 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2900 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2901 SPUnit const *unit = tracker->getActiveUnit();
2903 gpointer item = g_object_get_data( tbl, "item" );
2904 if (item && SP_IS_RECT(item)) {
2905 {
2906 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2907 gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2908 gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2909 }
2911 {
2912 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2913 gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2914 gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2915 }
2917 {
2918 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2919 gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2920 gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2921 }
2923 {
2924 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2925 gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2926 gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2927 }
2928 }
2930 sp_rtb_sensitivize( tbl );
2932 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2933 }
2936 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2937 NULL, /* child_added */
2938 NULL, /* child_removed */
2939 rect_tb_event_attr_changed,
2940 NULL, /* content_changed */
2941 NULL /* order_changed */
2942 };
2944 /**
2945 * \param selection should not be NULL.
2946 */
2947 static void
2948 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2949 {
2950 int n_selected = 0;
2951 Inkscape::XML::Node *repr = NULL;
2952 SPItem *item = NULL;
2954 if ( g_object_get_data( tbl, "repr" ) ) {
2955 g_object_set_data( tbl, "item", NULL );
2956 }
2957 purge_repr_listener( tbl, tbl );
2959 for (GSList const *items = selection->itemList();
2960 items != NULL;
2961 items = items->next) {
2962 if (SP_IS_RECT((SPItem *) items->data)) {
2963 n_selected++;
2964 item = (SPItem *) items->data;
2965 repr = SP_OBJECT_REPR(item);
2966 }
2967 }
2969 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2971 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2973 if (n_selected == 0) {
2974 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2976 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2977 gtk_action_set_sensitive(w, FALSE);
2978 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2979 gtk_action_set_sensitive(h, FALSE);
2981 } else if (n_selected == 1) {
2982 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2983 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2985 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2986 gtk_action_set_sensitive(w, TRUE);
2987 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2988 gtk_action_set_sensitive(h, TRUE);
2990 if (repr) {
2991 g_object_set_data( tbl, "repr", repr );
2992 g_object_set_data( tbl, "item", item );
2993 Inkscape::GC::anchor(repr);
2994 sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2995 sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2996 }
2997 } else {
2998 // FIXME: implement averaging of all parameters for multiple selected
2999 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3000 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3001 sp_rtb_sensitivize( tbl );
3002 }
3003 }
3006 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3007 {
3008 EgeAdjustmentAction* eact = 0;
3009 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3011 {
3012 EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
3013 ege_output_action_set_use_markup( act, TRUE );
3014 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3015 g_object_set_data( holder, "mode_action", act );
3016 }
3018 // rx/ry units menu: create
3019 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
3020 //tracker->addUnit( SP_UNIT_PERCENT, 0 );
3021 // fixme: add % meaning per cent of the width/height
3022 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
3023 g_object_set_data( holder, "tracker", tracker );
3025 /* W */
3026 {
3027 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3028 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3029 eact = create_adjustment_action( "RectWidthAction",
3030 _("Width"), _("W:"), _("Width of rectangle"),
3031 "/tools/shapes/rect/width", 0,
3032 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
3033 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3034 labels, values, G_N_ELEMENTS(labels),
3035 sp_rtb_width_value_changed );
3036 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3037 g_object_set_data( holder, "width_action", eact );
3038 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3039 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3040 }
3042 /* H */
3043 {
3044 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3045 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3046 eact = create_adjustment_action( "RectHeightAction",
3047 _("Height"), _("H:"), _("Height of rectangle"),
3048 "/tools/shapes/rect/height", 0,
3049 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3050 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3051 labels, values, G_N_ELEMENTS(labels),
3052 sp_rtb_height_value_changed );
3053 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3054 g_object_set_data( holder, "height_action", eact );
3055 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3056 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3057 }
3059 /* rx */
3060 {
3061 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3062 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3063 eact = create_adjustment_action( "RadiusXAction",
3064 _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
3065 "/tools/shapes/rect/rx", 0,
3066 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3067 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3068 labels, values, G_N_ELEMENTS(labels),
3069 sp_rtb_rx_value_changed);
3070 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3071 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3072 }
3074 /* ry */
3075 {
3076 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3077 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3078 eact = create_adjustment_action( "RadiusYAction",
3079 _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
3080 "/tools/shapes/rect/ry", 0,
3081 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3082 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3083 labels, values, G_N_ELEMENTS(labels),
3084 sp_rtb_ry_value_changed);
3085 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3086 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3087 }
3089 // add the units menu
3090 {
3091 GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
3092 gtk_action_group_add_action( mainActions, act );
3093 }
3095 /* Reset */
3096 {
3097 InkAction* inky = ink_action_new( "RectResetAction",
3098 _("Not rounded"),
3099 _("Make corners sharp"),
3100 INKSCAPE_ICON_RECTANGLE_MAKE_CORNERS_SHARP,
3101 secondarySize );
3102 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
3103 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3104 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3105 g_object_set_data( holder, "not_rounded", inky );
3106 }
3108 g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
3109 sp_rtb_sensitivize( holder );
3111 sigc::connection *connection = new sigc::connection(
3112 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
3113 );
3114 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3115 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3116 }
3118 //########################
3119 //## 3D Box ##
3120 //########################
3122 // normalize angle so that it lies in the interval [0,360]
3123 static double box3d_normalize_angle (double a) {
3124 double angle = a + ((int) (a/360.0))*360;
3125 if (angle < 0) {
3126 angle += 360.0;
3127 }
3128 return angle;
3129 }
3131 static void
3132 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
3133 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
3134 // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
3135 // have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
3136 // are reset).
3137 bool is_infinite = !persp3d_VP_is_finite(persp->perspective_impl, axis);
3139 if (is_infinite) {
3140 gtk_toggle_action_set_active(tact, TRUE);
3141 gtk_action_set_sensitive(act, TRUE);
3143 double angle = persp3d_get_infinite_angle(persp, axis);
3144 if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
3145 gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
3146 }
3147 } else {
3148 gtk_toggle_action_set_active(tact, FALSE);
3149 gtk_action_set_sensitive(act, FALSE);
3150 }
3151 }
3153 static void
3154 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
3155 if (!persp_repr) {
3156 g_print ("No perspective given to box3d_resync_toolbar().\n");
3157 return;
3158 }
3160 GtkWidget *tbl = GTK_WIDGET(data);
3161 GtkAdjustment *adj = 0;
3162 GtkAction *act = 0;
3163 GtkToggleAction *tact = 0;
3164 Persp3D *persp = persp3d_get_from_repr(persp_repr);
3165 if (!persp) {
3166 // Hmm, is it an error if this happens?
3167 return;
3168 }
3169 {
3170 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
3171 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
3172 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
3174 box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
3175 }
3176 {
3177 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
3178 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
3179 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
3181 box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
3182 }
3183 {
3184 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
3185 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
3186 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
3188 box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
3189 }
3190 }
3192 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3193 gchar const */*old_value*/, gchar const */*new_value*/,
3194 bool /*is_interactive*/, gpointer data)
3195 {
3196 GtkWidget *tbl = GTK_WIDGET(data);
3198 // quit if run by the attr_changed or selection changed listener
3199 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3200 return;
3201 }
3203 // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
3204 // sp_document_maybe_done() when the document is undo insensitive)
3205 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3207 // TODO: Only update the appropriate part of the toolbar
3208 // if (!strcmp(name, "inkscape:vp_z")) {
3209 box3d_resync_toolbar(repr, G_OBJECT(tbl));
3210 // }
3212 Persp3D *persp = persp3d_get_from_repr(repr);
3213 persp3d_update_box_reprs(persp);
3215 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3216 }
3218 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
3219 {
3220 NULL, /* child_added */
3221 NULL, /* child_removed */
3222 box3d_persp_tb_event_attr_changed,
3223 NULL, /* content_changed */
3224 NULL /* order_changed */
3225 };
3227 /**
3228 * \param selection Should not be NULL.
3229 */
3230 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
3231 // Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
3232 static void
3233 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3234 {
3235 // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
3236 // disable the angle entry fields for this direction (otherwise entering a value in them should only
3237 // update the perspectives with infinite VPs and leave the other ones untouched).
3239 Inkscape::XML::Node *persp_repr = NULL;
3240 purge_repr_listener(tbl, tbl);
3242 SPItem *item = selection->singleItem();
3243 if (item && SP_IS_BOX3D(item)) {
3244 // FIXME: Also deal with multiple selected boxes
3245 SPBox3D *box = SP_BOX3D(item);
3246 Persp3D *persp = box3d_get_perspective(box);
3247 persp_repr = SP_OBJECT_REPR(persp);
3248 if (persp_repr) {
3249 g_object_set_data(tbl, "repr", persp_repr);
3250 Inkscape::GC::anchor(persp_repr);
3251 sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
3252 sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
3253 }
3255 inkscape_active_document()->setCurrentPersp3D(persp3d_get_from_repr(persp_repr));
3256 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3257 prefs->setString("/tools/shapes/3dbox/persp", persp_repr->attribute("id"));
3259 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
3260 box3d_resync_toolbar(persp_repr, tbl);
3261 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
3262 }
3263 }
3265 static void
3266 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
3267 {
3268 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3269 SPDocument *document = sp_desktop_document(desktop);
3271 // quit if run by the attr_changed or selection changed listener
3272 if (g_object_get_data( dataKludge, "freeze" )) {
3273 return;
3274 }
3276 // in turn, prevent listener from responding
3277 g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
3279 std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
3280 if (sel_persps.empty()) {
3281 // this can happen when the document is created; we silently ignore it
3282 return;
3283 }
3284 Persp3D *persp = sel_persps.front();
3286 persp->perspective_impl->tmat.set_infinite_direction (axis, adj->value);
3287 SP_OBJECT(persp)->updateRepr();
3289 // TODO: use the correct axis here, too
3290 sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
3292 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
3293 }
3296 static void
3297 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3298 {
3299 box3d_angle_value_changed(adj, dataKludge, Proj::X);
3300 }
3302 static void
3303 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3304 {
3305 box3d_angle_value_changed(adj, dataKludge, Proj::Y);
3306 }
3308 static void
3309 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3310 {
3311 box3d_angle_value_changed(adj, dataKludge, Proj::Z);
3312 }
3315 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
3316 {
3317 // TODO: Take all selected perspectives into account
3318 std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
3319 if (sel_persps.empty()) {
3320 // this can happen when the document is created; we silently ignore it
3321 return;
3322 }
3323 Persp3D *persp = sel_persps.front();
3325 bool set_infinite = gtk_toggle_action_get_active(act);
3326 persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
3327 }
3329 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3330 {
3331 box3d_vp_state_changed(act, box3d_angle, Proj::X);
3332 }
3334 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3335 {
3336 box3d_vp_state_changed(act, box3d_angle, Proj::Y);
3337 }
3339 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3340 {
3341 box3d_vp_state_changed(act, box3d_angle, Proj::Z);
3342 }
3344 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3345 {
3346 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3347 EgeAdjustmentAction* eact = 0;
3348 SPDocument *document = sp_desktop_document (desktop);
3349 Persp3DImpl *persp_impl = document->getCurrentPersp3DImpl();
3351 EgeAdjustmentAction* box3d_angle_x = 0;
3352 EgeAdjustmentAction* box3d_angle_y = 0;
3353 EgeAdjustmentAction* box3d_angle_z = 0;
3355 /* Angle X */
3356 {
3357 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3358 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3359 eact = create_adjustment_action( "3DBoxAngleXAction",
3360 _("Angle in X direction"), _("Angle X:"),
3361 // Translators: PL is short for 'perspective line'
3362 _("Angle of PLs in X direction"),
3363 "/tools/shapes/3dbox/box3d_angle_x", 30,
3364 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
3365 -360.0, 360.0, 1.0, 10.0,
3366 labels, values, G_N_ELEMENTS(labels),
3367 box3d_angle_x_value_changed );
3368 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3369 g_object_set_data( holder, "box3d_angle_x_action", eact );
3370 box3d_angle_x = eact;
3371 }
3373 if (!persp3d_VP_is_finite(persp_impl, Proj::X)) {
3374 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3375 } else {
3376 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3377 }
3380 /* VP X state */
3381 {
3382 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
3383 // Translators: VP is short for 'vanishing point'
3384 _("State of VP in X direction"),
3385 _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
3386 INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3387 Inkscape::ICON_SIZE_DECORATION );
3388 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3389 g_object_set_data( holder, "box3d_vp_x_state_action", act );
3390 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
3391 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3392 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3393 }
3395 /* Angle Y */
3396 {
3397 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3398 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3399 eact = create_adjustment_action( "3DBoxAngleYAction",
3400 _("Angle in Y direction"), _("Angle Y:"),
3401 // Translators: PL is short for 'perspective line'
3402 _("Angle of PLs in Y direction"),
3403 "/tools/shapes/3dbox/box3d_angle_y", 30,
3404 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3405 -360.0, 360.0, 1.0, 10.0,
3406 labels, values, G_N_ELEMENTS(labels),
3407 box3d_angle_y_value_changed );
3408 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3409 g_object_set_data( holder, "box3d_angle_y_action", eact );
3410 box3d_angle_y = eact;
3411 }
3413 if (!persp3d_VP_is_finite(persp_impl, Proj::Y)) {
3414 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3415 } else {
3416 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3417 }
3419 /* VP Y state */
3420 {
3421 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
3422 // Translators: VP is short for 'vanishing point'
3423 _("State of VP in Y direction"),
3424 _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
3425 INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3426 Inkscape::ICON_SIZE_DECORATION );
3427 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3428 g_object_set_data( holder, "box3d_vp_y_state_action", act );
3429 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
3430 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3431 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3432 }
3434 /* Angle Z */
3435 {
3436 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3437 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3438 eact = create_adjustment_action( "3DBoxAngleZAction",
3439 _("Angle in Z direction"), _("Angle Z:"),
3440 // Translators: PL is short for 'perspective line'
3441 _("Angle of PLs in Z direction"),
3442 "/tools/shapes/3dbox/box3d_angle_z", 30,
3443 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3444 -360.0, 360.0, 1.0, 10.0,
3445 labels, values, G_N_ELEMENTS(labels),
3446 box3d_angle_z_value_changed );
3447 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3448 g_object_set_data( holder, "box3d_angle_z_action", eact );
3449 box3d_angle_z = eact;
3450 }
3452 if (!persp3d_VP_is_finite(persp_impl, Proj::Z)) {
3453 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3454 } else {
3455 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3456 }
3458 /* VP Z state */
3459 {
3460 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3461 // Translators: VP is short for 'vanishing point'
3462 _("State of VP in Z direction"),
3463 _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3464 INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3465 Inkscape::ICON_SIZE_DECORATION );
3466 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3467 g_object_set_data( holder, "box3d_vp_z_state_action", act );
3468 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3469 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3470 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3471 }
3473 sigc::connection *connection = new sigc::connection(
3474 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3475 );
3476 g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3477 g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3478 }
3480 //########################
3481 //## Spiral ##
3482 //########################
3484 static void
3485 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, Glib::ustring const &value_name)
3486 {
3487 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3489 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3490 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3491 prefs->setDouble("/tools/shapes/spiral/" + value_name, adj->value);
3492 }
3494 // quit if run by the attr_changed listener
3495 if (g_object_get_data( tbl, "freeze" )) {
3496 return;
3497 }
3499 // in turn, prevent listener from responding
3500 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3502 gchar* namespaced_name = g_strconcat("sodipodi:", value_name.data(), NULL);
3504 bool modmade = false;
3505 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3506 items != NULL;
3507 items = items->next)
3508 {
3509 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3510 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3511 sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3512 SP_OBJECT((SPItem *) items->data)->updateRepr();
3513 modmade = true;
3514 }
3515 }
3517 g_free(namespaced_name);
3519 if (modmade) {
3520 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3521 _("Change spiral"));
3522 }
3524 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3525 }
3527 static void
3528 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3529 {
3530 sp_spl_tb_value_changed(adj, tbl, "revolution");
3531 }
3533 static void
3534 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3535 {
3536 sp_spl_tb_value_changed(adj, tbl, "expansion");
3537 }
3539 static void
3540 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3541 {
3542 sp_spl_tb_value_changed(adj, tbl, "t0");
3543 }
3545 static void
3546 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3547 {
3548 GtkWidget *tbl = GTK_WIDGET(obj);
3550 GtkAdjustment *adj;
3552 // fixme: make settable
3553 gdouble rev = 5;
3554 gdouble exp = 1.0;
3555 gdouble t0 = 0.0;
3557 adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3558 gtk_adjustment_set_value(adj, rev);
3559 gtk_adjustment_value_changed(adj);
3561 adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3562 gtk_adjustment_set_value(adj, exp);
3563 gtk_adjustment_value_changed(adj);
3565 adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3566 gtk_adjustment_set_value(adj, t0);
3567 gtk_adjustment_value_changed(adj);
3569 spinbutton_defocus(GTK_OBJECT(tbl));
3570 }
3573 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3574 gchar const */*old_value*/, gchar const */*new_value*/,
3575 bool /*is_interactive*/, gpointer data)
3576 {
3577 GtkWidget *tbl = GTK_WIDGET(data);
3579 // quit if run by the _changed callbacks
3580 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3581 return;
3582 }
3584 // in turn, prevent callbacks from responding
3585 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3587 GtkAdjustment *adj;
3588 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3589 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3591 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3592 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3594 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3595 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3597 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3598 }
3601 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3602 NULL, /* child_added */
3603 NULL, /* child_removed */
3604 spiral_tb_event_attr_changed,
3605 NULL, /* content_changed */
3606 NULL /* order_changed */
3607 };
3609 static void
3610 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3611 {
3612 int n_selected = 0;
3613 Inkscape::XML::Node *repr = NULL;
3615 purge_repr_listener( tbl, tbl );
3617 for (GSList const *items = selection->itemList();
3618 items != NULL;
3619 items = items->next)
3620 {
3621 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3622 n_selected++;
3623 repr = SP_OBJECT_REPR((SPItem *) items->data);
3624 }
3625 }
3627 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3629 if (n_selected == 0) {
3630 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3631 } else if (n_selected == 1) {
3632 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3634 if (repr) {
3635 g_object_set_data( tbl, "repr", repr );
3636 Inkscape::GC::anchor(repr);
3637 sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3638 sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3639 }
3640 } else {
3641 // FIXME: implement averaging of all parameters for multiple selected
3642 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3643 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3644 }
3645 }
3648 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3649 {
3650 EgeAdjustmentAction* eact = 0;
3651 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3653 {
3654 EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3655 ege_output_action_set_use_markup( act, TRUE );
3656 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3657 g_object_set_data( holder, "mode_action", act );
3658 }
3660 /* Revolution */
3661 {
3662 gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3663 gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3664 eact = create_adjustment_action( "SpiralRevolutionAction",
3665 _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3666 "/tools/shapes/spiral/revolution", 3.0,
3667 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3668 0.01, 1024.0, 0.1, 1.0,
3669 labels, values, G_N_ELEMENTS(labels),
3670 sp_spl_tb_revolution_value_changed, 1, 2);
3671 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3672 }
3674 /* Expansion */
3675 {
3676 gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3677 gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3678 eact = create_adjustment_action( "SpiralExpansionAction",
3679 _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3680 "/tools/shapes/spiral/expansion", 1.0,
3681 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3682 0.0, 1000.0, 0.01, 1.0,
3683 labels, values, G_N_ELEMENTS(labels),
3684 sp_spl_tb_expansion_value_changed);
3685 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3686 }
3688 /* T0 */
3689 {
3690 gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3691 gdouble values[] = {0, 0.5, 0.9};
3692 eact = create_adjustment_action( "SpiralT0Action",
3693 _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3694 "/tools/shapes/spiral/t0", 0.0,
3695 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3696 0.0, 0.999, 0.01, 1.0,
3697 labels, values, G_N_ELEMENTS(labels),
3698 sp_spl_tb_t0_value_changed);
3699 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3700 }
3702 /* Reset */
3703 {
3704 InkAction* inky = ink_action_new( "SpiralResetAction",
3705 _("Defaults"),
3706 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3707 GTK_STOCK_CLEAR,
3708 secondarySize );
3709 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3710 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3711 }
3714 sigc::connection *connection = new sigc::connection(
3715 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3716 );
3717 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3718 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3719 }
3721 //########################
3722 //## Pen/Pencil ##
3723 //########################
3725 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3726 static Glib::ustring const
3727 freehand_tool_name(GObject *dataKludge)
3728 {
3729 SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3730 return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3731 ? "/tools/freehand/pen"
3732 : "/tools/freehand/pencil" );
3733 }
3735 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3736 {
3737 gint mode = ege_select_one_action_get_active(act);
3739 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3740 prefs->setInt(freehand_tool_name(tbl) + "/freehand-mode", mode);
3742 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3744 // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3745 // preparatory work here
3746 if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3747 SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3748 sp_pen_context_set_polyline_mode(pc);
3749 }
3750 }
3752 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3753 {
3754 /* Freehand mode toggle buttons */
3755 {
3756 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3757 guint freehandMode = prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/freehand-mode" : "/tools/freehand/pen/freehand-mode" ), 0);
3758 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3760 {
3761 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3763 GtkTreeIter iter;
3764 gtk_list_store_append( model, &iter );
3765 gtk_list_store_set( model, &iter,
3766 0, _("Bezier"),
3767 1, _("Create regular Bezier path"),
3768 2, INKSCAPE_ICON_PATH_MODE_BEZIER,
3769 -1 );
3771 gtk_list_store_append( model, &iter );
3772 gtk_list_store_set( model, &iter,
3773 0, _("Spiro"),
3774 1, _("Create Spiro path"),
3775 2, INKSCAPE_ICON_PATH_MODE_SPIRO,
3776 -1 );
3778 if (!tool_is_pencil) {
3779 gtk_list_store_append( model, &iter );
3780 gtk_list_store_set( model, &iter,
3781 0, _("Zigzag"),
3782 1, _("Create a sequence of straight line segments"),
3783 2, INKSCAPE_ICON_PATH_MODE_POLYLINE,
3784 -1 );
3786 gtk_list_store_append( model, &iter );
3787 gtk_list_store_set( model, &iter,
3788 0, _("Paraxial"),
3789 1, _("Create a sequence of paraxial line segments"),
3790 2, INKSCAPE_ICON_PATH_MODE_POLYLINE_PARAXIAL,
3791 -1 );
3792 }
3794 EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3795 "FreehandModeActionPencil" :
3796 "FreehandModeActionPen",
3797 (_("Mode:")), (_("Mode of new lines drawn by this tool")), NULL, GTK_TREE_MODEL(model) );
3798 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3800 ege_select_one_action_set_appearance( act, "full" );
3801 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3802 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3803 ege_select_one_action_set_icon_column( act, 2 );
3804 ege_select_one_action_set_icon_size( act, secondarySize );
3805 ege_select_one_action_set_tooltip_column( act, 1 );
3807 ege_select_one_action_set_active( act, freehandMode);
3808 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3809 }
3810 }
3811 }
3813 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3814 gint shape = ege_select_one_action_get_active( act );
3815 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3816 prefs->setInt(freehand_tool_name(dataKludge) + "/shape", shape);
3817 }
3819 /**
3820 * \brief Generate the list of freehand advanced shape option entries.
3821 */
3822 GList * freehand_shape_dropdown_items_list() {
3823 GList *glist = NULL;
3825 glist = g_list_append (glist, _("None"));
3826 glist = g_list_append (glist, _("Triangle in"));
3827 glist = g_list_append (glist, _("Triangle out"));
3828 glist = g_list_append (glist, _("Ellipse"));
3829 glist = g_list_append (glist, _("From clipboard"));
3831 return glist;
3832 }
3834 static void
3835 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3836 /*advanced shape options */
3837 {
3838 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3839 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3841 GList* items = 0;
3842 gint count = 0;
3843 for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3844 {
3845 GtkTreeIter iter;
3846 gtk_list_store_append( model, &iter );
3847 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3848 count++;
3849 }
3850 g_list_free( items );
3851 items = 0;
3852 EgeSelectOneAction* act1 = ege_select_one_action_new(
3853 tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3854 _("Shape:"), (_("Shape of new paths drawn by this tool")), NULL, GTK_TREE_MODEL(model));
3855 g_object_set( act1, "short_label", _("Shape:"), NULL );
3856 ege_select_one_action_set_appearance( act1, "compact" );
3857 ege_select_one_action_set_active( act1, prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/shape" : "/tools/freehand/pen/shape" ), 0) );
3858 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3859 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3860 g_object_set_data( holder, "shape_action", act1 );
3861 }
3862 }
3864 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3865 {
3866 sp_add_freehand_mode_toggle(mainActions, holder, false);
3867 freehand_add_advanced_shape_options(mainActions, holder, false);
3868 }
3871 static void
3872 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3873 {
3874 GtkWidget *tbl = GTK_WIDGET(obj);
3876 GtkAdjustment *adj;
3878 // fixme: make settable
3879 gdouble tolerance = 4;
3881 adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3882 gtk_adjustment_set_value(adj, tolerance);
3883 gtk_adjustment_value_changed(adj);
3885 spinbutton_defocus(GTK_OBJECT(tbl));
3886 }
3888 static void
3889 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3890 {
3891 // quit if run by the attr_changed listener
3892 if (g_object_get_data( tbl, "freeze" )) {
3893 return;
3894 }
3895 // in turn, prevent listener from responding
3896 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3897 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3898 prefs->setDouble("/tools/freehand/pencil/tolerance", adj->value);
3899 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3900 }
3902 /*
3903 class PencilToleranceObserver : public Inkscape::Preferences::Observer {
3904 public:
3905 PencilToleranceObserver(Glib::ustring const &path, GObject *x) : Observer(path), _obj(x)
3906 {
3907 g_object_set_data(_obj, "prefobserver", this);
3908 }
3909 virtual ~PencilToleranceObserver() {
3910 if (g_object_get_data(_obj, "prefobserver") == this) {
3911 g_object_set_data(_obj, "prefobserver", NULL);
3912 }
3913 }
3914 virtual void notify(Inkscape::Preferences::Entry const &val) {
3915 GObject* tbl = _obj;
3916 if (g_object_get_data( tbl, "freeze" )) {
3917 return;
3918 }
3919 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3921 GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl, "tolerance");
3923 double v = val.getDouble(adj->value);
3924 gtk_adjustment_set_value(adj, v);
3925 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3926 }
3927 private:
3928 GObject *_obj;
3929 };
3930 */
3932 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3933 {
3934 sp_add_freehand_mode_toggle(mainActions, holder, true);
3936 EgeAdjustmentAction* eact = 0;
3938 /* Tolerance */
3939 {
3940 gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
3941 gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
3942 eact = create_adjustment_action( "PencilToleranceAction",
3943 _("Smoothing:"), _("Smoothing: "),
3944 _("How much smoothing (simplifying) is applied to the line"),
3945 "/tools/freehand/pencil/tolerance",
3946 3.0,
3947 GTK_WIDGET(desktop->canvas), NULL,
3948 holder, TRUE, "altx-pencil",
3949 1, 100.0, 0.5, 1.0,
3950 labels, values, G_N_ELEMENTS(labels),
3951 sp_pencil_tb_tolerance_value_changed,
3952 1, 2);
3953 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3954 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3955 }
3957 /* advanced shape options */
3958 freehand_add_advanced_shape_options(mainActions, holder, true);
3960 /* Reset */
3961 {
3962 InkAction* inky = ink_action_new( "PencilResetAction",
3963 _("Defaults"),
3964 _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3965 GTK_STOCK_CLEAR,
3966 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3967 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
3968 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3969 }
3971 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3973 }
3976 //########################
3977 //## Tweak ##
3978 //########################
3980 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3981 {
3982 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3983 prefs->setDouble( "/tools/tweak/width", adj->value * 0.01 );
3984 }
3986 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3987 {
3988 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3989 prefs->setDouble( "/tools/tweak/force", adj->value * 0.01 );
3990 }
3992 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3993 {
3994 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3995 prefs->setBool("/tools/tweak/usepressure", gtk_toggle_action_get_active(act));
3996 }
3998 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3999 {
4000 int mode = ege_select_one_action_get_active( act );
4001 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4002 prefs->setInt("/tools/tweak/mode", mode);
4004 GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
4005 GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
4006 GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
4007 GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
4008 GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
4009 GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
4010 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
4011 if (doh) gtk_action_set_sensitive (doh, TRUE);
4012 if (dos) gtk_action_set_sensitive (dos, TRUE);
4013 if (dol) gtk_action_set_sensitive (dol, TRUE);
4014 if (doo) gtk_action_set_sensitive (doo, TRUE);
4015 if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
4016 if (fid) gtk_action_set_sensitive (fid, FALSE);
4017 } else {
4018 if (doh) gtk_action_set_sensitive (doh, FALSE);
4019 if (dos) gtk_action_set_sensitive (dos, FALSE);
4020 if (dol) gtk_action_set_sensitive (dol, FALSE);
4021 if (doo) gtk_action_set_sensitive (doo, FALSE);
4022 if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
4023 if (fid) gtk_action_set_sensitive (fid, TRUE);
4024 }
4025 }
4027 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4028 {
4029 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4030 prefs->setDouble( "/tools/tweak/fidelity", adj->value * 0.01 );
4031 }
4033 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
4034 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4035 prefs->setBool("/tools/tweak/doh", gtk_toggle_action_get_active(act));
4036 }
4037 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
4038 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4039 prefs->setBool("/tools/tweak/dos", gtk_toggle_action_get_active(act));
4040 }
4041 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
4042 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4043 prefs->setBool("/tools/tweak/dol", gtk_toggle_action_get_active(act));
4044 }
4045 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
4046 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4047 prefs->setBool("/tools/tweak/doo", gtk_toggle_action_get_active(act));
4048 }
4050 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4051 {
4052 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
4053 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4055 {
4056 /* Width */
4057 gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
4058 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4059 EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
4060 _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
4061 "/tools/tweak/width", 15,
4062 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
4063 1, 100, 1.0, 10.0,
4064 labels, values, G_N_ELEMENTS(labels),
4065 sp_tweak_width_value_changed, 0.01, 0, 100 );
4066 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4067 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4068 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4069 }
4072 {
4073 /* Force */
4074 gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
4075 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4076 EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
4077 _("Force"), _("Force:"), _("The force of the tweak action"),
4078 "/tools/tweak/force", 20,
4079 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
4080 1, 100, 1.0, 10.0,
4081 labels, values, G_N_ELEMENTS(labels),
4082 sp_tweak_force_value_changed, 0.01, 0, 100 );
4083 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4084 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4085 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4086 }
4088 /* Mode */
4089 {
4090 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4092 GtkTreeIter iter;
4093 gtk_list_store_append( model, &iter );
4094 gtk_list_store_set( model, &iter,
4095 0, _("Move mode"),
4096 1, _("Move objects in any direction"),
4097 2, INKSCAPE_ICON_OBJECT_TWEAK_PUSH,
4098 -1 );
4100 gtk_list_store_append( model, &iter );
4101 gtk_list_store_set( model, &iter,
4102 0, _("Move in/out mode"),
4103 1, _("Move objects towards cursor; with Shift from cursor"),
4104 2, INKSCAPE_ICON_OBJECT_TWEAK_ATTRACT,
4105 -1 );
4107 gtk_list_store_append( model, &iter );
4108 gtk_list_store_set( model, &iter,
4109 0, _("Move jitter mode"),
4110 1, _("Move objects in random directions"),
4111 2, INKSCAPE_ICON_OBJECT_TWEAK_RANDOMIZE,
4112 -1 );
4114 gtk_list_store_append( model, &iter );
4115 gtk_list_store_set( model, &iter,
4116 0, _("Scale mode"),
4117 1, _("Shrink objects, with Shift enlarge"),
4118 2, INKSCAPE_ICON_OBJECT_TWEAK_SHRINK,
4119 -1 );
4121 gtk_list_store_append( model, &iter );
4122 gtk_list_store_set( model, &iter,
4123 0, _("Rotate mode"),
4124 1, _("Rotate objects, with Shift counterclockwise"),
4125 2, INKSCAPE_ICON_OBJECT_TWEAK_ROTATE,
4126 -1 );
4128 gtk_list_store_append( model, &iter );
4129 gtk_list_store_set( model, &iter,
4130 0, _("Duplicate/delete mode"),
4131 1, _("Duplicate objects, with Shift delete"),
4132 2, INKSCAPE_ICON_OBJECT_TWEAK_DUPLICATE,
4133 -1 );
4135 gtk_list_store_append( model, &iter );
4136 gtk_list_store_set( model, &iter,
4137 0, _("Push mode"),
4138 1, _("Push parts of paths in any direction"),
4139 2, INKSCAPE_ICON_PATH_TWEAK_PUSH,
4140 -1 );
4142 gtk_list_store_append( model, &iter );
4143 gtk_list_store_set( model, &iter,
4144 0, _("Shrink/grow mode"),
4145 1, _("Shrink (inset) parts of paths; with Shift grow (outset)"),
4146 2, INKSCAPE_ICON_PATH_TWEAK_SHRINK,
4147 -1 );
4149 gtk_list_store_append( model, &iter );
4150 gtk_list_store_set( model, &iter,
4151 0, _("Attract/repel mode"),
4152 1, _("Attract parts of paths towards cursor; with Shift from cursor"),
4153 2, INKSCAPE_ICON_PATH_TWEAK_ATTRACT,
4154 -1 );
4156 gtk_list_store_append( model, &iter );
4157 gtk_list_store_set( model, &iter,
4158 0, _("Roughen mode"),
4159 1, _("Roughen parts of paths"),
4160 2, INKSCAPE_ICON_PATH_TWEAK_ROUGHEN,
4161 -1 );
4163 gtk_list_store_append( model, &iter );
4164 gtk_list_store_set( model, &iter,
4165 0, _("Color paint mode"),
4166 1, _("Paint the tool's color upon selected objects"),
4167 2, INKSCAPE_ICON_OBJECT_TWEAK_PAINT,
4168 -1 );
4170 gtk_list_store_append( model, &iter );
4171 gtk_list_store_set( model, &iter,
4172 0, _("Color jitter mode"),
4173 1, _("Jitter the colors of selected objects"),
4174 2, INKSCAPE_ICON_OBJECT_TWEAK_JITTER_COLOR,
4175 -1 );
4177 gtk_list_store_append( model, &iter );
4178 gtk_list_store_set( model, &iter,
4179 0, _("Blur mode"),
4180 1, _("Blur selected objects more; with Shift, blur less"),
4181 2, INKSCAPE_ICON_OBJECT_TWEAK_BLUR,
4182 -1 );
4185 EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4186 g_object_set( act, "short_label", _("Mode:"), NULL );
4187 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4188 g_object_set_data( holder, "mode_action", act );
4190 ege_select_one_action_set_appearance( act, "full" );
4191 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4192 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4193 ege_select_one_action_set_icon_column( act, 2 );
4194 ege_select_one_action_set_icon_size( act, secondarySize );
4195 ege_select_one_action_set_tooltip_column( act, 1 );
4197 gint mode = prefs->getInt("/tools/tweak/mode", 0);
4198 ege_select_one_action_set_active( act, mode );
4199 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
4201 g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
4202 }
4204 guint mode = prefs->getInt("/tools/tweak/mode", 0);
4206 {
4207 EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
4208 ege_output_action_set_use_markup( act, TRUE );
4209 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4210 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4211 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4212 g_object_set_data( holder, "tweak_channels_label", act);
4213 }
4215 {
4216 InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
4217 _("Hue"),
4218 _("In color mode, act on objects' hue"),
4219 NULL,
4220 Inkscape::ICON_SIZE_DECORATION );
4221 //TRANSLATORS: "H" here stands for hue
4222 g_object_set( act, "short_label", _("H"), NULL );
4223 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4224 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
4225 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doh", true) );
4226 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4227 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4228 g_object_set_data( holder, "tweak_doh", act);
4229 }
4230 {
4231 InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
4232 _("Saturation"),
4233 _("In color mode, act on objects' saturation"),
4234 NULL,
4235 Inkscape::ICON_SIZE_DECORATION );
4236 //TRANSLATORS: "S" here stands for Saturation
4237 g_object_set( act, "short_label", _("S"), NULL );
4238 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4239 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
4240 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dos", true) );
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_dos", act );
4244 }
4245 {
4246 InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
4247 _("Lightness"),
4248 _("In color mode, act on objects' lightness"),
4249 NULL,
4250 Inkscape::ICON_SIZE_DECORATION );
4251 //TRANSLATORS: "L" here stands for Lightness
4252 g_object_set( act, "short_label", _("L"), NULL );
4253 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4254 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
4255 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dol", true) );
4256 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4257 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4258 g_object_set_data( holder, "tweak_dol", act );
4259 }
4260 {
4261 InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
4262 _("Opacity"),
4263 _("In color mode, act on objects' opacity"),
4264 NULL,
4265 Inkscape::ICON_SIZE_DECORATION );
4266 //TRANSLATORS: "O" here stands for Opacity
4267 g_object_set( act, "short_label", _("O"), NULL );
4268 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4269 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
4270 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doo", true) );
4271 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4272 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4273 g_object_set_data( holder, "tweak_doo", act );
4274 }
4276 { /* Fidelity */
4277 gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
4278 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4279 EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
4280 _("Fidelity"), _("Fidelity:"),
4281 _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
4282 "/tools/tweak/fidelity", 50,
4283 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
4284 1, 100, 1.0, 10.0,
4285 labels, values, G_N_ELEMENTS(labels),
4286 sp_tweak_fidelity_value_changed, 0.01, 0, 100 );
4287 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4288 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4289 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
4290 gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
4291 g_object_set_data( holder, "tweak_fidelity", eact );
4292 }
4295 /* Use Pressure button */
4296 {
4297 InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
4298 _("Pressure"),
4299 _("Use the pressure of the input device to alter the force of tweak action"),
4300 INKSCAPE_ICON_DRAW_USE_PRESSURE,
4301 Inkscape::ICON_SIZE_DECORATION );
4302 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4303 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
4304 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/usepressure", true) );
4305 }
4307 }
4310 //########################
4311 //## Spray ##
4312 //########################
4314 static void sp_spray_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4315 {
4316 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4317 prefs->setDouble( "/tools/spray/width", adj->value );
4318 }
4320 static void sp_spray_mean_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4321 {
4322 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4323 prefs->setDouble( "/tools/spray/mean", adj->value );
4324 }
4326 static void sp_spray_standard_deviation_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4327 {
4328 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4329 prefs->setDouble( "/tools/spray/standard_deviation", adj->value );
4330 }
4332 static void sp_spray_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4333 {
4334 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4335 prefs->setBool("/tools/spray/usepressure", gtk_toggle_action_get_active(act));
4336 }
4338 static void sp_spray_mode_changed( EgeSelectOneAction *act, GObject */*tbl*/ )
4339 {
4340 int mode = ege_select_one_action_get_active( act );
4341 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4342 prefs->setInt("/tools/spray/mode", mode);
4343 }
4345 static void sp_spray_population_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4346 {
4347 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4348 prefs->setDouble( "/tools/spray/population", adj->value );
4349 }
4351 static void sp_spray_rotation_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4352 {
4353 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4354 prefs->setDouble( "/tools/spray/rotation_variation", adj->value );
4355 }
4357 static void sp_spray_scale_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4358 {
4359 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4360 prefs->setDouble( "/tools/spray/scale_variation", adj->value );
4361 }
4364 static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4365 {
4366 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
4367 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4369 {
4370 /* Width */
4371 gchar const* labels[] = {_("(narrow spray)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad spray)")};
4372 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4373 EgeAdjustmentAction *eact = create_adjustment_action( "SprayWidthAction",
4374 _("Width"), _("Width:"), _("The width of the spray area (relative to the visible canvas area)"),
4375 "/tools/spray/width", 15,
4376 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spray",
4377 1, 100, 1.0, 10.0,
4378 labels, values, G_N_ELEMENTS(labels),
4379 sp_spray_width_value_changed, 1, 0 );
4380 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4381 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4382 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4383 }
4385 {
4386 /* Mean */
4387 gchar const* labels[] = {_("(minimum mean)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum mean)")};
4388 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4389 EgeAdjustmentAction *eact = create_adjustment_action( "SprayMeanAction",
4390 _("Focus"), _("Focus:"), _("0 to spray a spot. Increase to enlarge the ring radius."),
4391 "/tools/spray/mean", 0,
4392 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-mean",
4393 0, 100, 1.0, 10.0,
4394 labels, values, G_N_ELEMENTS(labels),
4395 sp_spray_mean_value_changed, 1, 0 );
4396 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4397 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4398 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4399 }
4401 {
4402 /* Standard_deviation */
4403 gchar const* labels[] = {_("(minimum scatter)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum scatter)")};
4404 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4405 EgeAdjustmentAction *eact = create_adjustment_action( "SprayStandard_deviationAction",
4406 _("Scatter"), _("Scatter:"), _("Increase to scatter sprayed objects."),
4407 "/tools/spray/standard_deviation", 70,
4408 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-standard_deviation",
4409 1, 100, 1.0, 10.0,
4410 labels, values, G_N_ELEMENTS(labels),
4411 sp_spray_standard_deviation_value_changed, 1, 0 );
4412 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4413 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4414 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4415 }
4417 /* Mode */
4418 {
4419 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4421 GtkTreeIter iter;
4422 gtk_list_store_append( model, &iter );
4423 gtk_list_store_set( model, &iter,
4424 0, _("Spray with copies"),
4425 1, _("Spray copies of the initial selection"),
4426 2, INKSCAPE_ICON_SPRAY_COPY_MODE,
4427 -1 );
4429 gtk_list_store_append( model, &iter );
4430 gtk_list_store_set( model, &iter,
4431 0, _("Spray with clones"),
4432 1, _("Spray clones of the initial selection"),
4433 2, INKSCAPE_ICON_SPRAY_CLONE_MODE,
4434 -1 );
4436 gtk_list_store_append( model, &iter );
4437 gtk_list_store_set( model, &iter,
4438 0, _("Spray single path"),
4439 1, _("Spray objects in a single path"),
4440 2, INKSCAPE_ICON_SPRAY_UNION_MODE,
4441 -1 );
4443 EgeSelectOneAction* act = ege_select_one_action_new( "SprayModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4444 g_object_set( act, "short_label", _("Mode:"), NULL );
4445 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4446 g_object_set_data( holder, "mode_action", act );
4448 ege_select_one_action_set_appearance( act, "full" );
4449 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4450 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4451 ege_select_one_action_set_icon_column( act, 2 );
4452 ege_select_one_action_set_icon_size( act, secondarySize );
4453 ege_select_one_action_set_tooltip_column( act, 1 );
4455 gint mode = prefs->getInt("/tools/spray/mode", 1);
4456 ege_select_one_action_set_active( act, mode );
4457 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_spray_mode_changed), holder );
4459 g_object_set_data( G_OBJECT(holder), "spray_tool_mode", act);
4460 }
4462 { /* Population */
4463 gchar const* labels[] = {_("(low population)"), 0, 0, _("(default)"), 0, 0, _("(high population)")};
4464 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4465 EgeAdjustmentAction *eact = create_adjustment_action( "SprayPopulationAction",
4466 _("Amount"), _("Amount:"),
4467 _("Adjusts the number of items sprayed per clic."),
4468 "/tools/spray/population", 70,
4469 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-population",
4470 1, 100, 1.0, 10.0,
4471 labels, values, G_N_ELEMENTS(labels),
4472 sp_spray_population_value_changed, 1, 0 );
4473 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4474 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4475 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4476 g_object_set_data( holder, "spray_population", eact );
4477 }
4479 /* Use Pressure button */
4480 {
4481 InkToggleAction* act = ink_toggle_action_new( "SprayPressureAction",
4482 _("Pressure"),
4483 _("Use the pressure of the input device to alter the amount of sprayed objects."),
4484 "use_pressure",
4485 Inkscape::ICON_SIZE_DECORATION );
4486 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4487 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_spray_pressure_state_changed), NULL);
4488 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/spray/usepressure", true) );
4489 }
4491 { /* Rotation */
4492 gchar const* labels[] = {_("(low rotation variation)"), 0, 0, _("(default)"), 0, 0, _("(high rotation variation)")};
4493 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4494 EgeAdjustmentAction *eact = create_adjustment_action( "SprayRotationAction",
4495 _("Rotation"), _("Rotation:"),
4496 // xgettext:no-c-format
4497 _("Variation of the rotation of the sprayed objects. 0% for the same rotation than the original object."),
4498 "/tools/spray/rotation_variation", 0,
4499 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-rotation",
4500 0, 100, 1.0, 10.0,
4501 labels, values, G_N_ELEMENTS(labels),
4502 sp_spray_rotation_value_changed, 1, 0 );
4503 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4504 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4505 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4506 g_object_set_data( holder, "spray_rotation", eact );
4507 }
4509 { /* Scale */
4510 gchar const* labels[] = {_("(low scale variation)"), 0, 0, _("(default)"), 0, 0, _("(high scale variation)")};
4511 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4512 EgeAdjustmentAction *eact = create_adjustment_action( "SprayScaleAction",
4513 _("Scale"), _("Scale:"),
4514 // xgettext:no-c-format
4515 _("Variation in the scale of the sprayed objects. 0% for the same scale than the original object."),
4516 "/tools/spray/scale_variation", 0,
4517 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-scale",
4518 0, 100, 1.0, 10.0,
4519 labels, values, G_N_ELEMENTS(labels),
4520 sp_spray_scale_value_changed, 1, 0 );
4521 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4522 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4523 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4524 g_object_set_data( holder, "spray_scale", eact );
4525 }
4529 }
4532 //########################
4533 //## Calligraphy ##
4534 //########################
4535 static void update_presets_list (GObject *tbl)
4536 {
4537 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4538 if (g_object_get_data(tbl, "presets_blocked"))
4539 return;
4541 EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4542 if (!sel) {
4543 // WTF!? This will cause a segfault if ever reached
4544 //ege_select_one_action_set_active(sel, 0);
4545 return;
4546 }
4548 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4550 int ege_index = 1;
4551 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++ege_index) {
4552 bool match = true;
4554 std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(*i);
4555 for (std::vector<Inkscape::Preferences::Entry>::iterator j = preset.begin(); j != preset.end(); ++j) {
4556 Glib::ustring entry_name = j->getEntryName();
4557 if (entry_name == "id" || entry_name == "name") continue;
4559 void *widget = g_object_get_data(tbl, entry_name.data());
4560 if (widget) {
4561 if (GTK_IS_ADJUSTMENT(widget)) {
4562 double v = j->getDouble();
4563 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4564 //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
4565 if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
4566 match = false;
4567 break;
4568 }
4569 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4570 bool v = j->getBool();
4571 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4572 //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
4573 if ( static_cast<bool>(gtk_toggle_action_get_active(toggle)) != v ) {
4574 match = false;
4575 break;
4576 }
4577 }
4578 }
4579 }
4581 if (match) {
4582 // newly added item is at the same index as the
4583 // save command, so we need to change twice for it to take effect
4584 ege_select_one_action_set_active(sel, 0);
4585 ege_select_one_action_set_active(sel, ege_index); // one-based index
4586 return;
4587 }
4588 }
4590 // no match found
4591 ege_select_one_action_set_active(sel, 0);
4592 }
4594 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
4595 {
4596 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4597 prefs->setDouble( "/tools/calligraphic/mass", adj->value );
4598 update_presets_list(tbl);
4599 }
4601 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
4602 {
4603 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4604 prefs->setDouble( "/tools/calligraphic/wiggle", adj->value );
4605 update_presets_list(tbl);
4606 }
4608 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
4609 {
4610 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4611 prefs->setDouble( "/tools/calligraphic/angle", adj->value );
4612 update_presets_list(tbl);
4613 }
4615 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
4616 {
4617 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4618 prefs->setDouble( "/tools/calligraphic/width", adj->value );
4619 update_presets_list(tbl);
4620 }
4622 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
4623 {
4624 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4625 prefs->setDouble("/tools/calligraphic/thinning", adj->value );
4626 update_presets_list(tbl);
4627 }
4629 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
4630 {
4631 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4632 prefs->setDouble( "/tools/calligraphic/flatness", adj->value );
4633 update_presets_list(tbl);
4634 }
4636 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
4637 {
4638 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4639 prefs->setDouble( "/tools/calligraphic/tremor", adj->value );
4640 update_presets_list(tbl);
4641 }
4643 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
4644 {
4645 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4646 prefs->setDouble( "/tools/calligraphic/cap_rounding", adj->value );
4647 update_presets_list(tbl);
4648 }
4650 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject* tbl )
4651 {
4652 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4653 prefs->setBool("/tools/calligraphic/usepressure", gtk_toggle_action_get_active( act ));
4654 update_presets_list(tbl);
4655 }
4657 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject* tbl )
4658 {
4659 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4660 prefs->setBool("/tools/calligraphic/tracebackground", gtk_toggle_action_get_active( act ));
4661 update_presets_list(tbl);
4662 }
4664 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject* tbl )
4665 {
4666 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4667 GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
4668 prefs->setBool("/tools/calligraphic/usetilt", gtk_toggle_action_get_active( act ));
4669 update_presets_list(tbl);
4670 if (calligraphy_angle )
4671 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
4672 }
4675 static gchar const *const widget_names[] = {
4676 "width",
4677 "mass",
4678 "wiggle",
4679 "angle",
4680 "thinning",
4681 "tremor",
4682 "flatness",
4683 "cap_rounding",
4684 "usepressure",
4685 "tracebackground",
4686 "usetilt"
4687 };
4690 static void sp_dcc_build_presets_list(GObject *tbl)
4691 {
4692 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4694 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4695 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4696 gtk_list_store_clear (model);
4698 {
4699 GtkTreeIter iter;
4700 gtk_list_store_append( model, &iter );
4701 gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4702 }
4704 // iterate over all presets to populate the list
4705 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4706 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4707 int ii=1;
4709 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i) {
4710 GtkTreeIter iter;
4711 Glib::ustring preset_name = prefs->getString(*i + "/name");
4712 gtk_list_store_append( model, &iter );
4713 gtk_list_store_set( model, &iter, 0, _(preset_name.data()), 1, ii++, -1 );
4714 }
4716 {
4717 GtkTreeIter iter;
4718 gtk_list_store_append( model, &iter );
4719 gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4720 g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4721 }
4723 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4725 update_presets_list (tbl);
4726 }
4728 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4729 {
4730 using Inkscape::UI::Dialog::CalligraphicProfileRename;
4731 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4732 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4733 if (! desktop) return;
4735 if (g_object_get_data(tbl, "presets_blocked"))
4736 return;
4738 CalligraphicProfileRename::show(desktop);
4739 if ( !CalligraphicProfileRename::applied()) {
4740 // dialog cancelled
4741 update_presets_list (tbl);
4742 return;
4743 }
4744 Glib::ustring profile_name = CalligraphicProfileRename::getProfileName();
4746 if (profile_name.empty()) {
4747 // empty name entered
4748 update_presets_list (tbl);
4749 return;
4750 }
4752 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4754 // If there's a preset with the given name, find it and set save_path appropriately
4755 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4756 int total_presets = presets.size();
4757 int new_index = -1;
4758 Glib::ustring save_path; // profile pref path without a trailing slash
4760 int temp_index = 0;
4761 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++temp_index) {
4762 Glib::ustring name = prefs->getString(*i + "/name");
4763 if (!name.empty() && profile_name == name) {
4764 new_index = temp_index;
4765 save_path = *i;
4766 break;
4767 }
4768 }
4770 if (new_index == -1) {
4771 // no preset with this name, create
4772 new_index = total_presets + 1;
4773 gchar *profile_id = g_strdup_printf("/dcc%d", new_index);
4774 save_path = Glib::ustring("/tools/calligraphic/preset") + profile_id;
4775 g_free(profile_id);
4776 }
4778 for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4779 gchar const *const widget_name = widget_names[i];
4780 void *widget = g_object_get_data(tbl, widget_name);
4781 if (widget) {
4782 if (GTK_IS_ADJUSTMENT(widget)) {
4783 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4784 prefs->setDouble(save_path + "/" + widget_name, gtk_adjustment_get_value(adj));
4785 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4786 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4787 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4788 prefs->setBool(save_path + "/" + widget_name, gtk_toggle_action_get_active(toggle));
4789 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4790 } else {
4791 g_warning("Unknown widget type for preset: %s\n", widget_name);
4792 }
4793 } else {
4794 g_warning("Bad key when writing preset: %s\n", widget_name);
4795 }
4796 }
4797 prefs->setString(save_path + "/name", profile_name);
4799 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4800 sp_dcc_build_presets_list (tbl);
4801 }
4804 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4806 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4808 gint preset_index = ege_select_one_action_get_active( act );
4809 // This is necessary because EgeSelectOneAction spams us with GObject "changed" signal calls
4810 // even when the preset is not changed. It would be good to replace it with something more
4811 // modern. Index 0 means "No preset", so we don't do anything.
4812 if (preset_index == 0) return;
4814 gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4816 if (preset_index == save_presets_index) {
4817 // this is the Save command
4818 sp_dcc_save_profile(NULL, tbl);
4819 return;
4820 }
4822 if (g_object_get_data(tbl, "presets_blocked"))
4823 return;
4825 // preset_index is one-based so we subtract 1
4826 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4827 Glib::ustring preset_path = presets.at(preset_index - 1);
4829 if (!preset_path.empty()) {
4830 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
4832 std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(preset_path);
4834 // Shouldn't this be std::map?
4835 for (std::vector<Inkscape::Preferences::Entry>::iterator i = preset.begin(); i != preset.end(); ++i) {
4836 Glib::ustring entry_name = i->getEntryName();
4837 if (entry_name == "id" || entry_name == "name") continue;
4838 void *widget = g_object_get_data(tbl, entry_name.data());
4839 if (widget) {
4840 if (GTK_IS_ADJUSTMENT(widget)) {
4841 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4842 gtk_adjustment_set_value(adj, i->getDouble());
4843 //std::cout << "set adj " << attr_name << " to " << v << "\n";
4844 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4845 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4846 gtk_toggle_action_set_active(toggle, i->getBool());
4847 //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4848 } else {
4849 g_warning("Unknown widget type for preset: %s\n", entry_name.data());
4850 }
4851 } else {
4852 g_warning("Bad key found in a preset record: %s\n", entry_name.data());
4853 }
4854 }
4855 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4856 }
4857 }
4860 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4861 {
4862 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4863 {
4864 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4866 EgeAdjustmentAction* calligraphy_angle = 0;
4868 {
4869 /* Width */
4870 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4871 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4872 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4873 _("Pen Width"), _("Width:"),
4874 _("The width of the calligraphic pen (relative to the visible canvas area)"),
4875 "/tools/calligraphic/width", 15,
4876 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4877 1, 100, 1.0, 10.0,
4878 labels, values, G_N_ELEMENTS(labels),
4879 sp_ddc_width_value_changed, 1, 0 );
4880 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4881 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4882 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4883 }
4885 {
4886 /* Thinning */
4887 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4888 gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4889 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4890 _("Stroke Thinning"), _("Thinning:"),
4891 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4892 "/tools/calligraphic/thinning", 10,
4893 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4894 -100, 100, 1, 10.0,
4895 labels, values, G_N_ELEMENTS(labels),
4896 sp_ddc_velthin_value_changed, 1, 0);
4897 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4898 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4899 }
4901 {
4902 /* Angle */
4903 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4904 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4905 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4906 _("Pen Angle"), _("Angle:"),
4907 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4908 "/tools/calligraphic/angle", 30,
4909 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4910 -90.0, 90.0, 1.0, 10.0,
4911 labels, values, G_N_ELEMENTS(labels),
4912 sp_ddc_angle_value_changed, 1, 0 );
4913 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4914 g_object_set_data( holder, "angle_action", eact );
4915 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4916 calligraphy_angle = eact;
4917 }
4919 {
4920 /* Fixation */
4921 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4922 gdouble values[] = {0, 20, 40, 60, 90, 100};
4923 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4924 _("Fixation"), _("Fixation:"),
4925 _("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"),
4926 "/tools/calligraphic/flatness", 90,
4927 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4928 0.0, 100, 1.0, 10.0,
4929 labels, values, G_N_ELEMENTS(labels),
4930 sp_ddc_flatness_value_changed, 1, 0);
4931 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4932 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4933 }
4935 {
4936 /* Cap Rounding */
4937 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4938 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4939 // TRANSLATORS: "cap" means "end" (both start and finish) here
4940 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4941 _("Cap rounding"), _("Caps:"),
4942 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4943 "/tools/calligraphic/cap_rounding", 0.0,
4944 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4945 0.0, 5.0, 0.01, 0.1,
4946 labels, values, G_N_ELEMENTS(labels),
4947 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4948 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4949 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4950 }
4952 {
4953 /* Tremor */
4954 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4955 gdouble values[] = {0, 10, 20, 40, 60, 100};
4956 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4957 _("Stroke Tremor"), _("Tremor:"),
4958 _("Increase to make strokes rugged and trembling"),
4959 "/tools/calligraphic/tremor", 0.0,
4960 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4961 0.0, 100, 1, 10.0,
4962 labels, values, G_N_ELEMENTS(labels),
4963 sp_ddc_tremor_value_changed, 1, 0);
4965 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4966 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4967 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4968 }
4970 {
4971 /* Wiggle */
4972 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4973 gdouble values[] = {0, 20, 40, 60, 100};
4974 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4975 _("Pen Wiggle"), _("Wiggle:"),
4976 _("Increase to make the pen waver and wiggle"),
4977 "/tools/calligraphic/wiggle", 0.0,
4978 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4979 0.0, 100, 1, 10.0,
4980 labels, values, G_N_ELEMENTS(labels),
4981 sp_ddc_wiggle_value_changed, 1, 0);
4982 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4983 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4984 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4985 }
4987 {
4988 /* Mass */
4989 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4990 gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4991 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4992 _("Pen Mass"), _("Mass:"),
4993 _("Increase to make the pen drag behind, as if slowed by inertia"),
4994 "/tools/calligraphic/mass", 2.0,
4995 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4996 0.0, 100, 1, 10.0,
4997 labels, values, G_N_ELEMENTS(labels),
4998 sp_ddc_mass_value_changed, 1, 0);
4999 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5000 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5001 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5002 }
5005 /* Trace Background button */
5006 {
5007 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
5008 _("Trace Background"),
5009 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
5010 INKSCAPE_ICON_DRAW_TRACE_BACKGROUND,
5011 Inkscape::ICON_SIZE_DECORATION );
5012 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5013 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
5014 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/tracebackground", false) );
5015 g_object_set_data( holder, "tracebackground", act );
5016 }
5018 /* Use Pressure button */
5019 {
5020 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
5021 _("Pressure"),
5022 _("Use the pressure of the input device to alter the width of the pen"),
5023 INKSCAPE_ICON_DRAW_USE_PRESSURE,
5024 Inkscape::ICON_SIZE_DECORATION );
5025 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5026 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
5027 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usepressure", true) );
5028 g_object_set_data( holder, "usepressure", act );
5029 }
5031 /* Use Tilt button */
5032 {
5033 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
5034 _("Tilt"),
5035 _("Use the tilt of the input device to alter the angle of the pen's nib"),
5036 INKSCAPE_ICON_DRAW_USE_TILT,
5037 Inkscape::ICON_SIZE_DECORATION );
5038 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5039 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
5040 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs->getBool("/tools/calligraphic/usetilt", true) );
5041 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usetilt", true) );
5042 g_object_set_data( holder, "usetilt", act );
5043 }
5045 /*calligraphic profile */
5046 {
5047 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5048 EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
5049 ege_select_one_action_set_appearance (act1, "compact");
5050 g_object_set_data (holder, "profile_selector", act1 );
5052 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
5054 sp_dcc_build_presets_list (holder);
5056 g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
5057 gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
5058 }
5059 }
5060 }
5063 //########################
5064 //## Circle / Arc ##
5065 //########################
5067 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
5068 {
5069 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
5070 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
5072 if (v1 == 0 && v2 == 0) {
5073 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
5074 gtk_action_set_sensitive( ocb, FALSE );
5075 gtk_action_set_sensitive( make_whole, FALSE );
5076 }
5077 } else {
5078 gtk_action_set_sensitive( ocb, TRUE );
5079 gtk_action_set_sensitive( make_whole, TRUE );
5080 }
5081 }
5083 static void
5084 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
5085 {
5086 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5088 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5089 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5090 prefs->setDouble(Glib::ustring("/tools/shapes/arc/") + value_name, adj->value);
5091 }
5093 // quit if run by the attr_changed listener
5094 if (g_object_get_data( tbl, "freeze" )) {
5095 return;
5096 }
5098 // in turn, prevent listener from responding
5099 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5101 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
5103 bool modmade = false;
5104 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5105 items != NULL;
5106 items = items->next)
5107 {
5108 SPItem *item = SP_ITEM(items->data);
5110 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
5112 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
5113 SPArc *arc = SP_ARC(item);
5115 if (!strcmp(value_name, "start"))
5116 ge->start = (adj->value * M_PI)/ 180;
5117 else
5118 ge->end = (adj->value * M_PI)/ 180;
5120 sp_genericellipse_normalize(ge);
5121 ((SPObject *)arc)->updateRepr();
5122 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
5124 modmade = true;
5125 }
5126 }
5128 g_free(namespaced_name);
5130 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
5132 sp_arctb_sensitivize( tbl, adj->value, other->value );
5134 if (modmade) {
5135 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
5136 _("Arc: Change start/end"));
5137 }
5139 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5140 }
5143 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
5144 {
5145 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
5146 }
5148 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
5149 {
5150 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
5151 }
5154 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
5155 {
5156 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5157 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5158 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5159 prefs->setBool("/tools/shapes/arc/open", ege_select_one_action_get_active(act) != 0);
5160 }
5162 // quit if run by the attr_changed listener
5163 if (g_object_get_data( tbl, "freeze" )) {
5164 return;
5165 }
5167 // in turn, prevent listener from responding
5168 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5170 bool modmade = false;
5172 if ( ege_select_one_action_get_active(act) != 0 ) {
5173 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5174 items != NULL;
5175 items = items->next)
5176 {
5177 if (SP_IS_ARC((SPItem *) items->data)) {
5178 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5179 repr->setAttribute("sodipodi:open", "true");
5180 SP_OBJECT((SPItem *) items->data)->updateRepr();
5181 modmade = true;
5182 }
5183 }
5184 } else {
5185 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5186 items != NULL;
5187 items = items->next)
5188 {
5189 if (SP_IS_ARC((SPItem *) items->data)) {
5190 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5191 repr->setAttribute("sodipodi:open", NULL);
5192 SP_OBJECT((SPItem *) items->data)->updateRepr();
5193 modmade = true;
5194 }
5195 }
5196 }
5198 if (modmade) {
5199 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
5200 _("Arc: Change open/closed"));
5201 }
5203 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5204 }
5206 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
5207 {
5208 GtkAdjustment *adj;
5209 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
5210 gtk_adjustment_set_value(adj, 0.0);
5211 gtk_adjustment_value_changed(adj);
5213 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
5214 gtk_adjustment_set_value(adj, 0.0);
5215 gtk_adjustment_value_changed(adj);
5217 spinbutton_defocus( GTK_OBJECT(obj) );
5218 }
5220 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
5221 gchar const */*old_value*/, gchar const */*new_value*/,
5222 bool /*is_interactive*/, gpointer data)
5223 {
5224 GObject *tbl = G_OBJECT(data);
5226 // quit if run by the _changed callbacks
5227 if (g_object_get_data( tbl, "freeze" )) {
5228 return;
5229 }
5231 // in turn, prevent callbacks from responding
5232 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5234 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
5235 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
5237 GtkAdjustment *adj1,*adj2;
5238 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
5239 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
5240 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
5241 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
5243 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
5245 char const *openstr = NULL;
5246 openstr = repr->attribute("sodipodi:open");
5247 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
5249 if (openstr) {
5250 ege_select_one_action_set_active( ocb, 1 );
5251 } else {
5252 ege_select_one_action_set_active( ocb, 0 );
5253 }
5255 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5256 }
5258 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
5259 NULL, /* child_added */
5260 NULL, /* child_removed */
5261 arc_tb_event_attr_changed,
5262 NULL, /* content_changed */
5263 NULL /* order_changed */
5264 };
5267 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
5268 {
5269 int n_selected = 0;
5270 Inkscape::XML::Node *repr = NULL;
5272 purge_repr_listener( tbl, tbl );
5274 for (GSList const *items = selection->itemList();
5275 items != NULL;
5276 items = items->next)
5277 {
5278 if (SP_IS_ARC((SPItem *) items->data)) {
5279 n_selected++;
5280 repr = SP_OBJECT_REPR((SPItem *) items->data);
5281 }
5282 }
5284 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
5286 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
5287 if (n_selected == 0) {
5288 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
5289 } else if (n_selected == 1) {
5290 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
5291 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5293 if (repr) {
5294 g_object_set_data( tbl, "repr", repr );
5295 Inkscape::GC::anchor(repr);
5296 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
5297 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
5298 }
5299 } else {
5300 // FIXME: implement averaging of all parameters for multiple selected
5301 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
5302 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5303 sp_arctb_sensitivize( tbl, 1, 0 );
5304 }
5305 }
5308 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5309 {
5310 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5312 EgeAdjustmentAction* eact = 0;
5313 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
5316 {
5317 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
5318 ege_output_action_set_use_markup( act, TRUE );
5319 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5320 g_object_set_data( holder, "mode_action", act );
5321 }
5323 /* Start */
5324 {
5325 eact = create_adjustment_action( "ArcStartAction",
5326 _("Start"), _("Start:"),
5327 _("The angle (in degrees) from the horizontal to the arc's start point"),
5328 "/tools/shapes/arc/start", 0.0,
5329 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
5330 -360.0, 360.0, 1.0, 10.0,
5331 0, 0, 0,
5332 sp_arctb_start_value_changed);
5333 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5334 }
5336 /* End */
5337 {
5338 eact = create_adjustment_action( "ArcEndAction",
5339 _("End"), _("End:"),
5340 _("The angle (in degrees) from the horizontal to the arc's end point"),
5341 "/tools/shapes/arc/end", 0.0,
5342 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
5343 -360.0, 360.0, 1.0, 10.0,
5344 0, 0, 0,
5345 sp_arctb_end_value_changed);
5346 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5347 }
5349 /* Segments / Pie checkbox */
5350 {
5351 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5353 GtkTreeIter iter;
5354 gtk_list_store_append( model, &iter );
5355 gtk_list_store_set( model, &iter,
5356 0, _("Closed arc"),
5357 1, _("Switch to segment (closed shape with two radii)"),
5358 2, INKSCAPE_ICON_DRAW_ELLIPSE_SEGMENT,
5359 -1 );
5361 gtk_list_store_append( model, &iter );
5362 gtk_list_store_set( model, &iter,
5363 0, _("Open Arc"),
5364 1, _("Switch to arc (unclosed shape)"),
5365 2, INKSCAPE_ICON_DRAW_ELLIPSE_ARC,
5366 -1 );
5368 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5369 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5370 g_object_set_data( holder, "open_action", act );
5372 ege_select_one_action_set_appearance( act, "full" );
5373 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5374 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5375 ege_select_one_action_set_icon_column( act, 2 );
5376 ege_select_one_action_set_icon_size( act, secondarySize );
5377 ege_select_one_action_set_tooltip_column( act, 1 );
5379 bool isClosed = !prefs->getBool("/tools/shapes/arc/open", false);
5380 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
5381 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
5382 }
5384 /* Make Whole */
5385 {
5386 InkAction* inky = ink_action_new( "ArcResetAction",
5387 _("Make whole"),
5388 _("Make the shape a whole ellipse, not arc or segment"),
5389 INKSCAPE_ICON_DRAW_ELLIPSE_WHOLE,
5390 secondarySize );
5391 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
5392 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5393 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
5394 g_object_set_data( holder, "make_whole", inky );
5395 }
5397 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
5398 // sensitivize make whole and open checkbox
5399 {
5400 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
5401 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
5402 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
5403 }
5406 sigc::connection *connection = new sigc::connection(
5407 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
5408 );
5409 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
5410 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
5411 }
5416 // toggle button callbacks and updaters
5418 //########################
5419 //## Dropper ##
5420 //########################
5422 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
5423 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5424 prefs->setInt( "/tools/dropper/pick", gtk_toggle_action_get_active( act ) );
5425 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
5426 if ( set_action ) {
5427 if ( gtk_toggle_action_get_active( act ) ) {
5428 gtk_action_set_sensitive( set_action, TRUE );
5429 } else {
5430 gtk_action_set_sensitive( set_action, FALSE );
5431 }
5432 }
5434 spinbutton_defocus(GTK_OBJECT(tbl));
5435 }
5437 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
5438 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5439 prefs->setBool( "/tools/dropper/setalpha", gtk_toggle_action_get_active( act ) );
5440 spinbutton_defocus(GTK_OBJECT(tbl));
5441 }
5444 /**
5445 * Dropper auxiliary toolbar construction and setup.
5446 *
5447 * TODO: Would like to add swatch of current color.
5448 * TODO: Add queue of last 5 or so colors selected with new swatches so that
5449 * can drag and drop places. Will provide a nice mixing palette.
5450 */
5451 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
5452 {
5453 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5454 gint pickAlpha = prefs->getInt( "/tools/dropper/pick", 1 );
5456 {
5457 EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
5458 ege_output_action_set_use_markup( act, TRUE );
5459 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5460 }
5462 {
5463 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
5464 _("Pick opacity"),
5465 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
5466 NULL,
5467 Inkscape::ICON_SIZE_DECORATION );
5468 g_object_set( act, "short_label", _("Pick"), NULL );
5469 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5470 g_object_set_data( holder, "pick_action", act );
5471 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
5472 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
5473 }
5475 {
5476 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
5477 _("Assign opacity"),
5478 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
5479 NULL,
5480 Inkscape::ICON_SIZE_DECORATION );
5481 g_object_set( act, "short_label", _("Assign"), NULL );
5482 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5483 g_object_set_data( holder, "set_action", act );
5484 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/dropper/setalpha", true) );
5485 // make sure it's disabled if we're not picking alpha
5486 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
5487 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
5488 }
5489 }
5492 //########################
5493 //## LPETool ##
5494 //########################
5496 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
5498 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
5499 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
5500 {
5501 using namespace Inkscape::LivePathEffect;
5503 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
5504 SPEventContext *ec = desktop->event_context;
5505 if (!SP_IS_LPETOOL_CONTEXT(ec)) {
5506 return;
5507 }
5509 // only take action if run by the attr_changed listener
5510 if (!g_object_get_data(tbl, "freeze")) {
5511 // in turn, prevent listener from responding
5512 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5514 gint mode = ege_select_one_action_get_active(act);
5515 EffectType type = lpesubtools[mode].type;
5517 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5518 bool success = lpetool_try_construction(lc, type);
5519 if (success) {
5520 // since the construction was already performed, we set the state back to inactive
5521 ege_select_one_action_set_active(act, 0);
5522 mode = 0;
5523 } else {
5524 // switch to the chosen subtool
5525 SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
5526 }
5528 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5529 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5530 prefs->setInt( "/tools/lpetool/mode", mode );
5531 }
5533 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
5534 }
5535 }
5537 void sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject */*tbl*/)
5538 {
5539 SPEventContext *ec = selection->desktop()->event_context;
5540 if (!SP_IS_LPETOOL_CONTEXT(ec))
5541 return;
5543 lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
5544 }
5546 void
5547 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
5548 {
5549 using namespace Inkscape::LivePathEffect;
5550 SPEventContext *ec = selection->desktop()->event_context;
5551 if (!SP_IS_LPETOOL_CONTEXT(ec))
5552 return;
5553 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
5555 lpetool_delete_measuring_items(lc);
5556 lpetool_create_measuring_items(lc, selection);
5558 // activate line segment combo box if a single item with LPELineSegment is selected
5559 GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
5560 SPItem *item = selection->singleItem();
5561 if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
5562 SPLPEItem *lpeitem = SP_LPE_ITEM(item);
5563 Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
5564 if (lpe && lpe->effectType() == LINE_SEGMENT) {
5565 LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
5566 g_object_set_data(tbl, "currentlpe", lpe);
5567 g_object_set_data(tbl, "currentlpeitem", lpeitem);
5568 gtk_action_set_sensitive(w, TRUE);
5569 ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
5570 } else {
5571 g_object_set_data(tbl, "currentlpe", NULL);
5572 g_object_set_data(tbl, "currentlpeitem", NULL);
5573 gtk_action_set_sensitive(w, FALSE);
5574 }
5575 } else {
5576 g_object_set_data(tbl, "currentlpe", NULL);
5577 g_object_set_data(tbl, "currentlpeitem", NULL);
5578 gtk_action_set_sensitive(w, FALSE);
5579 }
5580 }
5582 static void
5583 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
5584 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5585 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5587 bool show = gtk_toggle_action_get_active( act );
5588 prefs->setBool("/tools/lpetool/show_bbox", show);
5590 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5591 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5592 lpetool_context_reset_limiting_bbox(lc);
5593 }
5594 }
5596 static void
5597 lpetool_toggle_show_measuring_info (GtkToggleAction *act, GObject *tbl) {
5598 SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
5599 if (!tools_isactive(desktop, TOOLS_LPETOOL))
5600 return;
5602 GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
5603 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5604 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5605 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5606 bool show = gtk_toggle_action_get_active( act );
5607 prefs->setBool("/tools/lpetool/show_measuring_info", show);
5608 lpetool_show_measuring_info(lc, show);
5609 gtk_action_set_sensitive(GTK_ACTION(unitact), show);
5610 }
5611 }
5613 static void lpetool_unit_changed(GtkAction* /*act*/, GObject* tbl) {
5614 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
5615 SPUnit const *unit = tracker->getActiveUnit();
5616 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5617 prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5619 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5620 if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
5621 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5622 lpetool_delete_measuring_items(lc);
5623 lpetool_create_measuring_items(lc);
5624 }
5625 }
5627 static void
5628 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
5629 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5630 Inkscape::Selection *selection = desktop->selection;
5632 Geom::OptRect bbox = selection->bounds();
5634 if (bbox) {
5635 Geom::Point A(bbox->min());
5636 Geom::Point B(bbox->max());
5638 A *= desktop->doc2dt();
5639 B *= desktop->doc2dt();
5641 // TODO: should we provide a way to store points in prefs?
5642 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5643 prefs->setDouble("/tools/lpetool/bbox_upperleftx", A[Geom::X]);
5644 prefs->setDouble("/tools/lpetool/bbox_upperlefty", A[Geom::Y]);
5645 prefs->setDouble("/tools/lpetool/bbox_lowerrightx", B[Geom::X]);
5646 prefs->setDouble("/tools/lpetool/bbox_lowerrighty", B[Geom::Y]);
5648 lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
5649 }
5651 gtk_toggle_action_set_active(act, false);
5652 }
5654 static void
5655 sp_line_segment_build_list(GObject *tbl)
5656 {
5657 g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
5659 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
5660 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
5661 gtk_list_store_clear (model);
5663 // TODO: we add the entries of rht combo box manually; later this should be done automatically
5664 {
5665 GtkTreeIter iter;
5666 gtk_list_store_append( model, &iter );
5667 gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
5668 gtk_list_store_append( model, &iter );
5669 gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
5670 gtk_list_store_append( model, &iter );
5671 gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
5672 gtk_list_store_append( model, &iter );
5673 gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
5674 }
5676 g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5677 }
5679 static void
5680 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
5681 using namespace Inkscape::LivePathEffect;
5683 // quit if run by the attr_changed listener
5684 if (g_object_get_data(tbl, "freeze")) {
5685 return;
5686 }
5688 // in turn, prevent listener from responding
5689 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5691 LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
5692 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5693 if (lpeitem) {
5694 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5695 lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
5696 sp_lpe_item_update_patheffect(lpeitem, true, true);
5697 }
5699 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5700 }
5702 static void
5703 lpetool_open_lpe_dialog (GtkToggleAction *act, gpointer data) {
5704 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5706 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5707 sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5708 }
5709 gtk_toggle_action_set_active(act, false);
5710 }
5712 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5713 {
5714 UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5715 tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5716 g_object_set_data(holder, "tracker", tracker);
5717 SPUnit const *unit = tracker->getActiveUnit();
5719 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5720 prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5722 /** Automatically create a list of LPEs that get added to the toolbar **/
5723 {
5724 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5726 GtkTreeIter iter;
5728 // the first toggle button represents the state that no subtool is active (remove this when
5729 // this can be modeled by EgeSelectOneAction or some other action)
5730 gtk_list_store_append( model, &iter );
5731 gtk_list_store_set( model, &iter,
5732 0, _("All inactive"),
5733 1, _("No geometric tool is active"),
5734 2, "draw-geometry-inactive",
5735 -1 );
5737 Inkscape::LivePathEffect::EffectType type;
5738 for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5739 type = lpesubtools[i].type;
5740 gtk_list_store_append( model, &iter );
5741 gtk_list_store_set( model, &iter,
5742 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5743 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5744 2, lpesubtools[i].icon_name,
5745 -1 );
5746 }
5748 EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5749 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5750 g_object_set_data( holder, "lpetool_mode_action", act );
5752 ege_select_one_action_set_appearance( act, "full" );
5753 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5754 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5755 ege_select_one_action_set_icon_column( act, 2 );
5756 ege_select_one_action_set_tooltip_column( act, 1 );
5758 gint lpeToolMode = prefs->getInt("/tools/lpetool/mode", 0);
5759 ege_select_one_action_set_active( act, lpeToolMode );
5760 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5761 }
5763 /* Show limiting bounding box */
5764 {
5765 InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5766 _("Show limiting bounding box"),
5767 _("Show bounding box (used to cut infinite lines)"),
5768 "show-bounding-box",
5769 Inkscape::ICON_SIZE_DECORATION );
5770 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5771 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5772 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_bbox", true ) );
5773 }
5775 /* Set limiting bounding box to bbox of current selection */
5776 {
5777 InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5778 _("Get limiting bounding box from selection"),
5779 _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5780 "draw-geometry-set-bounding-box",
5781 Inkscape::ICON_SIZE_DECORATION );
5782 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5783 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5784 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5785 }
5788 /* Combo box to choose line segment type */
5789 {
5790 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5791 EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5792 ege_select_one_action_set_appearance (act, "compact");
5793 g_object_set_data (holder, "lpetool_line_segment_action", act );
5795 g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5797 sp_line_segment_build_list (holder);
5799 g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5800 gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5801 gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5802 }
5804 /* Display measuring info for selected items */
5805 {
5806 InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5807 _("Display measuring info"),
5808 _("Display measuring info for selected items"),
5809 "draw-geometry-show-measuring-info",
5810 Inkscape::ICON_SIZE_DECORATION );
5811 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5812 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
5813 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_measuring_info", true ) );
5814 }
5816 // add the units menu
5817 {
5818 GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5819 gtk_action_group_add_action( mainActions, act );
5820 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5821 g_object_set_data(holder, "lpetool_units_action", act);
5822 gtk_action_set_sensitive(act, prefs->getBool("/tools/lpetool/show_measuring_info", true));
5823 }
5825 /* Open LPE dialog (to adapt parameters numerically) */
5826 {
5827 InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
5828 _("Open LPE dialog"),
5829 _("Open LPE dialog (to adapt parameters numerically)"),
5830 "dialog-geometry",
5831 Inkscape::ICON_SIZE_DECORATION );
5832 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5833 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
5834 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5835 }
5837 //watch selection
5838 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5840 sigc::connection *c_selection_modified =
5841 new sigc::connection (sp_desktop_selection (desktop)->connectModified
5842 (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
5843 pool->add_connection ("selection-modified", c_selection_modified);
5845 sigc::connection *c_selection_changed =
5846 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5847 (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5848 pool->add_connection ("selection-changed", c_selection_changed);
5849 }
5851 //########################
5852 //## Eraser ##
5853 //########################
5855 static void sp_erc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
5856 {
5857 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5858 prefs->setDouble( "/tools/eraser/width", adj->value );
5859 update_presets_list(tbl);
5860 }
5862 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5863 {
5864 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5865 bool eraserMode = ege_select_one_action_get_active( act ) != 0;
5866 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5867 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5868 prefs->setBool( "/tools/eraser/mode", eraserMode );
5869 }
5871 // only take action if run by the attr_changed listener
5872 if (!g_object_get_data( tbl, "freeze" )) {
5873 // in turn, prevent listener from responding
5874 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5876 if ( eraserMode != 0 ) {
5877 } else {
5878 }
5879 // TODO finish implementation
5881 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5882 }
5883 }
5885 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5886 {
5887 {
5888 /* Width */
5889 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5890 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5891 EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5892 _("Pen Width"), _("Width:"),
5893 _("The width of the eraser pen (relative to the visible canvas area)"),
5894 "/tools/eraser/width", 15,
5895 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5896 1, 100, 1.0, 10.0,
5897 labels, values, G_N_ELEMENTS(labels),
5898 sp_erc_width_value_changed, 1, 0);
5899 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5900 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5901 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5902 }
5904 {
5905 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5907 GtkTreeIter iter;
5908 gtk_list_store_append( model, &iter );
5909 gtk_list_store_set( model, &iter,
5910 0, _("Delete"),
5911 1, _("Delete objects touched by the eraser"),
5912 2, INKSCAPE_ICON_DRAW_ERASER_DELETE_OBJECTS,
5913 -1 );
5915 gtk_list_store_append( model, &iter );
5916 gtk_list_store_set( model, &iter,
5917 0, _("Cut"),
5918 1, _("Cut out from objects"),
5919 2, INKSCAPE_ICON_PATH_DIFFERENCE,
5920 -1 );
5922 EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5923 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5924 g_object_set_data( holder, "eraser_mode_action", act );
5926 ege_select_one_action_set_appearance( act, "full" );
5927 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5928 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5929 ege_select_one_action_set_icon_column( act, 2 );
5930 ege_select_one_action_set_tooltip_column( act, 1 );
5932 /// @todo Convert to boolean?
5933 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5934 gint eraserMode = prefs->getBool("/tools/eraser/mode") ? 1 : 0;
5935 ege_select_one_action_set_active( act, eraserMode );
5936 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
5937 }
5939 }
5941 //########################
5942 //## Text Toolbox ##
5943 //########################
5944 /*
5945 static void
5946 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
5947 {
5948 //Call back for letter sizing spinbutton
5949 }
5951 static void
5952 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
5953 {
5954 //Call back for line height spinbutton
5955 }
5957 static void
5958 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5959 {
5960 //Call back for horizontal kerning spinbutton
5961 }
5963 static void
5964 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5965 {
5966 //Call back for vertical kerning spinbutton
5967 }
5969 static void
5970 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
5971 {
5972 //Call back for letter rotation spinbutton
5973 }*/
5975 namespace {
5977 void
5978 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
5979 {
5980 // quit if run by the _changed callbacks
5981 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5982 return;
5983 }
5985 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5987 SPStyle *query =
5988 sp_style_new (SP_ACTIVE_DOCUMENT);
5990 int result_family =
5991 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5993 int result_style =
5994 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5996 int result_numbers =
5997 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5999 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6001 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6002 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
6003 // there are no texts in selection, read from prefs
6005 sp_style_read_from_prefs(query, "/tools/text");
6007 if (g_object_get_data(tbl, "text_style_from_prefs")) {
6008 // do not reset the toolbar style from prefs if we already did it last time
6009 sp_style_unref(query);
6010 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6011 return;
6012 }
6013 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
6014 } else {
6015 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
6016 }
6018 if (query->text)
6019 {
6020 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
6021 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6022 gtk_entry_set_text (GTK_ENTRY (entry), "");
6024 } else if (query->text->font_specification.value || query->text->font_family.value) {
6026 Gtk::ComboBoxEntry *combo = (Gtk::ComboBoxEntry *) (g_object_get_data (G_OBJECT (tbl), "family-entry-combo"));
6027 GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6029 // Get the font that corresponds
6030 Glib::ustring familyName;
6032 font_instance * font = font_factory::Default()->FaceFromStyle(query);
6033 if (font) {
6034 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
6035 font->Unref();
6036 font = NULL;
6037 }
6039 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
6041 Gtk::TreeIter iter;
6042 try {
6043 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
6044 Glib::RefPtr<Gtk::TreeModel> model = combo->get_model();
6045 iter = model->get_iter(path);
6046 } catch (...) {
6047 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
6048 sp_style_unref(query);
6049 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6050 return;
6051 }
6053 combo->set_active (iter);
6054 }
6056 //Size
6057 {
6058 GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
6059 gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
6060 gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
6061 g_free(str);
6062 }
6064 //Anchor
6065 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
6066 {
6067 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
6068 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6069 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6070 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6071 }
6072 else
6073 {
6074 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
6075 {
6076 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
6077 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6078 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6079 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6080 }
6081 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
6082 {
6083 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
6084 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6085 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6086 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6087 }
6088 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
6089 {
6090 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
6091 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6092 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6093 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6094 }
6095 }
6097 //Style
6098 {
6099 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
6101 gboolean active = gtk_toggle_button_get_active (button);
6102 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));
6104 if (active != check)
6105 {
6106 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6107 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6108 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6109 }
6110 }
6112 {
6113 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
6115 gboolean active = gtk_toggle_button_get_active (button);
6116 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
6118 if (active != check)
6119 {
6120 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6121 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6122 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6123 }
6124 }
6126 //Orientation
6127 //locking both buttons, changing one affect all group (both)
6128 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
6129 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6131 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
6132 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
6134 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
6135 {
6136 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6137 }
6138 else
6139 {
6140 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
6141 }
6142 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6143 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
6144 }
6146 sp_style_unref(query);
6148 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6149 }
6151 void
6152 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
6153 {
6154 sp_text_toolbox_selection_changed (selection, tbl);
6155 }
6157 void
6158 sp_text_toolbox_subselection_changed (gpointer /*tc*/, GObject *tbl)
6159 {
6160 sp_text_toolbox_selection_changed (NULL, tbl);
6161 }
6163 void
6164 sp_text_toolbox_family_changed (GtkComboBoxEntry *,
6165 GObject *tbl)
6166 {
6167 // quit if run by the _changed callbacks
6168 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6169 return;
6170 }
6172 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6174 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6175 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6176 const gchar* family = gtk_entry_get_text (GTK_ENTRY (entry));
6178 //g_print ("family changed to: %s\n", family);
6180 SPStyle *query =
6181 sp_style_new (SP_ACTIVE_DOCUMENT);
6183 int result_fontspec =
6184 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6186 SPCSSAttr *css = sp_repr_css_attr_new ();
6188 // First try to get the font spec from the stored value
6189 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
6191 if (fontSpec.empty()) {
6192 // Construct a new font specification if it does not yet exist
6193 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6194 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6195 fontFromStyle->Unref();
6196 }
6198 if (!fontSpec.empty()) {
6200 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
6202 if (!newFontSpec.empty()) {
6204 if (fontSpec != newFontSpec) {
6206 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
6208 if (font) {
6209 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6211 // Set all the these just in case they were altered when finding the best
6212 // match for the new family and old style...
6214 gchar c[256];
6216 font->Family(c, 256);
6218 sp_repr_css_set_property (css, "font-family", c);
6220 font->Attribute( "weight", c, 256);
6221 sp_repr_css_set_property (css, "font-weight", c);
6223 font->Attribute("style", c, 256);
6224 sp_repr_css_set_property (css, "font-style", c);
6226 font->Attribute("stretch", c, 256);
6227 sp_repr_css_set_property (css, "font-stretch", c);
6229 font->Attribute("variant", c, 256);
6230 sp_repr_css_set_property (css, "font-variant", c);
6232 font->Unref();
6233 }
6234 }
6236 } else {
6237 // If the old font on selection (or default) was not existing on the system,
6238 // ReplaceFontSpecificationFamily does not work. In that case we fall back to blindly
6239 // setting the family reported by the family chooser.
6241 //g_print ("fallback setting family: %s\n", family);
6242 sp_repr_css_set_property (css, "-inkscape-font-specification", family);
6243 sp_repr_css_set_property (css, "font-family", family);
6244 }
6245 }
6247 // If querying returned nothing, set the default style of the tool (for new texts)
6248 if (result_fontspec == QUERY_STYLE_NOTHING)
6249 {
6250 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6251 prefs->mergeStyle("/tools/text/style", css);
6252 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
6253 }
6254 else
6255 {
6256 sp_desktop_set_style (desktop, css, true, true);
6257 }
6259 sp_style_unref(query);
6261 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6262 _("Text: Change font family"));
6263 sp_repr_css_attr_unref (css);
6265 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6267 // unfreeze
6268 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6270 // focus to canvas
6271 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6272 }
6275 void
6276 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
6277 gpointer data)
6278 {
6279 if (g_object_get_data (G_OBJECT (button), "block")) return;
6280 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
6281 int prop = GPOINTER_TO_INT(data);
6283 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6285 // move the x of all texts to preserve the same bbox
6286 Inkscape::Selection *selection = sp_desktop_selection(desktop);
6287 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
6288 if (SP_IS_TEXT((SPItem *) items->data)) {
6289 SPItem *item = SP_ITEM(items->data);
6291 unsigned writing_mode = SP_OBJECT_STYLE(item)->writing_mode.value;
6292 // below, variable names suggest horizontal move, but we check the writing direction
6293 // and move in the corresponding axis
6294 int axis;
6295 if (writing_mode == SP_CSS_WRITING_MODE_LR_TB || writing_mode == SP_CSS_WRITING_MODE_RL_TB) {
6296 axis = NR::X;
6297 } else {
6298 axis = NR::Y;
6299 }
6301 Geom::OptRect bbox
6302 = item->getBounds(Geom::identity(), SPItem::GEOMETRIC_BBOX);
6303 if (!bbox)
6304 continue;
6305 double width = bbox->dimensions()[axis];
6306 // If you want to align within some frame, other than the text's own bbox, calculate
6307 // the left and right (or top and bottom for tb text) slacks of the text inside that
6308 // frame (currently unused)
6309 double left_slack = 0;
6310 double right_slack = 0;
6311 unsigned old_align = SP_OBJECT_STYLE(item)->text_align.value;
6312 double move = 0;
6313 if (old_align == SP_CSS_TEXT_ALIGN_START || old_align == SP_CSS_TEXT_ALIGN_LEFT) {
6314 switch (prop) {
6315 case 0:
6316 move = -left_slack;
6317 break;
6318 case 1:
6319 move = width/2 + (right_slack - left_slack)/2;
6320 break;
6321 case 2:
6322 move = width + right_slack;
6323 break;
6324 }
6325 } else if (old_align == SP_CSS_TEXT_ALIGN_CENTER) {
6326 switch (prop) {
6327 case 0:
6328 move = -width/2 - left_slack;
6329 break;
6330 case 1:
6331 move = (right_slack - left_slack)/2;
6332 break;
6333 case 2:
6334 move = width/2 + right_slack;
6335 break;
6336 }
6337 } else if (old_align == SP_CSS_TEXT_ALIGN_END || old_align == SP_CSS_TEXT_ALIGN_RIGHT) {
6338 switch (prop) {
6339 case 0:
6340 move = -width - left_slack;
6341 break;
6342 case 1:
6343 move = -width/2 + (right_slack - left_slack)/2;
6344 break;
6345 case 2:
6346 move = right_slack;
6347 break;
6348 }
6349 }
6350 Geom::Point XY = SP_TEXT(item)->attributes.firstXY();
6351 if (axis == NR::X) {
6352 XY = XY + Geom::Point (move, 0);
6353 } else {
6354 XY = XY + Geom::Point (0, move);
6355 }
6356 SP_TEXT(item)->attributes.setFirstXY(XY);
6357 SP_OBJECT(item)->updateRepr();
6358 SP_OBJECT(item)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
6359 }
6360 }
6362 SPCSSAttr *css = sp_repr_css_attr_new ();
6363 switch (prop)
6364 {
6365 case 0:
6366 {
6367 sp_repr_css_set_property (css, "text-anchor", "start");
6368 sp_repr_css_set_property (css, "text-align", "start");
6369 break;
6370 }
6371 case 1:
6372 {
6373 sp_repr_css_set_property (css, "text-anchor", "middle");
6374 sp_repr_css_set_property (css, "text-align", "center");
6375 break;
6376 }
6378 case 2:
6379 {
6380 sp_repr_css_set_property (css, "text-anchor", "end");
6381 sp_repr_css_set_property (css, "text-align", "end");
6382 break;
6383 }
6385 case 3:
6386 {
6387 sp_repr_css_set_property (css, "text-anchor", "start");
6388 sp_repr_css_set_property (css, "text-align", "justify");
6389 break;
6390 }
6391 }
6393 SPStyle *query =
6394 sp_style_new (SP_ACTIVE_DOCUMENT);
6395 int result_numbers =
6396 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6398 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6399 if (result_numbers == QUERY_STYLE_NOTHING)
6400 {
6401 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6402 prefs->mergeStyle("/tools/text/style", css);
6403 }
6405 sp_style_unref(query);
6407 sp_desktop_set_style (desktop, css, true, true);
6408 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6409 _("Text: Change alignment"));
6410 sp_repr_css_attr_unref (css);
6412 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6413 }
6415 void
6416 sp_text_toolbox_style_toggled (GtkToggleButton *button,
6417 gpointer data)
6418 {
6419 if (g_object_get_data (G_OBJECT (button), "block")) return;
6421 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6422 SPCSSAttr *css = sp_repr_css_attr_new ();
6423 int prop = GPOINTER_TO_INT(data);
6424 bool active = gtk_toggle_button_get_active (button);
6426 SPStyle *query =
6427 sp_style_new (SP_ACTIVE_DOCUMENT);
6429 int result_fontspec =
6430 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6432 //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6433 //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6434 //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6436 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
6437 Glib::ustring newFontSpec = "";
6439 if (fontSpec.empty()) {
6440 // Construct a new font specification if it does not yet exist
6441 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6442 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6443 fontFromStyle->Unref();
6444 }
6446 bool nochange = true;
6447 switch (prop)
6448 {
6449 case 0:
6450 {
6451 if (!fontSpec.empty()) {
6452 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
6453 if (!newFontSpec.empty()) {
6454 // Don't even set the bold if the font didn't exist on the system
6455 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
6456 nochange = false;
6457 }
6458 }
6459 // set or reset the button according
6460 if(nochange) {
6461 gboolean check = gtk_toggle_button_get_active (button);
6463 if (active != check)
6464 {
6465 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6466 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6467 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6468 }
6469 }
6471 break;
6472 }
6474 case 1:
6475 {
6476 if (!fontSpec.empty()) {
6477 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
6478 if (!newFontSpec.empty()) {
6479 // Don't even set the italic if the font didn't exist on the system
6480 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
6481 nochange = false;
6482 }
6483 }
6484 if(nochange) {
6485 gboolean check = gtk_toggle_button_get_active (button);
6487 if (active != check)
6488 {
6489 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6490 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6491 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6492 }
6493 }
6494 break;
6495 }
6496 }
6498 if (!newFontSpec.empty()) {
6499 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6500 }
6502 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6503 if (result_fontspec == QUERY_STYLE_NOTHING)
6504 {
6505 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6506 prefs->mergeStyle("/tools/text/style", css);
6507 }
6509 sp_style_unref(query);
6511 sp_desktop_set_style (desktop, css, true, true);
6512 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6513 _("Text: Change font style"));
6514 sp_repr_css_attr_unref (css);
6516 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6517 }
6519 void
6520 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
6521 gpointer data)
6522 {
6523 if (g_object_get_data (G_OBJECT (button), "block")) {
6524 return;
6525 }
6527 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6528 SPCSSAttr *css = sp_repr_css_attr_new ();
6529 int prop = GPOINTER_TO_INT(data);
6531 switch (prop)
6532 {
6533 case 0:
6534 {
6535 sp_repr_css_set_property (css, "writing-mode", "lr");
6536 break;
6537 }
6539 case 1:
6540 {
6541 sp_repr_css_set_property (css, "writing-mode", "tb");
6542 break;
6543 }
6544 }
6546 SPStyle *query =
6547 sp_style_new (SP_ACTIVE_DOCUMENT);
6548 int result_numbers =
6549 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6551 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6552 if (result_numbers == QUERY_STYLE_NOTHING)
6553 {
6554 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6555 prefs->mergeStyle("/tools/text/style", css);
6556 }
6558 sp_desktop_set_style (desktop, css, true, true);
6559 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6560 _("Text: Change orientation"));
6561 sp_repr_css_attr_unref (css);
6563 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6564 }
6566 gboolean
6567 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6568 {
6569 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6570 if (!desktop) return FALSE;
6572 switch (get_group0_keyval (event)) {
6573 case GDK_KP_Enter: // chosen
6574 case GDK_Return:
6575 // unfreeze and update, which will defocus
6576 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6577 sp_text_toolbox_family_changed (NULL, tbl);
6578 return TRUE; // I consumed the event
6579 break;
6580 case GDK_Escape:
6581 // defocus
6582 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6583 return TRUE; // I consumed the event
6584 break;
6585 }
6586 return FALSE;
6587 }
6589 gboolean
6590 sp_text_toolbox_family_list_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject */*tbl*/)
6591 {
6592 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6593 if (!desktop) return FALSE;
6595 switch (get_group0_keyval (event)) {
6596 case GDK_KP_Enter:
6597 case GDK_Return:
6598 case GDK_Escape: // defocus
6599 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6600 return TRUE; // I consumed the event
6601 break;
6602 case GDK_w:
6603 case GDK_W:
6604 if (event->state & GDK_CONTROL_MASK) {
6605 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6606 return TRUE; // I consumed the event
6607 }
6608 break;
6609 }
6610 return FALSE;
6611 }
6614 void
6615 sp_text_toolbox_size_changed (GtkComboBox *cbox,
6616 GObject *tbl)
6617 {
6618 // quit if run by the _changed callbacks
6619 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6620 return;
6621 }
6623 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6625 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6627 // If this is not from selecting a size in the list (in which case get_active will give the
6628 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
6629 // process this event. This fixes GTK's stupid insistence on sending an activate change every
6630 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
6631 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed")) {
6632 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6633 return;
6634 }
6636 gdouble value = -1;
6637 {
6638 gchar *endptr;
6639 gchar *const text = gtk_combo_box_get_active_text(cbox);
6640 if (text) {
6641 value = g_strtod(text, &endptr);
6642 if (endptr == text) { // Conversion failed, non-numeric input.
6643 value = -1;
6644 }
6645 g_free(text);
6646 }
6647 }
6648 if (value <= 0) {
6649 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6650 return; // could not parse value
6651 }
6653 SPCSSAttr *css = sp_repr_css_attr_new ();
6654 Inkscape::CSSOStringStream osfs;
6655 osfs << value;
6656 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
6658 SPStyle *query =
6659 sp_style_new (SP_ACTIVE_DOCUMENT);
6660 int result_numbers =
6661 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6663 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6664 if (result_numbers == QUERY_STYLE_NOTHING)
6665 {
6666 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6667 prefs->mergeStyle("/tools/text/style", css);
6668 }
6670 sp_style_unref(query);
6672 sp_desktop_set_style (desktop, css, true, true);
6673 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
6674 _("Text: Change font size"));
6675 sp_repr_css_attr_unref (css);
6677 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6679 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6680 }
6682 gboolean
6683 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
6684 {
6685 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6686 if (!desktop) return FALSE;
6688 if (!g_object_get_data (tbl, "esc-pressed")) {
6689 g_object_set_data (tbl, "enter-pressed", gpointer(1));
6690 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6691 sp_text_toolbox_size_changed (cbox, tbl);
6692 g_object_set_data (tbl, "enter-pressed", gpointer(0));
6693 }
6694 return FALSE; // I consumed the event
6695 }
6698 gboolean
6699 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6700 {
6701 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6702 if (!desktop) return FALSE;
6704 switch (get_group0_keyval (event)) {
6705 case GDK_Escape: // defocus
6706 g_object_set_data (tbl, "esc-pressed", gpointer(1));
6707 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6708 g_object_set_data (tbl, "esc-pressed", gpointer(0));
6709 return TRUE; // I consumed the event
6710 break;
6711 case GDK_Return: // defocus
6712 case GDK_KP_Enter:
6713 g_object_set_data (tbl, "enter-pressed", gpointer(1));
6714 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6715 sp_text_toolbox_size_changed (cbox, tbl);
6716 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6717 g_object_set_data (tbl, "enter-pressed", gpointer(0));
6718 return TRUE; // I consumed the event
6719 break;
6720 }
6721 return FALSE;
6722 }
6724 // While editing font name in the entry, disable family_changed by freezing, otherwise completion
6725 // does not work!
6726 gboolean
6727 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
6728 GdkEventFocus */*event*/,
6729 GObject *tbl)
6730 {
6731 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6732 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1); // select all
6733 return FALSE;
6734 }
6736 gboolean
6737 sp_text_toolbox_entry_focus_out (GtkWidget *entry,
6738 GdkEventFocus */*event*/,
6739 GObject *tbl)
6740 {
6741 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6742 gtk_entry_select_region (GTK_ENTRY (entry), 0, 0); // deselect
6743 return FALSE;
6744 }
6746 void
6747 cell_data_func (GtkCellLayout */*cell_layout*/,
6748 GtkCellRenderer *cell,
6749 GtkTreeModel *tree_model,
6750 GtkTreeIter *iter,
6751 gpointer /*data*/)
6752 {
6753 gchar *family;
6754 gtk_tree_model_get(tree_model, iter, 0, &family, -1);
6755 gchar *const family_escaped = g_markup_escape_text(family, -1);
6757 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6758 int show_sample = prefs->getInt("/tools/text/show_sample_in_list", 1);
6759 if (show_sample) {
6761 Glib::ustring sample = prefs->getString("/tools/text/font_sample");
6762 gchar *const sample_escaped = g_markup_escape_text(sample.data(), -1);
6764 std::stringstream markup;
6765 markup << family_escaped << " <span foreground='darkgray' font_family='"
6766 << family_escaped << "'>" << sample_escaped << "</span>";
6767 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6769 g_free(sample_escaped);
6770 } else {
6771 g_object_set (G_OBJECT (cell), "markup", family_escaped, NULL);
6772 }
6774 g_free(family);
6775 g_free(family_escaped);
6776 }
6778 gboolean text_toolbox_completion_match_selected(GtkEntryCompletion */*widget*/,
6779 GtkTreeModel *model,
6780 GtkTreeIter *iter,
6781 GObject *tbl)
6782 {
6783 // We intercept this signal so as to fire family_changed at once (without it, you'd have to
6784 // press Enter again after choosing a completion)
6785 gchar *family = 0;
6786 gtk_tree_model_get(model, iter, 0, &family, -1);
6788 GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6789 gtk_entry_set_text (GTK_ENTRY (entry), family);
6791 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6792 sp_text_toolbox_family_changed (NULL, tbl);
6793 return TRUE;
6794 }
6797 static void
6798 cbe_add_completion (GtkComboBoxEntry *cbe, GObject *tbl){
6799 GtkEntry *entry;
6800 GtkEntryCompletion *completion;
6801 GtkTreeModel *model;
6803 entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(cbe)));
6804 completion = gtk_entry_completion_new();
6805 model = gtk_combo_box_get_model(GTK_COMBO_BOX(cbe));
6806 gtk_entry_completion_set_model(completion, model);
6807 gtk_entry_completion_set_text_column(completion, 0);
6808 gtk_entry_completion_set_inline_completion(completion, FALSE);
6809 gtk_entry_completion_set_inline_selection(completion, FALSE);
6810 gtk_entry_completion_set_popup_completion(completion, TRUE);
6811 gtk_entry_set_completion(entry, completion);
6813 g_signal_connect (G_OBJECT (completion), "match-selected", G_CALLBACK (text_toolbox_completion_match_selected), tbl);
6815 g_object_unref(completion);
6816 }
6818 void sp_text_toolbox_family_popnotify(GtkComboBox *widget,
6819 void */*property*/,
6820 GObject *tbl)
6821 {
6822 // while the drop-down is open, we disable font family changing, reenabling it only when it closes
6824 gboolean shown;
6825 g_object_get (G_OBJECT(widget), "popup-shown", &shown, NULL);
6826 if (shown) {
6827 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6828 //g_print("POP: notify: SHOWN\n");
6829 } else {
6830 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6832 // stupid GTK doesn't let us attach to events in the drop-down window, so we peek here to
6833 // find out if the drop down was closed by Enter and if so, manually update (only
6834 // necessary on Windows, on Linux it updates itself - what a mess, but we'll manage)
6835 GdkEvent *ev = gtk_get_current_event();
6836 if (ev) {
6837 //g_print ("ev type: %d\n", ev->type);
6838 if (ev->type == GDK_KEY_PRESS) {
6839 switch (get_group0_keyval ((GdkEventKey *) ev)) {
6840 case GDK_KP_Enter: // chosen
6841 case GDK_Return:
6842 {
6843 // make sure the chosen one is inserted into the entry
6844 GtkComboBox *combo = GTK_COMBO_BOX (((Gtk::ComboBox *) (g_object_get_data (tbl, "family-entry-combo")))->gobj());
6845 GtkTreeModel *model = gtk_combo_box_get_model(combo);
6846 GtkTreeIter iter;
6847 gboolean has_active = gtk_combo_box_get_active_iter (combo, &iter);
6848 if (has_active) {
6849 gchar *family;
6850 gtk_tree_model_get(model, &iter, 0, &family, -1);
6851 GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6852 gtk_entry_set_text (GTK_ENTRY (entry), family);
6853 }
6855 // update
6856 sp_text_toolbox_family_changed (NULL, tbl);
6857 break;
6858 }
6859 }
6860 }
6861 }
6863 // regardless of whether we updated, defocus the widget
6864 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6865 if (desktop)
6866 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6867 //g_print("POP: notify: HIDDEN\n");
6868 }
6869 }
6871 GtkWidget *sp_text_toolbox_new (SPDesktop *desktop)
6872 {
6873 GtkToolbar *tbl = GTK_TOOLBAR(gtk_toolbar_new());
6874 GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("/toolbox/secondary", 1));
6876 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
6877 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
6879 GtkTooltips *tt = gtk_tooltips_new();
6881 ////////////Family
6882 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
6883 Gtk::ComboBoxEntry *font_sel = Gtk::manage(new Gtk::ComboBoxEntry(store));
6885 gtk_rc_parse_string (
6886 "style \"dropdown-as-list-style\"\n"
6887 "{\n"
6888 " GtkComboBox::appears-as-list = 1\n"
6889 "}\n"
6890 "widget \"*.toolbox-fontfamily-list\" style \"dropdown-as-list-style\"");
6891 gtk_widget_set_name(GTK_WIDGET (font_sel->gobj()), "toolbox-fontfamily-list");
6892 gtk_tooltips_set_tip (tt, GTK_WIDGET (font_sel->gobj()), _("Select font family (Alt+X to access)"), "");
6894 g_signal_connect (G_OBJECT (font_sel->gobj()), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
6896 cbe_add_completion(font_sel->gobj(), G_OBJECT(tbl));
6898 gtk_toolbar_append_widget( tbl, (GtkWidget*) font_sel->gobj(), "", "");
6899 g_object_set_data (G_OBJECT (tbl), "family-entry-combo", font_sel);
6901 // expand the field a bit so as to view more of the previews in the drop-down
6902 GtkRequisition req;
6903 gtk_widget_size_request (GTK_WIDGET (font_sel->gobj()), &req);
6904 gtk_widget_set_size_request (GTK_WIDGET (font_sel->gobj()), MIN(req.width + 50, 500), -1);
6906 GtkWidget* entry = (GtkWidget*) font_sel->get_entry()->gobj();
6907 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6909 g_signal_connect (G_OBJECT (font_sel->gobj()), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6910 g_signal_connect (G_OBJECT (font_sel->gobj()), "notify::popup-shown",
6911 G_CALLBACK (sp_text_toolbox_family_popnotify), tbl);
6912 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
6913 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
6914 g_signal_connect (G_OBJECT (entry), "focus-out-event", G_CALLBACK (sp_text_toolbox_entry_focus_out), tbl);
6916 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
6917 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
6919 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
6920 gtk_cell_layout_clear( GTK_CELL_LAYOUT(font_sel->gobj()) );
6921 gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(font_sel->gobj()) , cell , TRUE );
6922 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT(font_sel->gobj()), cell, GtkCellLayoutDataFunc (cell_data_func), NULL, NULL);
6924 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
6925 GtkWidget *box = gtk_event_box_new ();
6926 gtk_container_add (GTK_CONTAINER (box), image);
6927 gtk_toolbar_append_widget( tbl, box, "", "");
6928 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
6929 gtk_tooltips_set_tip (tt, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
6930 gtk_widget_hide (GTK_WIDGET (box));
6931 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
6933 ////////////Size
6934 gchar const *const sizes[] = {
6935 "4", "6", "8", "9", "10", "11", "12", "13", "14",
6936 "16", "18", "20", "22", "24", "28",
6937 "32", "36", "40", "48", "56", "64", "72", "144"
6938 };
6940 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
6941 for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
6942 gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
6943 }
6944 gtk_widget_set_size_request (cbox, 80, -1);
6945 gtk_toolbar_append_widget( tbl, cbox, "", "");
6946 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
6947 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
6948 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
6949 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
6951 ////////////Text anchor
6952 GtkWidget *group = gtk_radio_button_new (NULL);
6953 GtkWidget *row = gtk_hbox_new (FALSE, 4);
6954 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
6956 // left
6957 GtkWidget *rbutton = group;
6958 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6959 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
6960 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6962 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6963 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
6964 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
6965 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
6967 // center
6968 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6969 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6970 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
6971 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6973 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6974 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
6975 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
6976 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
6978 // right
6979 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6980 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6981 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
6982 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6984 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6985 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
6986 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
6987 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
6989 // fill
6990 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6991 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6992 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
6993 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6995 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6996 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
6997 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
6998 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
7000 gtk_toolbar_append_widget( tbl, row, "", "");
7002 //spacer
7003 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
7005 ////////////Text style
7006 row = gtk_hbox_new (FALSE, 4);
7008 // bold
7009 rbutton = gtk_toggle_button_new ();
7010 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7011 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
7012 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7013 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
7015 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7016 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
7017 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
7019 // italic
7020 rbutton = gtk_toggle_button_new ();
7021 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7022 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
7023 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7024 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
7026 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7027 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
7028 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
7030 gtk_toolbar_append_widget( tbl, row, "", "");
7032 //spacer
7033 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
7035 // Text orientation
7036 group = gtk_radio_button_new (NULL);
7037 row = gtk_hbox_new (FALSE, 4);
7038 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
7040 // horizontal
7041 rbutton = group;
7042 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7043 gtk_container_add (GTK_CONTAINER (rbutton),
7044 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_HORIZONTAL));
7045 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7046 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
7048 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7049 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
7050 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
7052 // vertical
7053 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7054 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7055 gtk_container_add (GTK_CONTAINER (rbutton),
7056 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_VERTICAL));
7057 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7058 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
7060 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
7061 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
7062 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
7063 gtk_toolbar_append_widget( tbl, row, "", "" );
7066 //watch selection
7067 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
7069 sigc::connection *c_selection_changed =
7070 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
7071 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
7072 pool->add_connection ("selection-changed", c_selection_changed);
7074 sigc::connection *c_selection_modified =
7075 new sigc::connection (sp_desktop_selection (desktop)->connectModified
7076 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
7077 pool->add_connection ("selection-modified", c_selection_modified);
7079 sigc::connection *c_subselection_changed =
7080 new sigc::connection (desktop->connectToolSubselectionChanged
7081 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
7082 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
7084 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
7087 gtk_widget_show_all( GTK_WIDGET(tbl) );
7089 return GTK_WIDGET(tbl);
7090 } // end of sp_text_toolbox_new()
7092 }//<unnamed> namespace
7095 //#########################
7096 //## Connector ##
7097 //#########################
7099 static void sp_connector_mode_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7100 {
7101 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7102 prefs->setBool("/tools/connector/mode",
7103 gtk_toggle_action_get_active( act ));
7104 }
7106 static void sp_connector_path_set_avoid(void)
7107 {
7108 cc_selection_set_avoid(true);
7109 }
7112 static void sp_connector_path_set_ignore(void)
7113 {
7114 cc_selection_set_avoid(false);
7115 }
7117 static void sp_connector_orthogonal_toggled( GtkToggleAction* act, GObject *tbl )
7118 {
7119 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7120 Inkscape::Selection * selection = sp_desktop_selection(desktop);
7121 SPDocument *doc = sp_desktop_document(desktop);
7123 if (!sp_document_get_undo_sensitive(doc))
7124 {
7125 return;
7126 }
7129 // quit if run by the _changed callbacks
7130 if (g_object_get_data( tbl, "freeze" )) {
7131 return;
7132 }
7134 // in turn, prevent callbacks from responding
7135 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7137 bool is_orthog = gtk_toggle_action_get_active( act );
7138 gchar orthog_str[] = "orthogonal";
7139 gchar polyline_str[] = "polyline";
7140 gchar *value = is_orthog ? orthog_str : polyline_str ;
7142 bool modmade = false;
7143 GSList *l = (GSList *) selection->itemList();
7144 while (l) {
7145 SPItem *item = (SPItem *) l->data;
7147 if (cc_item_is_connector(item)) {
7148 sp_object_setAttribute(item, "inkscape:connector-type",
7149 value, false);
7150 item->avoidRef->handleSettingChange();
7151 modmade = true;
7152 }
7153 l = l->next;
7154 }
7156 if (!modmade)
7157 {
7158 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7159 prefs->setBool("/tools/connector/orthogonal", is_orthog);
7160 }
7162 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7163 is_orthog ? _("Set connector type: orthogonal"): _("Set connector type: polyline"));
7165 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7166 }
7168 static void connector_curvature_changed(GtkAdjustment *adj, GObject* tbl)
7169 {
7170 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7171 Inkscape::Selection * selection = sp_desktop_selection(desktop);
7172 SPDocument *doc = sp_desktop_document(desktop);
7174 if (!sp_document_get_undo_sensitive(doc))
7175 {
7176 return;
7177 }
7180 // quit if run by the _changed callbacks
7181 if (g_object_get_data( tbl, "freeze" )) {
7182 return;
7183 }
7185 // in turn, prevent callbacks from responding
7186 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7188 gdouble newValue = gtk_adjustment_get_value(adj);
7189 gchar value[G_ASCII_DTOSTR_BUF_SIZE];
7190 g_ascii_dtostr(value, G_ASCII_DTOSTR_BUF_SIZE, newValue);
7192 bool modmade = false;
7193 GSList *l = (GSList *) selection->itemList();
7194 while (l) {
7195 SPItem *item = (SPItem *) l->data;
7197 if (cc_item_is_connector(item)) {
7198 sp_object_setAttribute(item, "inkscape:connector-curvature",
7199 value, false);
7200 item->avoidRef->handleSettingChange();
7201 modmade = true;
7202 }
7203 l = l->next;
7204 }
7206 if (!modmade)
7207 {
7208 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7209 prefs->setDouble(Glib::ustring("/tools/connector/curvature"), newValue);
7210 }
7212 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7213 _("Change connector curvature"));
7215 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7216 }
7219 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
7220 {
7221 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7222 SPDocument *doc = sp_desktop_document(desktop);
7224 if (!sp_document_get_undo_sensitive(doc))
7225 {
7226 return;
7227 }
7229 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7231 if ( !repr->attribute("inkscape:connector-spacing") &&
7232 ( adj->value == defaultConnSpacing )) {
7233 // Don't need to update the repr if the attribute doesn't
7234 // exist and it is being set to the default value -- as will
7235 // happen at startup.
7236 return;
7237 }
7239 // quit if run by the attr_changed listener
7240 if (g_object_get_data( tbl, "freeze" )) {
7241 return;
7242 }
7244 // in turn, prevent listener from responding
7245 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
7247 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
7248 SP_OBJECT(desktop->namedview)->updateRepr();
7250 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
7251 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
7252 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
7253 Geom::Matrix m = Geom::identity();
7254 avoid_item_move(&m, item);
7255 }
7257 if (items) {
7258 g_slist_free(items);
7259 }
7261 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7262 _("Change connector spacing"));
7264 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7265 }
7267 static void sp_connector_graph_layout(void)
7268 {
7269 if (!SP_ACTIVE_DESKTOP) return;
7270 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7272 // hack for clones, see comment in align-and-distribute.cpp
7273 int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7274 prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7276 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
7278 prefs->setInt("/options/clonecompensation/value", saved_compensation);
7280 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
7281 }
7283 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7284 {
7285 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7286 prefs->setBool("/tools/connector/directedlayout",
7287 gtk_toggle_action_get_active( act ));
7288 }
7290 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7291 {
7292 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7293 prefs->setBool("/tools/connector/avoidoverlaplayout",
7294 gtk_toggle_action_get_active( act ));
7295 }
7298 static void connector_length_changed(GtkAdjustment *adj, GObject* /*tbl*/)
7299 {
7300 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7301 prefs->setDouble("/tools/connector/length", adj->value);
7302 }
7304 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
7305 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
7306 bool /*is_interactive*/, gpointer data)
7307 {
7308 GtkWidget *tbl = GTK_WIDGET(data);
7310 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
7311 return;
7312 }
7313 if (strcmp(name, "inkscape:connector-spacing") == 0)
7314 {
7315 GtkAdjustment *adj = (GtkAdjustment*)
7316 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
7317 gdouble spacing = defaultConnSpacing;
7318 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
7320 gtk_adjustment_set_value(adj, spacing);
7321 gtk_adjustment_value_changed(adj);
7322 }
7324 spinbutton_defocus(GTK_OBJECT(tbl));
7325 }
7327 static void sp_connector_new_connection_point(GtkWidget *, GObject *tbl)
7328 {
7329 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7330 SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7332 if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE)
7333 cc_create_connection_point(cc);
7334 }
7336 static void sp_connector_remove_connection_point(GtkWidget *, GObject *tbl)
7337 {
7338 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7339 SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7341 if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE)
7342 cc_remove_connection_point(cc);
7343 }
7345 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
7346 NULL, /* child_added */
7347 NULL, /* child_removed */
7348 connector_tb_event_attr_changed,
7349 NULL, /* content_changed */
7350 NULL /* order_changed */
7351 };
7353 static void sp_connector_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
7354 {
7355 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "curvature" ) );
7356 GtkToggleAction *act = GTK_TOGGLE_ACTION( g_object_get_data( tbl, "orthogonal" ) );
7357 SPItem *item = selection->singleItem();
7358 if (SP_IS_PATH(item))
7359 {
7360 gdouble curvature = SP_PATH(item)->connEndPair.getCurvature();
7361 bool is_orthog = SP_PATH(item)->connEndPair.isOrthogonal();
7362 gtk_toggle_action_set_active(act, is_orthog);
7363 gtk_adjustment_set_value(adj, curvature);
7364 }
7366 }
7368 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
7369 {
7370 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7371 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
7373 // Editing mode toggle button
7374 {
7375 InkToggleAction* act = ink_toggle_action_new( "ConnectorEditModeAction",
7376 _("EditMode"),
7377 _("Switch between connection point editing and connector drawing mode"),
7378 INKSCAPE_ICON_CONNECTOR_EDIT,
7379 Inkscape::ICON_SIZE_DECORATION );
7380 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7382 bool tbuttonstate = prefs->getBool("/tools/connector/mode");
7383 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7384 g_object_set_data( holder, "mode", act );
7385 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_mode_toggled), holder );
7386 }
7389 {
7390 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
7391 _("Avoid"),
7392 _("Make connectors avoid selected objects"),
7393 INKSCAPE_ICON_CONNECTOR_AVOID,
7394 secondarySize );
7395 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
7396 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7397 }
7399 {
7400 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
7401 _("Ignore"),
7402 _("Make connectors ignore selected objects"),
7403 INKSCAPE_ICON_CONNECTOR_IGNORE,
7404 secondarySize );
7405 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
7406 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7407 }
7409 // Orthogonal connectors toggle button
7410 {
7411 InkToggleAction* act = ink_toggle_action_new( "ConnectorOrthogonalAction",
7412 _("Orthogonal"),
7413 _("Make connector orthogonal or polyline"),
7414 INKSCAPE_ICON_CONNECTOR_ORTHOGONAL,
7415 Inkscape::ICON_SIZE_DECORATION );
7416 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7418 bool tbuttonstate = prefs->getBool("/tools/connector/orthogonal");
7419 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7420 g_object_set_data( holder, "orthogonal", act );
7421 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_orthogonal_toggled), holder );
7422 }
7424 EgeAdjustmentAction* eact = 0;
7425 // Curvature spinbox
7426 eact = create_adjustment_action( "ConnectorCurvatureAction",
7427 _("Connector Curvature"), _("Curvature:"),
7428 _("The amount of connectors curvature"),
7429 "/tools/connector/curvature", defaultConnCurvature,
7430 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-curvature",
7431 0, 100, 1.0, 10.0,
7432 0, 0, 0,
7433 connector_curvature_changed, 1, 0 );
7434 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7436 // Spacing spinbox
7437 eact = create_adjustment_action( "ConnectorSpacingAction",
7438 _("Connector Spacing"), _("Spacing:"),
7439 _("The amount of space left around objects by auto-routing connectors"),
7440 "/tools/connector/spacing", defaultConnSpacing,
7441 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
7442 0, 100, 1.0, 10.0,
7443 0, 0, 0,
7444 connector_spacing_changed, 1, 0 );
7445 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7447 // Graph (connector network) layout
7448 {
7449 InkAction* inky = ink_action_new( "ConnectorGraphAction",
7450 _("Graph"),
7451 _("Nicely arrange selected connector network"),
7452 INKSCAPE_ICON_DISTRIBUTE_GRAPH,
7453 secondarySize );
7454 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
7455 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7456 }
7458 // Default connector length spinbox
7459 eact = create_adjustment_action( "ConnectorLengthAction",
7460 _("Connector Length"), _("Length:"),
7461 _("Ideal length for connectors when layout is applied"),
7462 "/tools/connector/length", 100,
7463 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
7464 10, 1000, 10.0, 100.0,
7465 0, 0, 0,
7466 connector_length_changed, 1, 0 );
7467 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7470 // Directed edges toggle button
7471 {
7472 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
7473 _("Downwards"),
7474 _("Make connectors with end-markers (arrows) point downwards"),
7475 INKSCAPE_ICON_DISTRIBUTE_GRAPH_DIRECTED,
7476 Inkscape::ICON_SIZE_DECORATION );
7477 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7479 bool tbuttonstate = prefs->getBool("/tools/connector/directedlayout");
7480 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7482 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
7483 sigc::connection *connection = new sigc::connection(sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_connector_toolbox_selection_changed), (GObject *)holder))
7484 );
7485 }
7487 // Avoid overlaps toggle button
7488 {
7489 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
7490 _("Remove overlaps"),
7491 _("Do not allow overlapping shapes"),
7492 INKSCAPE_ICON_DISTRIBUTE_REMOVE_OVERLAPS,
7493 Inkscape::ICON_SIZE_DECORATION );
7494 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7496 bool tbuttonstate = prefs->getBool("/tools/connector/avoidoverlaplayout");
7497 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), (tbuttonstate ? TRUE : FALSE ));
7499 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
7500 }
7503 // New connection point button
7504 {
7505 InkAction* inky = ink_action_new( "ConnectorNewConnPointAction",
7506 _("New connection point"),
7507 _("Add a new connection point to the currently selected item"),
7508 INKSCAPE_ICON_CONNECTOR_NEW_CONNPOINT,
7509 secondarySize );
7510 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_new_connection_point), holder );
7511 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7512 }
7514 // Remove selected connection point button
7516 {
7517 InkAction* inky = ink_action_new( "ConnectorRemoveConnPointAction",
7518 _("Remove connection point"),
7519 _("Remove the currently selected connection point"),
7520 INKSCAPE_ICON_CONNECTOR_REMOVE_CONNPOINT,
7521 secondarySize );
7522 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_remove_connection_point), holder );
7523 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7524 }
7527 // Code to watch for changes to the connector-spacing attribute in
7528 // the XML.
7529 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7530 g_assert(repr != NULL);
7532 purge_repr_listener( holder, holder );
7534 if (repr) {
7535 g_object_set_data( holder, "repr", repr );
7536 Inkscape::GC::anchor(repr);
7537 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
7538 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
7539 }
7540 } // end of sp_connector_toolbox_prep()
7543 //#########################
7544 //## Paintbucket ##
7545 //#########################
7547 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
7548 {
7549 gint channels = ege_select_one_action_get_active( act );
7550 flood_channels_set_channels( channels );
7551 }
7553 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
7554 {
7555 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7556 prefs->setInt("/tools/paintbucket/threshold", (gint)adj->value);
7557 }
7559 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
7560 {
7561 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7562 prefs->setBool("/tools/paintbucket/autogap", ege_select_one_action_get_active( act ));
7563 }
7565 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
7566 {
7567 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
7568 SPUnit const *unit = tracker->getActiveUnit();
7569 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7571 prefs->setDouble("/tools/paintbucket/offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
7572 prefs->setString("/tools/paintbucket/offsetunits", sp_unit_get_abbreviation(unit));
7573 }
7575 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
7576 {
7577 // FIXME: make defaults settable via Inkscape Options
7578 struct KeyValue {
7579 char const *key;
7580 double value;
7581 } const key_values[] = {
7582 {"threshold", 15},
7583 {"offset", 0.0}
7584 };
7586 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
7587 KeyValue const &kv = key_values[i];
7588 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
7589 if ( adj ) {
7590 gtk_adjustment_set_value(adj, kv.value);
7591 }
7592 }
7594 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
7595 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
7596 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
7597 ege_select_one_action_set_active( autogap_action, 0 );
7598 }
7600 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
7601 {
7602 EgeAdjustmentAction* eact = 0;
7603 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7605 {
7606 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7608 GList* items = 0;
7609 gint count = 0;
7610 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
7611 {
7612 GtkTreeIter iter;
7613 gtk_list_store_append( model, &iter );
7614 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7615 count++;
7616 }
7617 g_list_free( items );
7618 items = 0;
7619 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
7620 g_object_set( act1, "short_label", _("Fill by:"), NULL );
7621 ege_select_one_action_set_appearance( act1, "compact" );
7622 ege_select_one_action_set_active( act1, prefs->getInt("/tools/paintbucket/channels", 0) );
7623 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
7624 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
7625 g_object_set_data( holder, "channels_action", act1 );
7626 }
7628 // Spacing spinbox
7629 {
7630 eact = create_adjustment_action(
7631 "ThresholdAction",
7632 _("Fill Threshold"), _("Threshold:"),
7633 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
7634 "/tools/paintbucket/threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
7635 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
7636 0, 0, 0,
7637 paintbucket_threshold_changed, 1, 0 );
7639 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
7640 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7641 }
7643 // Create the units menu.
7644 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
7645 Glib::ustring stored_unit = prefs->getString("/tools/paintbucket/offsetunits");
7646 if (!stored_unit.empty())
7647 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit.data()));
7648 g_object_set_data( holder, "tracker", tracker );
7649 {
7650 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
7651 gtk_action_group_add_action( mainActions, act );
7652 }
7654 // Offset spinbox
7655 {
7656 eact = create_adjustment_action(
7657 "OffsetAction",
7658 _("Grow/shrink by"), _("Grow/shrink by:"),
7659 _("The amount to grow (positive) or shrink (negative) the created fill path"),
7660 "/tools/paintbucket/offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
7661 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
7662 0, 0, 0,
7663 paintbucket_offset_changed, 1, 2);
7664 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
7666 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7667 }
7669 /* Auto Gap */
7670 {
7671 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7673 GList* items = 0;
7674 gint count = 0;
7675 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
7676 {
7677 GtkTreeIter iter;
7678 gtk_list_store_append( model, &iter );
7679 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7680 count++;
7681 }
7682 g_list_free( items );
7683 items = 0;
7684 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
7685 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
7686 ege_select_one_action_set_appearance( act2, "compact" );
7687 ege_select_one_action_set_active( act2, prefs->getBool("/tools/paintbucket/autogap") );
7688 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
7689 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
7690 g_object_set_data( holder, "autogap_action", act2 );
7691 }
7693 /* Reset */
7694 {
7695 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
7696 _("Defaults"),
7697 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
7698 GTK_STOCK_CLEAR );
7699 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
7700 gtk_action_group_add_action( mainActions, act );
7701 gtk_action_set_sensitive( act, TRUE );
7702 }
7704 }
7706 /*
7707 Local Variables:
7708 mode:c++
7709 c-file-style:"stroustrup"
7710 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
7711 indent-tabs-mode:nil
7712 fill-column:99
7713 End:
7714 */
7715 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :