929af817a0a7c1b274b1975a10892e4cb8d8ba1e
1 /** \file
2 * Controls bars for some of Inkscape's tools
3 * (for some tools, they are in their own files)
4 */
6 /*
7 *
8 * Authors:
9 * MenTaLguY <mental@rydia.net>
10 * Lauris Kaplinski <lauris@kaplinski.com>
11 * bulia byak <buliabyak@users.sf.net>
12 * Frank Felfe <innerspace@iname.com>
13 * John Cliff <simarilius@yahoo.com>
14 * David Turner <novalis@gnu.org>
15 * Josh Andler <scislac@scislac.com>
16 * Jon A. Cruz <jon@joncruz.org>
17 * Maximilian Albert <maximilian.albert@gmail.com>
18 *
19 * Copyright (C) 2004 David Turner
20 * Copyright (C) 2003 MenTaLguY
21 * Copyright (C) 1999-2008 authors
22 * Copyright (C) 2001-2002 Ximian, Inc.
23 *
24 * Released under GNU GPL, read the file 'COPYING' for more information
25 */
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
31 #include <cstring>
32 #include <string>
34 #include <gtkmm.h>
35 #include <gtk/gtk.h>
36 #include <iostream>
37 #include <sstream>
39 #include "widgets/button.h"
40 #include "widgets/widget-sizes.h"
41 #include "widgets/spw-utilities.h"
42 #include "widgets/spinbutton-events.h"
43 #include "dialogs/text-edit.h"
44 #include "dialogs/dialog-events.h"
46 #include "ui/widget/style-swatch.h"
48 #include "verbs.h"
49 #include "sp-namedview.h"
50 #include "desktop.h"
51 #include "desktop-handles.h"
52 #include "xml/repr.h"
53 #include "xml/node-event-vector.h"
54 #include "xml/attribute-record.h"
55 #include <glibmm/i18n.h>
56 #include "helper/unit-menu.h"
57 #include "helper/units.h"
58 #include "live_effects/effect.h"
60 #include "inkscape.h"
61 #include "conn-avoid-ref.h"
64 #include "select-toolbar.h"
65 #include "gradient-toolbar.h"
67 #include "connector-context.h"
68 #include "node-context.h"
69 #include "pen-context.h"
70 #include "lpe-tool-context.h"
71 #include "live_effects/lpe-line_segment.h"
72 #include "shape-editor.h"
73 #include "tweak-context.h"
74 #include "sp-rect.h"
75 #include "box3d.h"
76 #include "box3d-context.h"
77 #include "sp-star.h"
78 #include "sp-spiral.h"
79 #include "sp-ellipse.h"
80 #include "sp-text.h"
81 #include "sp-flowtext.h"
82 #include "sp-clippath.h"
83 #include "sp-mask.h"
84 #include "style.h"
85 #include "tools-switch.h"
86 #include "selection.h"
87 #include "selection-chemistry.h"
88 #include "document-private.h"
89 #include "desktop-style.h"
90 #include "../libnrtype/font-lister.h"
91 #include "../libnrtype/font-instance.h"
92 #include "../connection-pool.h"
93 #include "../preferences.h"
94 #include "../inkscape-stock.h"
95 #include "icon.h"
96 #include "graphlayout/graphlayout.h"
97 #include "interface.h"
98 #include "shortcuts.h"
100 #include "mod360.h"
102 #include "toolbox.h"
104 #include "flood-context.h"
106 #include "ink-action.h"
107 #include "ege-adjustment-action.h"
108 #include "ege-output-action.h"
109 #include "ege-select-one-action.h"
110 #include "helper/unit-tracker.h"
111 #include "live_effects/lpe-angle_bisector.h"
113 #include "svg/css-ostringstream.h"
115 #include "widgets/calligraphic-profile-rename.h"
117 using Inkscape::UnitTracker;
119 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
120 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
122 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
123 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 static void sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
126 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
127 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
128 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
129 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
130 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
131 static void sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
132 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
133 static void sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
134 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
135 static void sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
136 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
137 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
138 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
140 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
143 Inkscape::IconSize prefToSize( gchar const *path, gchar const *attr, int base ) {
144 static Inkscape::IconSize sizeChoices[] = {
145 Inkscape::ICON_SIZE_LARGE_TOOLBAR,
146 Inkscape::ICON_SIZE_SMALL_TOOLBAR,
147 Inkscape::ICON_SIZE_MENU
148 };
149 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
150 int index = prefs->getIntLimited( path, attr, base, 0, G_N_ELEMENTS(sizeChoices) );
151 return sizeChoices[index];
152 }
154 static struct {
155 gchar const *type_name;
156 gchar const *data_name;
157 sp_verb_t verb;
158 sp_verb_t doubleclick_verb;
159 } const tools[] = {
160 { "SPSelectContext", "select_tool", SP_VERB_CONTEXT_SELECT, SP_VERB_CONTEXT_SELECT_PREFS},
161 { "SPNodeContext", "node_tool", SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
162 { "SPTweakContext", "tweak_tool", SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
163 { "SPZoomContext", "zoom_tool", SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
164 { "SPRectContext", "rect_tool", SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
165 { "Box3DContext", "3dbox_tool", SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
166 { "SPArcContext", "arc_tool", SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
167 { "SPStarContext", "star_tool", SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
168 { "SPSpiralContext", "spiral_tool", SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
169 { "SPPencilContext", "pencil_tool", SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
170 { "SPPenContext", "pen_tool", SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
171 { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
172 { "SPLPEToolContext", "lpetool_tool", SP_VERB_CONTEXT_LPETOOL, SP_VERB_CONTEXT_LPETOOL_PREFS },
173 { "SPEraserContext", "eraser_tool", SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
174 { "SPFloodContext", "paintbucket_tool", SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
175 { "SPTextContext", "text_tool", SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
176 { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
177 { "SPGradientContext", "gradient_tool", SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
178 { "SPDropperContext", "dropper_tool", SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
179 { NULL, NULL, 0, 0 }
180 };
182 static struct {
183 gchar const *type_name;
184 gchar const *data_name;
185 GtkWidget *(*create_func)(SPDesktop *desktop);
186 void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
187 gchar const *ui_name;
188 gint swatch_verb_id;
189 gchar const *swatch_tool;
190 gchar const *swatch_tip;
191 } const aux_toolboxes[] = {
192 { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep, "SelectToolbar",
193 SP_VERB_INVALID, 0, 0},
194 { "SPNodeContext", "node_toolbox", 0, sp_node_toolbox_prep, "NodeToolbar",
195 SP_VERB_INVALID, 0, 0},
196 { "SPTweakContext", "tweak_toolbox", 0, sp_tweak_toolbox_prep, "TweakToolbar",
197 SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", N_("Color/opacity used for color tweaking")},
198 { "SPZoomContext", "zoom_toolbox", 0, sp_zoom_toolbox_prep, "ZoomToolbar",
199 SP_VERB_INVALID, 0, 0},
200 { "SPStarContext", "star_toolbox", 0, sp_star_toolbox_prep, "StarToolbar",
201 SP_VERB_CONTEXT_STAR_PREFS, "tools.shapes.star", N_("Style of new stars")},
202 { "SPRectContext", "rect_toolbox", 0, sp_rect_toolbox_prep, "RectToolbar",
203 SP_VERB_CONTEXT_RECT_PREFS, "tools.shapes.rect", N_("Style of new rectangles")},
204 { "Box3DContext", "3dbox_toolbox", 0, box3d_toolbox_prep, "3DBoxToolbar",
205 SP_VERB_CONTEXT_3DBOX_PREFS, "tools.shapes.3dbox", N_("Style of new 3D boxes")},
206 { "SPArcContext", "arc_toolbox", 0, sp_arc_toolbox_prep, "ArcToolbar",
207 SP_VERB_CONTEXT_ARC_PREFS, "tools.shapes.arc", N_("Style of new ellipses")},
208 { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep, "SpiralToolbar",
209 SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral", N_("Style of new spirals")},
210 { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep, "PencilToolbar",
211 SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", N_("Style of new paths created by Pencil")},
212 { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep, "PenToolbar",
213 SP_VERB_CONTEXT_PEN_PREFS, "tools.freehand.pen", N_("Style of new paths created by Pen")},
214 { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
215 SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", N_("Style of new calligraphic strokes")},
216 { "SPEraserContext", "eraser_toolbox", 0, sp_eraser_toolbox_prep,"EraserToolbar",
217 SP_VERB_CONTEXT_ERASER_PREFS, "tools.eraser", _("TBD")},
218 { "SPLPEToolContext", "lpetool_toolbox", 0, sp_lpetool_toolbox_prep, "LPEToolToolbar",
219 SP_VERB_CONTEXT_LPETOOL_PREFS, "tools.lpetool", _("TBD")},
220 { "SPTextContext", "text_toolbox", sp_text_toolbox_new, 0, 0,
221 SP_VERB_INVALID, 0, 0},
222 { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep, "DropperToolbar",
223 SP_VERB_INVALID, 0, 0},
224 { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0, 0,
225 SP_VERB_INVALID, 0, 0},
226 { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep, "ConnectorToolbar",
227 SP_VERB_INVALID, 0, 0},
228 { "SPFloodContext", "paintbucket_toolbox", 0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
229 SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", N_("Style of Paint Bucket fill objects")},
230 { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
231 };
233 #define TOOLBAR_SLIDER_HINT "full"
235 static gchar const * ui_descr =
236 "<ui>"
237 " <toolbar name='SelectToolbar'>"
238 " <toolitem action='EditSelectAll' />"
239 " <toolitem action='EditSelectAllInAllLayers' />"
240 " <toolitem action='EditDeselect' />"
241 " <separator />"
242 " <toolitem action='ObjectRotate90CCW' />"
243 " <toolitem action='ObjectRotate90' />"
244 " <toolitem action='ObjectFlipHorizontally' />"
245 " <toolitem action='ObjectFlipVertically' />"
246 " <separator />"
247 " <toolitem action='SelectionToBack' />"
248 " <toolitem action='SelectionLower' />"
249 " <toolitem action='SelectionRaise' />"
250 " <toolitem action='SelectionToFront' />"
251 " <separator />"
252 " <toolitem action='XAction' />"
253 " <toolitem action='YAction' />"
254 " <toolitem action='WidthAction' />"
255 " <toolitem action='LockAction' />"
256 " <toolitem action='HeightAction' />"
257 " <toolitem action='UnitsAction' />"
258 " <separator />"
259 " <toolitem action='transform_affect_label' />"
260 " <toolitem action='transform_stroke' />"
261 " <toolitem action='transform_corners' />"
262 " <toolitem action='transform_gradient' />"
263 " <toolitem action='transform_pattern' />"
264 " </toolbar>"
266 " <toolbar name='NodeToolbar'>"
267 " <toolitem action='NodeInsertAction' />"
268 " <toolitem action='NodeDeleteAction' />"
269 " <separator />"
270 " <toolitem action='NodeJoinAction' />"
271 " <toolitem action='NodeBreakAction' />"
272 " <separator />"
273 " <toolitem action='NodeJoinSegmentAction' />"
274 " <toolitem action='NodeDeleteSegmentAction' />"
275 " <separator />"
276 " <toolitem action='NodeCuspAction' />"
277 " <toolitem action='NodeSmoothAction' />"
278 " <toolitem action='NodeSymmetricAction' />"
279 " <separator />"
280 " <toolitem action='NodeLineAction' />"
281 " <toolitem action='NodeCurveAction' />"
282 " <separator />"
283 " <toolitem action='ObjectToPath' />"
284 " <toolitem action='StrokeToPath' />"
285 " <separator />"
286 " <toolitem action='NodeXAction' />"
287 " <toolitem action='NodeYAction' />"
288 " <toolitem action='NodeUnitsAction' />"
289 " <separator />"
290 " <toolitem action='ObjectEditClipPathAction' />"
291 " <toolitem action='ObjectEditMaskPathAction' />"
292 " <toolitem action='EditNextLPEParameterAction' />"
293 " <separator />"
294 " <toolitem action='NodesShowHandlesAction' />"
295 " <toolitem action='NodesShowHelperpath' />"
296 " </toolbar>"
298 " <toolbar name='TweakToolbar'>"
299 " <toolitem action='TweakWidthAction' />"
300 " <separator />"
301 " <toolitem action='TweakForceAction' />"
302 " <toolitem action='TweakPressureAction' />"
303 " <separator />"
304 " <toolitem action='TweakModeAction' />"
305 " <separator />"
306 " <toolitem action='TweakFidelityAction' />"
307 " <separator />"
308 " <toolitem action='TweakChannelsLabel' />"
309 " <toolitem action='TweakDoH' />"
310 " <toolitem action='TweakDoS' />"
311 " <toolitem action='TweakDoL' />"
312 " <toolitem action='TweakDoO' />"
313 " </toolbar>"
315 " <toolbar name='ZoomToolbar'>"
316 " <toolitem action='ZoomIn' />"
317 " <toolitem action='ZoomOut' />"
318 " <separator />"
319 " <toolitem action='Zoom1:0' />"
320 " <toolitem action='Zoom1:2' />"
321 " <toolitem action='Zoom2:1' />"
322 " <separator />"
323 " <toolitem action='ZoomSelection' />"
324 " <toolitem action='ZoomDrawing' />"
325 " <toolitem action='ZoomPage' />"
326 " <toolitem action='ZoomPageWidth' />"
327 " <separator />"
328 " <toolitem action='ZoomPrev' />"
329 " <toolitem action='ZoomNext' />"
330 " </toolbar>"
332 " <toolbar name='StarToolbar'>"
333 " <separator />"
334 " <toolitem action='StarStateAction' />"
335 " <separator />"
336 " <toolitem action='FlatAction' />"
337 " <separator />"
338 " <toolitem action='MagnitudeAction' />"
339 " <toolitem action='SpokeAction' />"
340 " <toolitem action='RoundednessAction' />"
341 " <toolitem action='RandomizationAction' />"
342 " <separator />"
343 " <toolitem action='StarResetAction' />"
344 " </toolbar>"
346 " <toolbar name='RectToolbar'>"
347 " <toolitem action='RectStateAction' />"
348 " <toolitem action='RectWidthAction' />"
349 " <toolitem action='RectHeightAction' />"
350 " <toolitem action='RadiusXAction' />"
351 " <toolitem action='RadiusYAction' />"
352 " <toolitem action='RectUnitsAction' />"
353 " <separator />"
354 " <toolitem action='RectResetAction' />"
355 " </toolbar>"
357 " <toolbar name='3DBoxToolbar'>"
358 " <toolitem action='3DBoxAngleXAction' />"
359 " <toolitem action='3DBoxVPXStateAction' />"
360 " <separator />"
361 " <toolitem action='3DBoxAngleYAction' />"
362 " <toolitem action='3DBoxVPYStateAction' />"
363 " <separator />"
364 " <toolitem action='3DBoxAngleZAction' />"
365 " <toolitem action='3DBoxVPZStateAction' />"
366 " </toolbar>"
368 " <toolbar name='SpiralToolbar'>"
369 " <toolitem action='SpiralStateAction' />"
370 " <toolitem action='SpiralRevolutionAction' />"
371 " <toolitem action='SpiralExpansionAction' />"
372 " <toolitem action='SpiralT0Action' />"
373 " <separator />"
374 " <toolitem action='SpiralResetAction' />"
375 " </toolbar>"
377 " <toolbar name='PenToolbar'>"
378 " <toolitem action='FreehandModeActionPen' />"
379 " <separator />"
380 " <toolitem action='SetPenShapeAction'/>"
381 " </toolbar>"
383 " <toolbar name='PencilToolbar'>"
384 " <toolitem action='FreehandModeActionPencil' />"
385 " <separator />"
386 " <toolitem action='PencilToleranceAction' />"
387 " <separator />"
388 " <toolitem action='PencilResetAction' />"
389 " <separator />"
390 " <toolitem action='SetPencilShapeAction'/>"
391 " </toolbar>"
393 " <toolbar name='CalligraphyToolbar'>"
394 " <separator />"
395 " <toolitem action='SetProfileAction'/>"
396 " <separator />"
397 " <toolitem action='CalligraphyWidthAction' />"
398 " <toolitem action='PressureAction' />"
399 " <toolitem action='TraceAction' />"
400 " <toolitem action='ThinningAction' />"
401 " <separator />"
402 " <toolitem action='AngleAction' />"
403 " <toolitem action='TiltAction' />"
404 " <toolitem action='FixationAction' />"
405 " <separator />"
406 " <toolitem action='CapRoundingAction' />"
407 " <separator />"
408 " <toolitem action='TremorAction' />"
409 " <toolitem action='WiggleAction' />"
410 " <toolitem action='MassAction' />"
411 " <separator />"
412 " </toolbar>"
414 " <toolbar name='ArcToolbar'>"
415 " <toolitem action='ArcStateAction' />"
416 " <separator />"
417 " <toolitem action='ArcStartAction' />"
418 " <toolitem action='ArcEndAction' />"
419 " <separator />"
420 " <toolitem action='ArcOpenAction' />"
421 " <separator />"
422 " <toolitem action='ArcResetAction' />"
423 " <separator />"
424 " </toolbar>"
426 " <toolbar name='PaintbucketToolbar'>"
427 " <toolitem action='ChannelsAction' />"
428 " <separator />"
429 " <toolitem action='ThresholdAction' />"
430 " <separator />"
431 " <toolitem action='OffsetAction' />"
432 " <toolitem action='PaintbucketUnitsAction' />"
433 " <separator />"
434 " <toolitem action='AutoGapAction' />"
435 " <separator />"
436 " <toolitem action='PaintbucketResetAction' />"
437 " </toolbar>"
439 " <toolbar name='EraserToolbar'>"
440 " <toolitem action='EraserWidthAction' />"
441 " <separator />"
442 " <toolitem action='EraserModeAction' />"
443 " </toolbar>"
445 " <toolbar name='LPEToolToolbar'>"
446 " <toolitem action='LPEToolModeAction' />"
447 " <separator />"
448 " <toolitem action='LPEShowBBoxAction' />"
449 " <toolitem action='LPEBBoxFromSelectionAction' />"
450 " <separator />"
451 " <toolitem action='LPELineSegmentAction' />"
452 " <separator />"
453 " <toolitem action='LPEMeasuringAction' />"
454 " <toolitem action='LPEToolUnitsAction' />"
455 " <separator />"
456 " <toolitem action='LPEOpenLPEDialogAction' />"
457 " </toolbar>"
459 " <toolbar name='DropperToolbar'>"
460 " <toolitem action='DropperOpacityAction' />"
461 " <toolitem action='DropperPickAlphaAction' />"
462 " <toolitem action='DropperSetAlphaAction' />"
463 " </toolbar>"
465 " <toolbar name='ConnectorToolbar'>"
466 " <toolitem action='ConnectorAvoidAction' />"
467 " <toolitem action='ConnectorIgnoreAction' />"
468 " <toolitem action='ConnectorSpacingAction' />"
469 " <toolitem action='ConnectorGraphAction' />"
470 " <toolitem action='ConnectorLengthAction' />"
471 " <toolitem action='ConnectorDirectedAction' />"
472 " <toolitem action='ConnectorOverlapAction' />"
473 " </toolbar>"
475 "</ui>"
476 ;
478 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
480 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
482 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
483 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
485 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
486 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
488 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
489 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
492 GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick( GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
493 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
494 Inkscape::UI::View::View *view, GtkTooltips *tt);
496 class VerbAction : public Gtk::Action {
497 public:
498 static Glib::RefPtr<VerbAction> create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
500 virtual ~VerbAction();
501 virtual void set_active(bool active = true);
503 protected:
504 virtual Gtk::Widget* create_menu_item_vfunc();
505 virtual Gtk::Widget* create_tool_item_vfunc();
507 virtual void connect_proxy_vfunc(Gtk::Widget* proxy);
508 virtual void disconnect_proxy_vfunc(Gtk::Widget* proxy);
510 virtual void on_activate();
512 private:
513 Inkscape::Verb* verb;
514 Inkscape::Verb* verb2;
515 Inkscape::UI::View::View *view;
516 GtkTooltips *tooltips;
517 bool active;
519 VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
520 };
523 Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips)
524 {
525 Glib::RefPtr<VerbAction> result;
526 SPAction *action = verb->get_action(view);
527 if ( action ) {
528 //SPAction* action2 = verb2 ? verb2->get_action(view) : 0;
529 result = Glib::RefPtr<VerbAction>(new VerbAction(verb, verb2, view, tooltips));
530 }
532 return result;
533 }
535 VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips) :
536 Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(verb->get_image()), Glib::ustring(_(verb->get_name())), Glib::ustring(_(verb->get_tip()))),
537 verb(verb),
538 verb2(verb2),
539 view(view),
540 tooltips(tooltips),
541 active(false)
542 {
543 }
545 VerbAction::~VerbAction()
546 {
547 }
549 Gtk::Widget* VerbAction::create_menu_item_vfunc()
550 {
551 // First call in to get the icon rendered if present in SVG
552 Gtk::Widget *widget = sp_icon_get_icon( property_stock_id().get_value().get_string(), Inkscape::ICON_SIZE_MENU );
553 delete widget;
554 widget = 0;
556 Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
557 // g_message("create_menu_item_vfunc() = %p for '%s'", widg, verb->get_id());
558 return widg;
559 }
561 Gtk::Widget* VerbAction::create_tool_item_vfunc()
562 {
563 // Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
564 Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
565 GtkWidget* toolbox = 0;
566 GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
567 SP_BUTTON_TYPE_TOGGLE,
568 verb,
569 verb2,
570 view,
571 tooltips );
572 if ( active ) {
573 sp_button_toggle_set_down( SP_BUTTON(button), active);
574 }
575 gtk_widget_show_all( button );
576 Gtk::Widget* wrapped = Glib::wrap(button);
577 Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
578 holder->add(*wrapped);
580 // g_message("create_tool_item_vfunc() = %p for '%s'", holder, verb->get_id());
581 return holder;
582 }
584 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
585 {
586 // g_message("connect_proxy_vfunc(%p) for '%s'", proxy, verb->get_id());
587 Gtk::Action::connect_proxy_vfunc(proxy);
588 }
590 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
591 {
592 // g_message("disconnect_proxy_vfunc(%p) for '%s'", proxy, verb->get_id());
593 Gtk::Action::disconnect_proxy_vfunc(proxy);
594 }
596 void VerbAction::set_active(bool active)
597 {
598 this->active = active;
599 Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
600 for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
601 Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
602 if (ti) {
603 // *should* have one child that is the SPButton
604 Gtk::Widget* child = ti->get_child();
605 if ( child && SP_IS_BUTTON(child->gobj()) ) {
606 SPButton* button = SP_BUTTON(child->gobj());
607 sp_button_toggle_set_down( button, active );
608 }
609 }
610 }
611 }
613 void VerbAction::on_activate()
614 {
615 if ( verb ) {
616 SPAction *action = verb->get_action(view);
617 if ( action ) {
618 sp_action_perform(action, 0);
619 }
620 }
621 }
623 /* Global text entry widgets necessary for update */
624 /* GtkWidget *dropper_rgb_entry,
625 *dropper_opacity_entry ; */
626 // should be made a private member once this is converted to class
628 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
629 connection->disconnect();
630 delete connection;
631 }
633 static void purge_repr_listener( GObject* obj, GObject* tbl )
634 {
635 (void)obj;
636 Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
637 if (oldrepr) { // remove old listener
638 sp_repr_remove_listener_by_data(oldrepr, tbl);
639 Inkscape::GC::release(oldrepr);
640 oldrepr = 0;
641 g_object_set_data( tbl, "repr", NULL );
642 }
643 }
645 GtkWidget *
646 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
647 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
648 Inkscape::UI::View::View *view, GtkTooltips *tt)
649 {
650 SPAction *action = verb->get_action(view);
651 if (!action) return NULL;
653 SPAction *doubleclick_action;
654 if (doubleclick_verb)
655 doubleclick_action = doubleclick_verb->get_action(view);
656 else
657 doubleclick_action = NULL;
659 /* fixme: Handle sensitive/unsensitive */
660 /* fixme: Implement sp_button_new_from_action */
661 GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
662 gtk_widget_show(b);
665 unsigned int shortcut = sp_shortcut_get_primary(verb);
666 if (shortcut) {
667 gchar key[256];
668 sp_ui_shortcut_string(shortcut, key);
669 gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
670 if ( t ) {
671 gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
672 }
673 g_free(tip);
674 } else {
675 if ( t ) {
676 gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
677 }
678 }
680 return b;
681 }
684 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
685 {
686 SPAction* targetAction = SP_ACTION(user_data);
687 if ( targetAction ) {
688 sp_action_perform( targetAction, NULL );
689 }
690 }
692 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
693 {
694 if ( data ) {
695 GtkAction* act = GTK_ACTION(data);
696 gtk_action_set_sensitive( act, sensitive );
697 }
698 }
700 static SPActionEventVector action_event_vector = {
701 {NULL},
702 NULL,
703 NULL,
704 sp_action_action_set_sensitive,
705 NULL,
706 NULL
707 };
709 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
710 {
711 GtkAction* act = 0;
713 SPAction* targetAction = verb->get_action(view);
714 InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size );
715 act = GTK_ACTION(inky);
716 gtk_action_set_sensitive( act, targetAction->sensitive );
718 g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
720 SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
721 nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
723 return act;
724 }
726 Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
727 {
728 Inkscape::UI::View::View *view = desktop;
729 gint verbsToUse[] = {
730 // disabled until we have icons for them:
731 //find
732 //SP_VERB_EDIT_TILE,
733 //SP_VERB_EDIT_UNTILE,
734 SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
735 SP_VERB_DIALOG_DISPLAY,
736 SP_VERB_DIALOG_FILL_STROKE,
737 SP_VERB_DIALOG_NAMEDVIEW,
738 SP_VERB_DIALOG_TEXT,
739 SP_VERB_DIALOG_XML_EDITOR,
740 SP_VERB_EDIT_CLONE,
741 SP_VERB_EDIT_COPY,
742 SP_VERB_EDIT_CUT,
743 SP_VERB_EDIT_DUPLICATE,
744 SP_VERB_EDIT_PASTE,
745 SP_VERB_EDIT_REDO,
746 SP_VERB_EDIT_UNDO,
747 SP_VERB_EDIT_UNLINK_CLONE,
748 SP_VERB_FILE_EXPORT,
749 SP_VERB_FILE_IMPORT,
750 SP_VERB_FILE_NEW,
751 SP_VERB_FILE_OPEN,
752 SP_VERB_FILE_PRINT,
753 SP_VERB_FILE_SAVE,
754 SP_VERB_OBJECT_TO_CURVE,
755 SP_VERB_SELECTION_GROUP,
756 SP_VERB_SELECTION_OUTLINE,
757 SP_VERB_SELECTION_UNGROUP,
758 SP_VERB_ZOOM_1_1,
759 SP_VERB_ZOOM_1_2,
760 SP_VERB_ZOOM_2_1,
761 SP_VERB_ZOOM_DRAWING,
762 SP_VERB_ZOOM_IN,
763 SP_VERB_ZOOM_NEXT,
764 SP_VERB_ZOOM_OUT,
765 SP_VERB_ZOOM_PAGE,
766 SP_VERB_ZOOM_PAGE_WIDTH,
767 SP_VERB_ZOOM_PREV,
768 SP_VERB_ZOOM_SELECTION,
769 };
771 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
773 static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
774 Glib::RefPtr<Gtk::ActionGroup> mainActions;
775 if ( groups.find(desktop) != groups.end() ) {
776 mainActions = groups[desktop];
777 }
779 if ( !mainActions ) {
780 mainActions = Gtk::ActionGroup::create("main");
781 groups[desktop] = mainActions;
782 }
784 for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
785 Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
786 if ( verb ) {
787 if (!mainActions->get_action(verb->get_id())) {
788 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
789 mainActions->add(Glib::wrap(act));
790 }
791 }
792 }
794 if ( !mainActions->get_action("ToolZoom") ) {
795 GtkTooltips *tt = gtk_tooltips_new();
796 for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
797 Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
798 if ( va ) {
799 mainActions->add(va);
800 if ( i == 0 ) {
801 va->set_active(true);
802 }
803 }
804 }
805 }
808 return mainActions;
809 }
812 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
813 {
814 gtk_widget_set_size_request( widget,
815 widget->allocation.width,
816 widget->allocation.height );
817 }
819 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
820 {
821 gtk_widget_set_size_request( widget, -1, -1 );
822 }
826 GtkWidget *
827 sp_tool_toolbox_new()
828 {
829 GtkTooltips *tt = gtk_tooltips_new();
830 GtkWidget* tb = gtk_toolbar_new();
831 gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
832 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
834 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
835 g_object_set_data(G_OBJECT(tb), "tooltips", tt);
837 gtk_widget_set_sensitive(tb, FALSE);
839 GtkWidget *hb = gtk_handle_box_new();
840 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
841 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
842 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
844 gtk_container_add(GTK_CONTAINER(hb), tb);
845 gtk_widget_show(GTK_WIDGET(tb));
847 sigc::connection* conn = new sigc::connection;
848 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
850 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
851 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
853 return hb;
854 }
856 GtkWidget *
857 sp_aux_toolbox_new()
858 {
859 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
861 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
863 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
865 gtk_widget_set_sensitive(tb, FALSE);
867 GtkWidget *hb = gtk_handle_box_new();
868 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
869 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
870 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
872 gtk_container_add(GTK_CONTAINER(hb), tb);
873 gtk_widget_show(GTK_WIDGET(tb));
875 sigc::connection* conn = new sigc::connection;
876 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
878 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
879 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
881 return hb;
882 }
884 //####################################
885 //# Commands Bar
886 //####################################
888 GtkWidget *
889 sp_commands_toolbox_new()
890 {
891 GtkWidget *tb = gtk_toolbar_new();
893 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
894 gtk_widget_set_sensitive(tb, FALSE);
896 GtkWidget *hb = gtk_handle_box_new();
897 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
898 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
899 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
901 gtk_container_add(GTK_CONTAINER(hb), tb);
902 gtk_widget_show(GTK_WIDGET(tb));
904 sigc::connection* conn = new sigc::connection;
905 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
907 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
908 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
910 return hb;
911 }
914 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
915 gchar const *label, gchar const *shortLabel, gchar const *tooltip,
916 gchar const *path, gchar const *data, gdouble def,
917 GtkWidget *focusTarget,
918 GtkWidget *us,
919 GObject *dataKludge,
920 gboolean altx, gchar const *altx_mark,
921 gdouble lower, gdouble upper, gdouble step, gdouble page,
922 gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
923 void (*callback)(GtkAdjustment *, GObject *),
924 gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
925 {
926 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
927 GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs->getDouble(path, data, def) * factor,
928 lower, upper, step, page, page ) );
929 if (us) {
930 sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
931 }
933 gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
935 EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
936 if ( shortLabel ) {
937 g_object_set( act, "short_label", shortLabel, NULL );
938 }
940 if ( (descrCount > 0) && descrLabels && descrValues ) {
941 ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
942 }
944 if ( focusTarget ) {
945 ege_adjustment_action_set_focuswidget( act, focusTarget );
946 }
948 if ( altx && altx_mark ) {
949 g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
950 }
952 if ( dataKludge ) {
953 g_object_set_data( dataKludge, data, adj );
954 }
956 // Using a cast just to make sure we pass in the right kind of function pointer
957 g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
959 return act;
960 }
963 //####################################
964 //# node editing callbacks
965 //####################################
967 /**
968 * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
969 */
970 static ShapeEditor *get_current_shape_editor()
971 {
972 if (!SP_ACTIVE_DESKTOP) {
973 return NULL;
974 }
976 SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
978 if (!SP_IS_NODE_CONTEXT(event_context)) {
979 return NULL;
980 }
982 return SP_NODE_CONTEXT(event_context)->shape_editor;
983 }
986 void
987 sp_node_path_edit_add(void)
988 {
989 ShapeEditor *shape_editor = get_current_shape_editor();
990 if (shape_editor) shape_editor->add_node();
991 }
993 void
994 sp_node_path_edit_delete(void)
995 {
996 ShapeEditor *shape_editor = get_current_shape_editor();
997 if (shape_editor) shape_editor->delete_nodes_preserving_shape();
998 }
1000 void
1001 sp_node_path_edit_delete_segment(void)
1002 {
1003 ShapeEditor *shape_editor = get_current_shape_editor();
1004 if (shape_editor) shape_editor->delete_segment();
1005 }
1007 void
1008 sp_node_path_edit_break(void)
1009 {
1010 ShapeEditor *shape_editor = get_current_shape_editor();
1011 if (shape_editor) shape_editor->break_at_nodes();
1012 }
1014 void
1015 sp_node_path_edit_join(void)
1016 {
1017 ShapeEditor *shape_editor = get_current_shape_editor();
1018 if (shape_editor) shape_editor->join_nodes();
1019 }
1021 void
1022 sp_node_path_edit_join_segment(void)
1023 {
1024 ShapeEditor *shape_editor = get_current_shape_editor();
1025 if (shape_editor) shape_editor->join_segments();
1026 }
1028 void
1029 sp_node_path_edit_toline(void)
1030 {
1031 ShapeEditor *shape_editor = get_current_shape_editor();
1032 if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1033 }
1035 void
1036 sp_node_path_edit_tocurve(void)
1037 {
1038 ShapeEditor *shape_editor = get_current_shape_editor();
1039 if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1040 }
1042 void
1043 sp_node_path_edit_cusp(void)
1044 {
1045 ShapeEditor *shape_editor = get_current_shape_editor();
1046 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1047 }
1049 void
1050 sp_node_path_edit_smooth(void)
1051 {
1052 ShapeEditor *shape_editor = get_current_shape_editor();
1053 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1054 }
1056 void
1057 sp_node_path_edit_symmetrical(void)
1058 {
1059 ShapeEditor *shape_editor = get_current_shape_editor();
1060 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1061 }
1063 static void toggle_show_handles (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_handles", show);
1067 ShapeEditor *shape_editor = get_current_shape_editor();
1068 if (shape_editor) shape_editor->show_handles(show);
1069 }
1071 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1072 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1073 bool show = gtk_toggle_action_get_active( act );
1074 prefs->setBool("tools.nodes", "show_helperpath", show);
1075 ShapeEditor *shape_editor = get_current_shape_editor();
1076 if (shape_editor) shape_editor->show_helperpath(show);
1077 }
1079 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1080 sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1081 }
1083 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1084 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1085 }
1087 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1088 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1089 }
1091 /* is called when the node selection is modified */
1092 static void
1093 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1094 {
1095 GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1096 GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1097 GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1098 GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1100 // quit if run by the attr_changed listener
1101 if (g_object_get_data( tbl, "freeze" )) {
1102 return;
1103 }
1105 // in turn, prevent listener from responding
1106 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1108 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1109 SPUnit const *unit = tracker->getActiveUnit();
1111 ShapeEditor *shape_editor = get_current_shape_editor();
1112 if (shape_editor && shape_editor->has_nodepath()) {
1113 Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1114 int n_selected = 0;
1115 if (nodepath) {
1116 n_selected = nodepath->numSelected();
1117 }
1119 if (n_selected == 0) {
1120 gtk_action_set_sensitive(xact, FALSE);
1121 gtk_action_set_sensitive(yact, FALSE);
1122 } else {
1123 gtk_action_set_sensitive(xact, TRUE);
1124 gtk_action_set_sensitive(yact, TRUE);
1125 Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1126 Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1128 if (n_selected == 1) {
1129 Geom::Point sel_node = nodepath->singleSelectedCoords();
1130 if (oldx != sel_node[Geom::X] || oldy != sel_node[Geom::Y]) {
1131 gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[Geom::X], *unit));
1132 gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[Geom::Y], *unit));
1133 }
1134 } else {
1135 boost::optional<Geom::Coord> x = sp_node_selected_common_coord(nodepath, Geom::X);
1136 boost::optional<Geom::Coord> y = sp_node_selected_common_coord(nodepath, Geom::Y);
1137 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1138 /* Note: Currently x and y will always have a value, even if the coordinates of the
1139 selected nodes don't coincide (in this case we use the coordinates of the center
1140 of the bounding box). So the entries are never set to zero. */
1141 // FIXME: Maybe we should clear the entry if several nodes are selected
1142 // instead of providing a kind of average value
1143 gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1144 gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1145 }
1146 }
1147 }
1148 } else {
1149 // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1150 gtk_action_set_sensitive(xact, FALSE);
1151 gtk_action_set_sensitive(yact, FALSE);
1152 }
1154 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1155 }
1157 static void
1158 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1159 {
1160 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1161 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1163 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1164 SPUnit const *unit = tracker->getActiveUnit();
1166 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1167 prefs->setDouble("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
1168 }
1170 // quit if run by the attr_changed listener
1171 if (g_object_get_data( tbl, "freeze" )) {
1172 return;
1173 }
1175 // in turn, prevent listener from responding
1176 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1178 ShapeEditor *shape_editor = get_current_shape_editor();
1179 if (shape_editor && shape_editor->has_nodepath()) {
1180 double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1181 if (!strcmp(value_name, "x")) {
1182 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::X);
1183 }
1184 if (!strcmp(value_name, "y")) {
1185 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::Y);
1186 }
1187 }
1189 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1190 }
1192 static void
1193 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1194 {
1195 sp_node_path_value_changed(adj, tbl, "x");
1196 }
1198 static void
1199 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1200 {
1201 sp_node_path_value_changed(adj, tbl, "y");
1202 }
1204 void
1205 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1206 {
1207 {
1208 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1209 SPItem *item = selection->singleItem();
1210 if (item && SP_IS_LPE_ITEM(item)) {
1211 if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1212 gtk_action_set_sensitive(w, TRUE);
1213 } else {
1214 gtk_action_set_sensitive(w, FALSE);
1215 }
1216 } else {
1217 gtk_action_set_sensitive(w, FALSE);
1218 }
1219 }
1221 {
1222 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1223 SPItem *item = selection->singleItem();
1224 if (item && item->clip_ref && item->clip_ref->getObject()) {
1225 gtk_action_set_sensitive(w, TRUE);
1226 } else {
1227 gtk_action_set_sensitive(w, FALSE);
1228 }
1229 }
1231 {
1232 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1233 SPItem *item = selection->singleItem();
1234 if (item && item->mask_ref && item->mask_ref->getObject()) {
1235 gtk_action_set_sensitive(w, TRUE);
1236 } else {
1237 gtk_action_set_sensitive(w, FALSE);
1238 }
1239 }
1240 }
1242 void
1243 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1244 {
1245 sp_node_toolbox_sel_changed (selection, tbl);
1246 }
1250 //################################
1251 //## Node Editing Toolbox ##
1252 //################################
1254 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1255 {
1256 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1257 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1258 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1259 g_object_set_data( holder, "tracker", tracker );
1261 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
1263 {
1264 InkAction* inky = ink_action_new( "NodeInsertAction",
1265 _("Insert node"),
1266 _("Insert new nodes into selected segments"),
1267 "node_insert",
1268 secondarySize );
1269 g_object_set( inky, "short_label", _("Insert"), NULL );
1270 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1271 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1272 }
1274 {
1275 InkAction* inky = ink_action_new( "NodeDeleteAction",
1276 _("Delete node"),
1277 _("Delete selected nodes"),
1278 "node_delete",
1279 secondarySize );
1280 g_object_set( inky, "short_label", _("Delete"), NULL );
1281 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1282 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1283 }
1285 {
1286 InkAction* inky = ink_action_new( "NodeJoinAction",
1287 _("Join endnodes"),
1288 _("Join selected endnodes"),
1289 "node_join",
1290 secondarySize );
1291 g_object_set( inky, "short_label", _("Join"), NULL );
1292 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1293 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1294 }
1296 {
1297 InkAction* inky = ink_action_new( "NodeBreakAction",
1298 _("Break nodes"),
1299 _("Break path at selected nodes"),
1300 "node_break",
1301 secondarySize );
1302 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1303 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1304 }
1307 {
1308 InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1309 _("Join with segment"),
1310 _("Join selected endnodes with a new segment"),
1311 "node_join_segment",
1312 secondarySize );
1313 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1314 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1315 }
1317 {
1318 InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1319 _("Delete segment"),
1320 _("Delete segment between two non-endpoint nodes"),
1321 "node_delete_segment",
1322 secondarySize );
1323 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1324 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1325 }
1327 {
1328 InkAction* inky = ink_action_new( "NodeCuspAction",
1329 _("Node Cusp"),
1330 _("Make selected nodes corner"),
1331 "node_cusp",
1332 secondarySize );
1333 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1334 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1335 }
1337 {
1338 InkAction* inky = ink_action_new( "NodeSmoothAction",
1339 _("Node Smooth"),
1340 _("Make selected nodes smooth"),
1341 "node_smooth",
1342 secondarySize );
1343 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1344 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1345 }
1347 {
1348 InkAction* inky = ink_action_new( "NodeSymmetricAction",
1349 _("Node Symmetric"),
1350 _("Make selected nodes symmetric"),
1351 "node_symmetric",
1352 secondarySize );
1353 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1354 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1355 }
1357 {
1358 InkAction* inky = ink_action_new( "NodeLineAction",
1359 _("Node Line"),
1360 _("Make selected segments lines"),
1361 "node_line",
1362 secondarySize );
1363 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1364 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1365 }
1367 {
1368 InkAction* inky = ink_action_new( "NodeCurveAction",
1369 _("Node Curve"),
1370 _("Make selected segments curves"),
1371 "node_curve",
1372 secondarySize );
1373 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1374 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1375 }
1377 {
1378 InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1379 _("Show Handles"),
1380 _("Show the Bezier handles of selected nodes"),
1381 "nodes_show_handles",
1382 Inkscape::ICON_SIZE_DECORATION );
1383 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1384 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1385 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("tools.nodes", "show_handles", true) );
1386 }
1388 {
1389 InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1390 _("Show Outline"),
1391 _("Show the outline of the path"),
1392 "nodes_show_helperpath",
1393 Inkscape::ICON_SIZE_DECORATION );
1394 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1395 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1396 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("tools.nodes", "show_helperpath", false) );
1397 }
1399 {
1400 InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1401 _("Next path effect parameter"),
1402 _("Show next path effect parameter for editing"),
1403 "edit_next_parameter",
1404 Inkscape::ICON_SIZE_DECORATION );
1405 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1406 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1407 g_object_set_data( holder, "nodes_lpeedit", inky);
1408 }
1410 {
1411 InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1412 _("Edit clipping path"),
1413 _("Edit the clipping path of the object"),
1414 "nodeedit-clippath",
1415 Inkscape::ICON_SIZE_DECORATION );
1416 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1417 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1418 g_object_set_data( holder, "nodes_clippathedit", inky);
1419 }
1421 {
1422 InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1423 _("Edit mask path"),
1424 _("Edit the mask of the object"),
1425 "nodeedit-mask",
1426 Inkscape::ICON_SIZE_DECORATION );
1427 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1428 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1429 g_object_set_data( holder, "nodes_maskedit", inky);
1430 }
1432 /* X coord of selected node(s) */
1433 {
1434 EgeAdjustmentAction* eact = 0;
1435 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1436 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1437 eact = create_adjustment_action( "NodeXAction",
1438 _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1439 "tools.nodes", "Xcoord", 0,
1440 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1441 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1442 labels, values, G_N_ELEMENTS(labels),
1443 sp_node_path_x_value_changed );
1444 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1445 g_object_set_data( holder, "nodes_x_action", eact );
1446 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1447 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1448 }
1450 /* Y coord of selected node(s) */
1451 {
1452 EgeAdjustmentAction* eact = 0;
1453 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1454 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1455 eact = create_adjustment_action( "NodeYAction",
1456 _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1457 "tools.nodes", "Ycoord", 0,
1458 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1459 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1460 labels, values, G_N_ELEMENTS(labels),
1461 sp_node_path_y_value_changed );
1462 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1463 g_object_set_data( holder, "nodes_y_action", eact );
1464 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1465 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1466 }
1468 // add the units menu
1469 {
1470 GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1471 gtk_action_group_add_action( mainActions, act );
1472 }
1475 sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1477 //watch selection
1478 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1480 sigc::connection *c_selection_changed =
1481 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1482 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1483 pool->add_connection ("selection-changed", c_selection_changed);
1485 sigc::connection *c_selection_modified =
1486 new sigc::connection (sp_desktop_selection (desktop)->connectModified
1487 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1488 pool->add_connection ("selection-modified", c_selection_modified);
1490 sigc::connection *c_subselection_changed =
1491 new sigc::connection (desktop->connectToolSubselectionChanged
1492 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1493 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1495 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1497 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1498 } // end of sp_node_toolbox_prep()
1501 //########################
1502 //## Zoom Toolbox ##
1503 //########################
1505 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1506 {
1507 // no custom GtkAction setup needed
1508 } // end of sp_zoom_toolbox_prep()
1510 void
1511 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1512 {
1513 toolbox_set_desktop(toolbox,
1514 desktop,
1515 setup_tool_toolbox,
1516 update_tool_toolbox,
1517 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1518 "event_context_connection")));
1519 }
1522 void
1523 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1524 {
1525 toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1526 desktop,
1527 setup_aux_toolbox,
1528 update_aux_toolbox,
1529 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1530 "event_context_connection")));
1531 }
1533 void
1534 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1535 {
1536 toolbox_set_desktop(toolbox,
1537 desktop,
1538 setup_commands_toolbox,
1539 update_commands_toolbox,
1540 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1541 "event_context_connection")));
1542 }
1544 static void
1545 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1546 {
1547 gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1548 SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1550 if (old_desktop) {
1551 GList *children, *iter;
1553 children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1554 for ( iter = children ; iter ; iter = iter->next ) {
1555 gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1556 }
1557 g_list_free(children);
1558 }
1560 g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1562 if (desktop) {
1563 gtk_widget_set_sensitive(toolbox, TRUE);
1564 setup_func(toolbox, desktop);
1565 update_func(desktop, desktop->event_context, toolbox);
1566 *conn = desktop->connectEventContextChanged
1567 (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1568 } else {
1569 gtk_widget_set_sensitive(toolbox, FALSE);
1570 }
1572 } // end of toolbox_set_desktop()
1575 static void
1576 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1577 {
1578 gchar const * descr =
1579 "<ui>"
1580 " <toolbar name='ToolToolbar'>"
1581 " <toolitem action='ToolSelector' />"
1582 " <toolitem action='ToolNode' />"
1583 " <toolitem action='ToolTweak' />"
1584 " <toolitem action='ToolZoom' />"
1585 " <toolitem action='ToolRect' />"
1586 " <toolitem action='Tool3DBox' />"
1587 " <toolitem action='ToolArc' />"
1588 " <toolitem action='ToolStar' />"
1589 " <toolitem action='ToolSpiral' />"
1590 " <toolitem action='ToolPencil' />"
1591 " <toolitem action='ToolPen' />"
1592 " <toolitem action='ToolCalligraphic' />"
1593 " <toolitem action='ToolEraser' />"
1594 // " <toolitem action='ToolLPETool' />"
1595 " <toolitem action='ToolPaintBucket' />"
1596 " <toolitem action='ToolText' />"
1597 " <toolitem action='ToolConnector' />"
1598 " <toolitem action='ToolGradient' />"
1599 " <toolitem action='ToolDropper' />"
1600 " </toolbar>"
1601 "</ui>";
1602 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1603 GtkUIManager* mgr = gtk_ui_manager_new();
1604 GError* errVal = 0;
1605 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1607 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1608 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1610 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" );
1611 if ( prefs->getIntLimited("toolbox", "icononly", 1, 0, 1) ) {
1612 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1613 }
1614 Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1615 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1617 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
1618 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1620 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1622 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1623 if ( child ) {
1624 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1625 }
1627 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1628 // Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1629 }
1632 static void
1633 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1634 {
1635 gchar const *const tname = ( eventcontext
1636 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1637 : NULL );
1638 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1640 for (int i = 0 ; tools[i].type_name ; i++ ) {
1641 Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1642 if ( act ) {
1643 bool setActive = tname && !strcmp(tname, tools[i].type_name);
1644 Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1645 if ( verbAct ) {
1646 verbAct->set_active(setActive);
1647 }
1648 }
1649 }
1650 }
1652 static void
1653 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1654 {
1655 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1656 GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1657 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1658 GtkUIManager* mgr = gtk_ui_manager_new();
1659 GError* errVal = 0;
1660 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1661 gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1663 std::map<std::string, GtkWidget*> dataHolders;
1665 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1666 if ( aux_toolboxes[i].prep_func ) {
1667 // converted to GtkActions and UIManager
1669 GtkWidget* kludge = gtk_toolbar_new();
1670 g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1671 g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1672 dataHolders[aux_toolboxes[i].type_name] = kludge;
1673 aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1674 } else {
1676 GtkWidget *sub_toolbox = 0;
1677 if (aux_toolboxes[i].create_func == NULL)
1678 sub_toolbox = sp_empty_toolbox_new(desktop);
1679 else {
1680 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1681 }
1683 gtk_size_group_add_widget( grouper, sub_toolbox );
1685 gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1686 g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1688 }
1689 }
1691 // Second pass to create toolbars *after* all GtkActions are created
1692 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1693 if ( aux_toolboxes[i].prep_func ) {
1694 // converted to GtkActions and UIManager
1696 GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1698 GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1699 gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1701 gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1702 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1703 g_free( tmp );
1704 tmp = 0;
1706 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1707 /// @todo convert to getBool
1708 if ( prefs->getIntLimited( "toolbox", "icononly", 1, 0, 1 ) ) {
1709 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1710 }
1711 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1714 gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1716 if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1717 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1718 swatch->setDesktop( desktop );
1719 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1720 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1721 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1722 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 );
1723 }
1725 gtk_widget_show_all( holder );
1726 sp_set_font_size_smaller( holder );
1728 gtk_size_group_add_widget( grouper, holder );
1730 gtk_container_add( GTK_CONTAINER(toolbox), holder );
1731 g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1732 }
1733 }
1735 g_object_unref( G_OBJECT(grouper) );
1736 }
1738 static void
1739 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1740 {
1741 gchar const *tname = ( eventcontext
1742 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1743 : NULL );
1744 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1745 GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1746 if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1747 gtk_widget_show_all(sub_toolbox);
1748 g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1749 } else {
1750 gtk_widget_hide(sub_toolbox);
1751 }
1752 }
1753 }
1755 static void
1756 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1757 {
1758 gchar const * descr =
1759 "<ui>"
1760 " <toolbar name='CommandsToolbar'>"
1761 " <toolitem action='FileNew' />"
1762 " <toolitem action='FileOpen' />"
1763 " <toolitem action='FileSave' />"
1764 " <toolitem action='FilePrint' />"
1765 " <separator />"
1766 " <toolitem action='FileImport' />"
1767 " <toolitem action='FileExport' />"
1768 " <separator />"
1769 " <toolitem action='EditUndo' />"
1770 " <toolitem action='EditRedo' />"
1771 " <separator />"
1772 " <toolitem action='EditCopy' />"
1773 " <toolitem action='EditCut' />"
1774 " <toolitem action='EditPaste' />"
1775 " <separator />"
1776 " <toolitem action='ZoomSelection' />"
1777 " <toolitem action='ZoomDrawing' />"
1778 " <toolitem action='ZoomPage' />"
1779 " <separator />"
1780 " <toolitem action='EditDuplicate' />"
1781 " <toolitem action='EditClone' />"
1782 " <toolitem action='EditUnlinkClone' />"
1783 " <separator />"
1784 " <toolitem action='SelectionGroup' />"
1785 " <toolitem action='SelectionUnGroup' />"
1786 " <separator />"
1787 " <toolitem action='DialogFillStroke' />"
1788 " <toolitem action='DialogText' />"
1789 " <toolitem action='DialogXMLEditor' />"
1790 " <toolitem action='DialogAlignDistribute' />"
1791 " <separator />"
1792 " <toolitem action='DialogPreferences' />"
1793 " <toolitem action='DialogDocumentProperties' />"
1794 " </toolbar>"
1795 "</ui>";
1796 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1797 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1799 GtkUIManager* mgr = gtk_ui_manager_new();
1800 GError* errVal = 0;
1802 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1803 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1805 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1806 if ( prefs->getBool("toolbox", "icononly", true) ) {
1807 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1808 }
1810 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1811 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1813 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1814 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1817 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1819 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1820 if ( child ) {
1821 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1822 }
1824 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1825 }
1827 static void
1828 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1829 {
1830 }
1832 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1833 {
1834 gtk_widget_show(toolbox_toplevel);
1835 GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1837 GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1838 if (!shown_toolbox) {
1839 return;
1840 }
1841 gtk_widget_show(toolbox);
1843 gtk_widget_show_all(shown_toolbox);
1844 }
1846 static GtkWidget *
1847 sp_empty_toolbox_new(SPDesktop *desktop)
1848 {
1849 GtkWidget *tbl = gtk_toolbar_new();
1850 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1851 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1853 gtk_widget_show_all(tbl);
1854 sp_set_font_size_smaller (tbl);
1856 return tbl;
1857 }
1859 #define MODE_LABEL_WIDTH 70
1861 //########################
1862 //## Star ##
1863 //########################
1865 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1866 {
1867 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1869 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1870 // do not remember prefs if this call is initiated by an undo change, because undoing object
1871 // creation sets bogus values to its attributes before it is deleted
1872 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1873 prefs->setInt("tools.shapes.star", "magnitude", (gint)adj->value);
1874 }
1876 // quit if run by the attr_changed listener
1877 if (g_object_get_data( dataKludge, "freeze" )) {
1878 return;
1879 }
1881 // in turn, prevent listener from responding
1882 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1884 bool modmade = false;
1886 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1887 GSList const *items = selection->itemList();
1888 for (; items != NULL; items = items->next) {
1889 if (SP_IS_STAR((SPItem *) items->data)) {
1890 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1891 sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1892 sp_repr_set_svg_double(repr, "sodipodi:arg2",
1893 (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1894 + M_PI / (gint)adj->value));
1895 SP_OBJECT((SPItem *) items->data)->updateRepr();
1896 modmade = true;
1897 }
1898 }
1899 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1900 _("Star: Change number of corners"));
1902 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1903 }
1905 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1906 {
1907 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1909 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1910 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1911 prefs->setDouble("tools.shapes.star", "proportion", adj->value);
1912 }
1914 // quit if run by the attr_changed listener
1915 if (g_object_get_data( dataKludge, "freeze" )) {
1916 return;
1917 }
1919 // in turn, prevent listener from responding
1920 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1922 bool modmade = false;
1923 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1924 GSList const *items = selection->itemList();
1925 for (; items != NULL; items = items->next) {
1926 if (SP_IS_STAR((SPItem *) items->data)) {
1927 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1929 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1930 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1931 if (r2 < r1) {
1932 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1933 } else {
1934 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1935 }
1937 SP_OBJECT((SPItem *) items->data)->updateRepr();
1938 modmade = true;
1939 }
1940 }
1942 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1943 _("Star: Change spoke ratio"));
1945 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1946 }
1948 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1949 {
1950 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1951 bool flat = ege_select_one_action_get_active( act ) == 0;
1953 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1954 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1955 prefs->setBool( "tools.shapes.star", "isflatsided", flat);
1956 }
1958 // quit if run by the attr_changed listener
1959 if (g_object_get_data( dataKludge, "freeze" )) {
1960 return;
1961 }
1963 // in turn, prevent listener from responding
1964 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1966 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1967 GSList const *items = selection->itemList();
1968 GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1969 bool modmade = false;
1971 if ( prop_action ) {
1972 gtk_action_set_sensitive( prop_action, !flat );
1973 }
1975 for (; items != NULL; items = items->next) {
1976 if (SP_IS_STAR((SPItem *) items->data)) {
1977 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1978 repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1979 SP_OBJECT((SPItem *) items->data)->updateRepr();
1980 modmade = true;
1981 }
1982 }
1984 if (modmade) {
1985 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1986 flat ? _("Make polygon") : _("Make star"));
1987 }
1989 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1990 }
1992 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1993 {
1994 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1996 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1997 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1998 prefs->setDouble("tools.shapes.star", "rounded", (gdouble) adj->value);
1999 }
2001 // quit if run by the attr_changed listener
2002 if (g_object_get_data( dataKludge, "freeze" )) {
2003 return;
2004 }
2006 // in turn, prevent listener from responding
2007 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2009 bool modmade = false;
2011 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2012 GSList const *items = selection->itemList();
2013 for (; items != NULL; items = items->next) {
2014 if (SP_IS_STAR((SPItem *) items->data)) {
2015 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2016 sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2017 SP_OBJECT(items->data)->updateRepr();
2018 modmade = true;
2019 }
2020 }
2021 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2022 _("Star: Change rounding"));
2024 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2025 }
2027 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2028 {
2029 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2031 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2032 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2033 prefs->setDouble("tools.shapes.star", "randomized", (gdouble) adj->value);
2034 }
2036 // quit if run by the attr_changed listener
2037 if (g_object_get_data( dataKludge, "freeze" )) {
2038 return;
2039 }
2041 // in turn, prevent listener from responding
2042 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2044 bool modmade = false;
2046 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2047 GSList const *items = selection->itemList();
2048 for (; items != NULL; items = items->next) {
2049 if (SP_IS_STAR((SPItem *) items->data)) {
2050 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2051 sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2052 SP_OBJECT(items->data)->updateRepr();
2053 modmade = true;
2054 }
2055 }
2056 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2057 _("Star: Change randomization"));
2059 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2060 }
2063 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2064 gchar const */*old_value*/, gchar const */*new_value*/,
2065 bool /*is_interactive*/, gpointer data)
2066 {
2067 GtkWidget *tbl = GTK_WIDGET(data);
2069 // quit if run by the _changed callbacks
2070 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2071 return;
2072 }
2074 // in turn, prevent callbacks from responding
2075 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2077 GtkAdjustment *adj = 0;
2079 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2080 bool isFlatSided = prefs->getBool("tools.shapes.star", "isflatsided", true);
2082 if (!strcmp(name, "inkscape:randomized")) {
2083 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2084 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2085 } else if (!strcmp(name, "inkscape:rounded")) {
2086 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2087 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2088 } else if (!strcmp(name, "inkscape:flatsided")) {
2089 GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2090 char const *flatsides = repr->attribute("inkscape:flatsided");
2091 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2092 if ( flatsides && !strcmp(flatsides,"false") ) {
2093 ege_select_one_action_set_active( flat_action, 1 );
2094 gtk_action_set_sensitive( prop_action, TRUE );
2095 } else {
2096 ege_select_one_action_set_active( flat_action, 0 );
2097 gtk_action_set_sensitive( prop_action, FALSE );
2098 }
2099 } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2100 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2101 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2102 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2103 if (r2 < r1) {
2104 gtk_adjustment_set_value(adj, r2/r1);
2105 } else {
2106 gtk_adjustment_set_value(adj, r1/r2);
2107 }
2108 } else if (!strcmp(name, "sodipodi:sides")) {
2109 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2110 gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2111 }
2113 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2114 }
2117 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2118 {
2119 NULL, /* child_added */
2120 NULL, /* child_removed */
2121 star_tb_event_attr_changed,
2122 NULL, /* content_changed */
2123 NULL /* order_changed */
2124 };
2127 /**
2128 * \param selection Should not be NULL.
2129 */
2130 static void
2131 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2132 {
2133 int n_selected = 0;
2134 Inkscape::XML::Node *repr = NULL;
2136 purge_repr_listener( tbl, tbl );
2138 for (GSList const *items = selection->itemList();
2139 items != NULL;
2140 items = items->next)
2141 {
2142 if (SP_IS_STAR((SPItem *) items->data)) {
2143 n_selected++;
2144 repr = SP_OBJECT_REPR((SPItem *) items->data);
2145 }
2146 }
2148 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2150 if (n_selected == 0) {
2151 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2152 } else if (n_selected == 1) {
2153 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2155 if (repr) {
2156 g_object_set_data( tbl, "repr", repr );
2157 Inkscape::GC::anchor(repr);
2158 sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2159 sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2160 }
2161 } else {
2162 // FIXME: implement averaging of all parameters for multiple selected stars
2163 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2164 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2165 }
2166 }
2169 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2170 {
2171 // FIXME: in this and all other _default functions, set some flag telling the value_changed
2172 // callbacks to lump all the changes for all selected objects in one undo step
2174 GtkAdjustment *adj = 0;
2176 // fixme: make settable in prefs!
2177 gint mag = 5;
2178 gdouble prop = 0.5;
2179 gboolean flat = FALSE;
2180 gdouble randomized = 0;
2181 gdouble rounded = 0;
2183 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2184 ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2186 GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2187 gtk_action_set_sensitive( sb2, !flat );
2189 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2190 gtk_adjustment_set_value(adj, mag);
2191 gtk_adjustment_value_changed(adj);
2193 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2194 gtk_adjustment_set_value(adj, prop);
2195 gtk_adjustment_value_changed(adj);
2197 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2198 gtk_adjustment_set_value(adj, rounded);
2199 gtk_adjustment_value_changed(adj);
2201 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2202 gtk_adjustment_set_value(adj, randomized);
2203 gtk_adjustment_value_changed(adj);
2204 }
2207 void
2208 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2209 {
2210 GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2211 if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2212 GtkWidget *l = gtk_label_new(NULL);
2213 gtk_label_set_markup(GTK_LABEL(l), title);
2214 gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2215 if ( GTK_IS_TOOLBAR(tbl) ) {
2216 gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2217 } else {
2218 gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2219 }
2220 gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2221 }
2224 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2225 {
2226 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2228 {
2229 EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2230 ege_output_action_set_use_markup( act, TRUE );
2231 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2232 g_object_set_data( holder, "mode_action", act );
2233 }
2235 {
2236 EgeAdjustmentAction* eact = 0;
2237 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2238 bool isFlatSided = prefs->getBool("tools.shapes.star", "isflatsided", true);
2240 /* Flatsided checkbox */
2241 {
2242 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2244 GtkTreeIter iter;
2245 gtk_list_store_append( model, &iter );
2246 gtk_list_store_set( model, &iter,
2247 0, _("Polygon"),
2248 1, _("Regular polygon (with one handle) instead of a star"),
2249 2, "star_flat",
2250 -1 );
2252 gtk_list_store_append( model, &iter );
2253 gtk_list_store_set( model, &iter,
2254 0, _("Star"),
2255 1, _("Star instead of a regular polygon (with one handle)"),
2256 2, "star_angled",
2257 -1 );
2259 EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2260 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2261 g_object_set_data( holder, "flat_action", act );
2263 ege_select_one_action_set_appearance( act, "full" );
2264 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2265 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2266 ege_select_one_action_set_icon_column( act, 2 );
2267 ege_select_one_action_set_icon_size( act, secondarySize );
2268 ege_select_one_action_set_tooltip_column( act, 1 );
2270 ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2271 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2272 }
2274 /* Magnitude */
2275 {
2276 gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2277 gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2278 eact = create_adjustment_action( "MagnitudeAction",
2279 _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2280 "tools.shapes.star", "magnitude", 3,
2281 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2282 3, 1024, 1, 5,
2283 labels, values, G_N_ELEMENTS(labels),
2284 sp_stb_magnitude_value_changed,
2285 1.0, 0 );
2286 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2287 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2288 }
2290 /* Spoke ratio */
2291 {
2292 gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2293 gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2294 eact = create_adjustment_action( "SpokeAction",
2295 _("Spoke ratio"), _("Spoke ratio:"),
2296 // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2297 // Base radius is the same for the closest handle.
2298 _("Base radius to tip radius ratio"),
2299 "tools.shapes.star", "proportion", 0.5,
2300 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2301 0.01, 1.0, 0.01, 0.1,
2302 labels, values, G_N_ELEMENTS(labels),
2303 sp_stb_proportion_value_changed );
2304 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2305 g_object_set_data( holder, "prop_action", eact );
2306 }
2308 if ( !isFlatSided ) {
2309 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2310 } else {
2311 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2312 }
2314 /* Roundedness */
2315 {
2316 gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2317 gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2318 eact = create_adjustment_action( "RoundednessAction",
2319 _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2320 "tools.shapes.star", "rounded", 0.0,
2321 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2322 -10.0, 10.0, 0.01, 0.1,
2323 labels, values, G_N_ELEMENTS(labels),
2324 sp_stb_rounded_value_changed );
2325 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2326 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2327 }
2329 /* Randomization */
2330 {
2331 gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2332 gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2333 eact = create_adjustment_action( "RandomizationAction",
2334 _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2335 "tools.shapes.star", "randomized", 0.0,
2336 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2337 -10.0, 10.0, 0.001, 0.01,
2338 labels, values, G_N_ELEMENTS(labels),
2339 sp_stb_randomized_value_changed, 0.1, 3 );
2340 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2341 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2342 }
2343 }
2345 {
2346 /* Reset */
2347 {
2348 GtkAction* act = gtk_action_new( "StarResetAction",
2349 _("Defaults"),
2350 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2351 GTK_STOCK_CLEAR );
2352 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2353 gtk_action_group_add_action( mainActions, act );
2354 gtk_action_set_sensitive( act, TRUE );
2355 }
2356 }
2358 sigc::connection *connection = new sigc::connection(
2359 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2360 );
2361 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2362 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2363 }
2366 //########################
2367 //## Rect ##
2368 //########################
2370 static void sp_rtb_sensitivize( GObject *tbl )
2371 {
2372 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2373 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2374 GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2376 if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2377 gtk_action_set_sensitive( not_rounded, FALSE );
2378 } else {
2379 gtk_action_set_sensitive( not_rounded, TRUE );
2380 }
2381 }
2384 static void
2385 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2386 void (*setter)(SPRect *, gdouble))
2387 {
2388 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2390 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2391 SPUnit const *unit = tracker->getActiveUnit();
2393 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2394 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2395 prefs->setDouble("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2396 }
2398 // quit if run by the attr_changed listener
2399 if (g_object_get_data( tbl, "freeze" )) {
2400 return;
2401 }
2403 // in turn, prevent listener from responding
2404 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2406 bool modmade = false;
2407 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2408 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2409 if (SP_IS_RECT(items->data)) {
2410 if (adj->value != 0) {
2411 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2412 } else {
2413 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2414 }
2415 modmade = true;
2416 }
2417 }
2419 sp_rtb_sensitivize( tbl );
2421 if (modmade) {
2422 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2423 _("Change rectangle"));
2424 }
2426 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2427 }
2429 static void
2430 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2431 {
2432 sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2433 }
2435 static void
2436 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2437 {
2438 sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2439 }
2441 static void
2442 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2443 {
2444 sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2445 }
2447 static void
2448 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2449 {
2450 sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2451 }
2455 static void
2456 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2457 {
2458 GtkAdjustment *adj = 0;
2460 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2461 gtk_adjustment_set_value(adj, 0.0);
2462 // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2463 gtk_adjustment_value_changed(adj);
2465 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2466 gtk_adjustment_set_value(adj, 0.0);
2467 gtk_adjustment_value_changed(adj);
2469 sp_rtb_sensitivize( obj );
2470 }
2472 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2473 gchar const */*old_value*/, gchar const */*new_value*/,
2474 bool /*is_interactive*/, gpointer data)
2475 {
2476 GObject *tbl = G_OBJECT(data);
2478 // quit if run by the _changed callbacks
2479 if (g_object_get_data( tbl, "freeze" )) {
2480 return;
2481 }
2483 // in turn, prevent callbacks from responding
2484 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2486 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2487 SPUnit const *unit = tracker->getActiveUnit();
2489 gpointer item = g_object_get_data( tbl, "item" );
2490 if (item && SP_IS_RECT(item)) {
2491 {
2492 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2493 gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2494 gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2495 }
2497 {
2498 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2499 gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2500 gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2501 }
2503 {
2504 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2505 gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2506 gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2507 }
2509 {
2510 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2511 gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2512 gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2513 }
2514 }
2516 sp_rtb_sensitivize( tbl );
2518 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2519 }
2522 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2523 NULL, /* child_added */
2524 NULL, /* child_removed */
2525 rect_tb_event_attr_changed,
2526 NULL, /* content_changed */
2527 NULL /* order_changed */
2528 };
2530 /**
2531 * \param selection should not be NULL.
2532 */
2533 static void
2534 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2535 {
2536 int n_selected = 0;
2537 Inkscape::XML::Node *repr = NULL;
2538 SPItem *item = NULL;
2540 if ( g_object_get_data( tbl, "repr" ) ) {
2541 g_object_set_data( tbl, "item", NULL );
2542 }
2543 purge_repr_listener( tbl, tbl );
2545 for (GSList const *items = selection->itemList();
2546 items != NULL;
2547 items = items->next) {
2548 if (SP_IS_RECT((SPItem *) items->data)) {
2549 n_selected++;
2550 item = (SPItem *) items->data;
2551 repr = SP_OBJECT_REPR(item);
2552 }
2553 }
2555 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2557 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2559 if (n_selected == 0) {
2560 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2562 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2563 gtk_action_set_sensitive(w, FALSE);
2564 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2565 gtk_action_set_sensitive(h, FALSE);
2567 } else if (n_selected == 1) {
2568 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2569 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2571 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2572 gtk_action_set_sensitive(w, TRUE);
2573 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2574 gtk_action_set_sensitive(h, TRUE);
2576 if (repr) {
2577 g_object_set_data( tbl, "repr", repr );
2578 g_object_set_data( tbl, "item", item );
2579 Inkscape::GC::anchor(repr);
2580 sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2581 sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2582 }
2583 } else {
2584 // FIXME: implement averaging of all parameters for multiple selected
2585 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2586 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2587 sp_rtb_sensitivize( tbl );
2588 }
2589 }
2592 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2593 {
2594 EgeAdjustmentAction* eact = 0;
2595 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2597 {
2598 EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2599 ege_output_action_set_use_markup( act, TRUE );
2600 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2601 g_object_set_data( holder, "mode_action", act );
2602 }
2604 // rx/ry units menu: create
2605 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2606 //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2607 // fixme: add % meaning per cent of the width/height
2608 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2609 g_object_set_data( holder, "tracker", tracker );
2611 /* W */
2612 {
2613 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2614 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2615 eact = create_adjustment_action( "RectWidthAction",
2616 _("Width"), _("W:"), _("Width of rectangle"),
2617 "tools.shapes.rect", "width", 0,
2618 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2619 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2620 labels, values, G_N_ELEMENTS(labels),
2621 sp_rtb_width_value_changed );
2622 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2623 g_object_set_data( holder, "width_action", eact );
2624 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2625 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2626 }
2628 /* H */
2629 {
2630 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2631 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2632 eact = create_adjustment_action( "RectHeightAction",
2633 _("Height"), _("H:"), _("Height of rectangle"),
2634 "tools.shapes.rect", "height", 0,
2635 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2636 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2637 labels, values, G_N_ELEMENTS(labels),
2638 sp_rtb_height_value_changed );
2639 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2640 g_object_set_data( holder, "height_action", eact );
2641 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2642 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2643 }
2645 /* rx */
2646 {
2647 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2648 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2649 eact = create_adjustment_action( "RadiusXAction",
2650 _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2651 "tools.shapes.rect", "rx", 0,
2652 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2653 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2654 labels, values, G_N_ELEMENTS(labels),
2655 sp_rtb_rx_value_changed);
2656 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2657 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2658 }
2660 /* ry */
2661 {
2662 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2663 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2664 eact = create_adjustment_action( "RadiusYAction",
2665 _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2666 "tools.shapes.rect", "ry", 0,
2667 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2668 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2669 labels, values, G_N_ELEMENTS(labels),
2670 sp_rtb_ry_value_changed);
2671 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2672 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2673 }
2675 // add the units menu
2676 {
2677 GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2678 gtk_action_group_add_action( mainActions, act );
2679 }
2681 /* Reset */
2682 {
2683 InkAction* inky = ink_action_new( "RectResetAction",
2684 _("Not rounded"),
2685 _("Make corners sharp"),
2686 "squared_corner",
2687 secondarySize );
2688 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2689 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2690 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2691 g_object_set_data( holder, "not_rounded", inky );
2692 }
2694 g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2695 sp_rtb_sensitivize( holder );
2697 sigc::connection *connection = new sigc::connection(
2698 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2699 );
2700 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2701 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2702 }
2704 //########################
2705 //## 3D Box ##
2706 //########################
2708 // normalize angle so that it lies in the interval [0,360]
2709 static double box3d_normalize_angle (double a) {
2710 double angle = a + ((int) (a/360.0))*360;
2711 if (angle < 0) {
2712 angle += 360.0;
2713 }
2714 return angle;
2715 }
2717 static void
2718 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2719 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2720 // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2721 // have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2722 // are reset).
2723 bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2725 if (is_infinite) {
2726 gtk_toggle_action_set_active(tact, TRUE);
2727 gtk_action_set_sensitive(act, TRUE);
2729 double angle = persp3d_get_infinite_angle(persp, axis);
2730 if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2731 gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2732 }
2733 } else {
2734 gtk_toggle_action_set_active(tact, FALSE);
2735 gtk_action_set_sensitive(act, FALSE);
2736 }
2737 }
2739 static void
2740 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2741 if (!persp_repr) {
2742 g_print ("No perspective given to box3d_resync_toolbar().\n");
2743 return;
2744 }
2746 GtkWidget *tbl = GTK_WIDGET(data);
2747 GtkAdjustment *adj = 0;
2748 GtkAction *act = 0;
2749 GtkToggleAction *tact = 0;
2750 Persp3D *persp = persp3d_get_from_repr(persp_repr);
2751 {
2752 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2753 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2754 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2756 box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2757 }
2758 {
2759 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2760 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2761 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2763 box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2764 }
2765 {
2766 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2767 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2768 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2770 box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2771 }
2772 }
2774 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2775 gchar const */*old_value*/, gchar const */*new_value*/,
2776 bool /*is_interactive*/, gpointer data)
2777 {
2778 GtkWidget *tbl = GTK_WIDGET(data);
2780 // quit if run by the attr_changed listener
2781 // note: it used to work without the differently called freeze_ attributes (here and in
2782 // box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2783 if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2784 return;
2785 }
2787 // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2788 // sp_document_maybe_done() when the document is undo insensitive)
2789 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2791 // TODO: Only update the appropriate part of the toolbar
2792 // if (!strcmp(name, "inkscape:vp_z")) {
2793 box3d_resync_toolbar(repr, G_OBJECT(tbl));
2794 // }
2796 Persp3D *persp = persp3d_get_from_repr(repr);
2797 persp3d_update_box_reprs(persp);
2799 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2800 }
2802 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2803 {
2804 NULL, /* child_added */
2805 NULL, /* child_removed */
2806 box3d_persp_tb_event_attr_changed,
2807 NULL, /* content_changed */
2808 NULL /* order_changed */
2809 };
2811 /**
2812 * \param selection Should not be NULL.
2813 */
2814 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2815 // Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2816 static void
2817 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2818 {
2819 // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2820 // disable the angle entry fields for this direction (otherwise entering a value in them should only
2821 // update the perspectives with infinite VPs and leave the other ones untouched).
2823 Inkscape::XML::Node *persp_repr = NULL;
2824 purge_repr_listener(tbl, tbl);
2826 SPItem *item = selection->singleItem();
2827 if (item && SP_IS_BOX3D(item)) {
2828 // FIXME: Also deal with multiple selected boxes
2829 SPBox3D *box = SP_BOX3D(item);
2830 Persp3D *persp = box3d_get_perspective(box);
2831 persp_repr = SP_OBJECT_REPR(persp);
2832 if (persp_repr) {
2833 g_object_set_data(tbl, "repr", persp_repr);
2834 Inkscape::GC::anchor(persp_repr);
2835 sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2836 sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2837 }
2839 inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2840 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2841 prefs->setString("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2843 box3d_resync_toolbar(persp_repr, tbl);
2844 }
2845 }
2847 static void
2848 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2849 {
2850 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2851 SPDocument *document = sp_desktop_document(desktop);
2853 // quit if run by the attr_changed listener
2854 // note: it used to work without the differently called freeze_ attributes (here and in
2855 // box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2856 if (g_object_get_data( dataKludge, "freeze_attr" )) {
2857 return;
2858 }
2860 // in turn, prevent listener from responding
2861 g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2863 //Persp3D *persp = document->current_persp3d;
2864 std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
2865 if (sel_persps.empty()) {
2866 // this can happen when the document is created; we silently ignore it
2867 return;
2868 }
2869 Persp3D *persp = sel_persps.front();
2871 persp->tmat.set_infinite_direction (axis, adj->value);
2872 SP_OBJECT(persp)->updateRepr();
2874 // TODO: use the correct axis here, too
2875 sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2877 g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2878 }
2881 static void
2882 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2883 {
2884 box3d_angle_value_changed(adj, dataKludge, Proj::X);
2885 }
2887 static void
2888 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2889 {
2890 box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2891 }
2893 static void
2894 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2895 {
2896 box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2897 }
2900 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2901 {
2902 // TODO: Take all selected perspectives into account
2903 std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
2904 if (sel_persps.empty()) {
2905 // this can happen when the document is created; we silently ignore it
2906 return;
2907 }
2908 Persp3D *persp = sel_persps.front();
2910 bool set_infinite = gtk_toggle_action_get_active(act);
2911 persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2912 }
2914 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2915 {
2916 box3d_vp_state_changed(act, box3d_angle, Proj::X);
2917 }
2919 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2920 {
2921 box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2922 }
2924 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2925 {
2926 box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2927 }
2929 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2930 {
2931 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2932 EgeAdjustmentAction* eact = 0;
2933 SPDocument *document = sp_desktop_document (desktop);
2934 Persp3D *persp = document->current_persp3d;
2936 EgeAdjustmentAction* box3d_angle_x = 0;
2937 EgeAdjustmentAction* box3d_angle_y = 0;
2938 EgeAdjustmentAction* box3d_angle_z = 0;
2940 /* Angle X */
2941 {
2942 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2943 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2944 eact = create_adjustment_action( "3DBoxAngleXAction",
2945 _("Angle in X direction"), _("Angle X:"),
2946 // Translators: PL is short for 'perspective line'
2947 _("Angle of PLs in X direction"),
2948 "tools.shapes.3dbox", "box3d_angle_x", 30,
2949 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2950 -360.0, 360.0, 1.0, 10.0,
2951 labels, values, G_N_ELEMENTS(labels),
2952 box3d_angle_x_value_changed );
2953 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2954 g_object_set_data( holder, "box3d_angle_x_action", eact );
2955 box3d_angle_x = eact;
2956 }
2958 if (!persp3d_VP_is_finite(persp, Proj::X)) {
2959 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2960 } else {
2961 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2962 }
2965 /* VP X state */
2966 {
2967 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2968 // Translators: VP is short for 'vanishing point'
2969 _("State of VP in X direction"),
2970 _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2971 "toggle_vp_x",
2972 Inkscape::ICON_SIZE_DECORATION );
2973 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2974 g_object_set_data( holder, "box3d_vp_x_state_action", act );
2975 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2976 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs->getBool("tools.shapes.3dbox", "vp_x_state", true) );
2977 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("tools.shapes.3dbox", "vp_x_state", true) );
2978 }
2980 /* Angle Y */
2981 {
2982 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2983 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2984 eact = create_adjustment_action( "3DBoxAngleYAction",
2985 _("Angle in Y direction"), _("Angle Y:"),
2986 // Translators: PL is short for 'perspective line'
2987 _("Angle of PLs in Y direction"),
2988 "tools.shapes.3dbox", "box3d_angle_y", 30,
2989 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2990 -360.0, 360.0, 1.0, 10.0,
2991 labels, values, G_N_ELEMENTS(labels),
2992 box3d_angle_y_value_changed );
2993 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2994 g_object_set_data( holder, "box3d_angle_y_action", eact );
2995 box3d_angle_y = eact;
2996 }
2998 if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2999 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3000 } else {
3001 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3002 }
3004 /* VP Y state */
3005 {
3006 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
3007 // Translators: VP is short for 'vanishing point'
3008 _("State of VP in Y direction"),
3009 _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
3010 "toggle_vp_y",
3011 Inkscape::ICON_SIZE_DECORATION );
3012 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3013 g_object_set_data( holder, "box3d_vp_y_state_action", act );
3014 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
3015 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs->getBool("tools.shapes.3dbox", "vp_y_state", true) );
3016 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("tools.shapes.3dbox", "vp_y_state", true) );
3017 }
3019 /* Angle Z */
3020 {
3021 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3022 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3023 eact = create_adjustment_action( "3DBoxAngleZAction",
3024 _("Angle in Z direction"), _("Angle Z:"),
3025 // Translators: PL is short for 'perspective line'
3026 _("Angle of PLs in Z direction"),
3027 "tools.shapes.3dbox", "box3d_angle_z", 30,
3028 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3029 -360.0, 360.0, 1.0, 10.0,
3030 labels, values, G_N_ELEMENTS(labels),
3031 box3d_angle_z_value_changed );
3032 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3033 g_object_set_data( holder, "box3d_angle_z_action", eact );
3034 box3d_angle_z = eact;
3035 }
3037 if (!persp3d_VP_is_finite(persp, Proj::Z)) {
3038 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3039 } else {
3040 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3041 }
3043 /* VP Z state */
3044 {
3045 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3046 // Translators: VP is short for 'vanishing point'
3047 _("State of VP in Z direction"),
3048 _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3049 "toggle_vp_z",
3050 Inkscape::ICON_SIZE_DECORATION );
3051 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3052 g_object_set_data( holder, "box3d_vp_z_state_action", act );
3053 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3054 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs->getBool("tools.shapes.3dbox", "vp_z_state", true) );
3055 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("tools.shapes.3dbox", "vp_z_state", true) );
3056 }
3058 sigc::connection *connection = new sigc::connection(
3059 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3060 );
3061 g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3062 g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3063 }
3065 //########################
3066 //## Spiral ##
3067 //########################
3069 static void
3070 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
3071 {
3072 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3074 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3075 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3076 prefs->setDouble("tools.shapes.spiral", value_name, adj->value);
3077 }
3079 // quit if run by the attr_changed listener
3080 if (g_object_get_data( tbl, "freeze" )) {
3081 return;
3082 }
3084 // in turn, prevent listener from responding
3085 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3087 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3089 bool modmade = false;
3090 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3091 items != NULL;
3092 items = items->next)
3093 {
3094 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3095 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3096 sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3097 SP_OBJECT((SPItem *) items->data)->updateRepr();
3098 modmade = true;
3099 }
3100 }
3102 g_free(namespaced_name);
3104 if (modmade) {
3105 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3106 _("Change spiral"));
3107 }
3109 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3110 }
3112 static void
3113 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3114 {
3115 sp_spl_tb_value_changed(adj, tbl, "revolution");
3116 }
3118 static void
3119 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3120 {
3121 sp_spl_tb_value_changed(adj, tbl, "expansion");
3122 }
3124 static void
3125 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3126 {
3127 sp_spl_tb_value_changed(adj, tbl, "t0");
3128 }
3130 static void
3131 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3132 {
3133 GtkWidget *tbl = GTK_WIDGET(obj);
3135 GtkAdjustment *adj;
3137 // fixme: make settable
3138 gdouble rev = 5;
3139 gdouble exp = 1.0;
3140 gdouble t0 = 0.0;
3142 adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3143 gtk_adjustment_set_value(adj, rev);
3144 gtk_adjustment_value_changed(adj);
3146 adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3147 gtk_adjustment_set_value(adj, exp);
3148 gtk_adjustment_value_changed(adj);
3150 adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3151 gtk_adjustment_set_value(adj, t0);
3152 gtk_adjustment_value_changed(adj);
3154 spinbutton_defocus(GTK_OBJECT(tbl));
3155 }
3158 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3159 gchar const */*old_value*/, gchar const */*new_value*/,
3160 bool /*is_interactive*/, gpointer data)
3161 {
3162 GtkWidget *tbl = GTK_WIDGET(data);
3164 // quit if run by the _changed callbacks
3165 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3166 return;
3167 }
3169 // in turn, prevent callbacks from responding
3170 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3172 GtkAdjustment *adj;
3173 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3174 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3176 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3177 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3179 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3180 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3182 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3183 }
3186 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3187 NULL, /* child_added */
3188 NULL, /* child_removed */
3189 spiral_tb_event_attr_changed,
3190 NULL, /* content_changed */
3191 NULL /* order_changed */
3192 };
3194 static void
3195 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3196 {
3197 int n_selected = 0;
3198 Inkscape::XML::Node *repr = NULL;
3200 purge_repr_listener( tbl, tbl );
3202 for (GSList const *items = selection->itemList();
3203 items != NULL;
3204 items = items->next)
3205 {
3206 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3207 n_selected++;
3208 repr = SP_OBJECT_REPR((SPItem *) items->data);
3209 }
3210 }
3212 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3214 if (n_selected == 0) {
3215 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3216 } else if (n_selected == 1) {
3217 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3219 if (repr) {
3220 g_object_set_data( tbl, "repr", repr );
3221 Inkscape::GC::anchor(repr);
3222 sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3223 sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3224 }
3225 } else {
3226 // FIXME: implement averaging of all parameters for multiple selected
3227 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3228 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3229 }
3230 }
3233 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3234 {
3235 EgeAdjustmentAction* eact = 0;
3236 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3238 {
3239 EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3240 ege_output_action_set_use_markup( act, TRUE );
3241 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3242 g_object_set_data( holder, "mode_action", act );
3243 }
3245 /* Revolution */
3246 {
3247 gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3248 gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3249 eact = create_adjustment_action( "SpiralRevolutionAction",
3250 _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3251 "tools.shapes.spiral", "revolution", 3.0,
3252 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3253 0.01, 1024.0, 0.1, 1.0,
3254 labels, values, G_N_ELEMENTS(labels),
3255 sp_spl_tb_revolution_value_changed, 1, 2);
3256 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3257 }
3259 /* Expansion */
3260 {
3261 gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3262 gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3263 eact = create_adjustment_action( "SpiralExpansionAction",
3264 _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3265 "tools.shapes.spiral", "expansion", 1.0,
3266 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3267 0.0, 1000.0, 0.01, 1.0,
3268 labels, values, G_N_ELEMENTS(labels),
3269 sp_spl_tb_expansion_value_changed);
3270 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3271 }
3273 /* T0 */
3274 {
3275 gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3276 gdouble values[] = {0, 0.5, 0.9};
3277 eact = create_adjustment_action( "SpiralT0Action",
3278 _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3279 "tools.shapes.spiral", "t0", 0.0,
3280 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3281 0.0, 0.999, 0.01, 1.0,
3282 labels, values, G_N_ELEMENTS(labels),
3283 sp_spl_tb_t0_value_changed);
3284 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3285 }
3287 /* Reset */
3288 {
3289 InkAction* inky = ink_action_new( "SpiralResetAction",
3290 _("Defaults"),
3291 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3292 GTK_STOCK_CLEAR,
3293 secondarySize );
3294 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3295 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3296 }
3299 sigc::connection *connection = new sigc::connection(
3300 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3301 );
3302 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3303 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3304 }
3306 //########################
3307 //## Pen/Pencil ##
3308 //########################
3310 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3311 static char const *
3312 freehand_tool_name(GObject *dataKludge)
3313 {
3314 SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3315 return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3316 ? "tools.freehand.pen"
3317 : "tools.freehand.pencil" );
3318 }
3320 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3321 {
3322 gint mode = ege_select_one_action_get_active(act);
3324 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3325 prefs->setInt(freehand_tool_name(tbl), "freehand-mode", mode);
3327 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3329 // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3330 // preparatory work here
3331 if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3332 SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3333 sp_pen_context_set_polyline_mode(pc);
3334 }
3335 }
3337 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3338 {
3339 /* Freehand mode toggle buttons */
3340 {
3341 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3342 guint freehandMode = prefs->getInt(( tool_is_pencil ? "tools.freehand.pencil" : "tools.freehand.pen" ), "freehand-mode", 0);
3343 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3345 {
3346 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3348 GtkTreeIter iter;
3349 gtk_list_store_append( model, &iter );
3350 gtk_list_store_set( model, &iter,
3351 0, _("Bezier"),
3352 1, _("Create regular Bezier path"),
3353 2, "bezier_mode",
3354 -1 );
3356 gtk_list_store_append( model, &iter );
3357 gtk_list_store_set( model, &iter,
3358 0, _("Spiro"),
3359 1, _("Create Spiro path"),
3360 2, "spiro_splines_mode",
3361 -1 );
3363 if (!tool_is_pencil) {
3364 gtk_list_store_append( model, &iter );
3365 gtk_list_store_set( model, &iter,
3366 0, _("Zigzag"),
3367 1, _("Create a sequence of straight line segments"),
3368 2, "polylines_mode",
3369 -1 );
3371 gtk_list_store_append( model, &iter );
3372 gtk_list_store_set( model, &iter,
3373 0, _("Paraxial"),
3374 1, _("Create a sequence of paraxial line segments"),
3375 2, "paraxial_lines_mode",
3376 -1 );
3377 }
3379 EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3380 "FreehandModeActionPencil" :
3381 "FreehandModeActionPen",
3382 (_("Mode:")), ("Mode"), NULL, GTK_TREE_MODEL(model) );
3383 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3385 ege_select_one_action_set_appearance( act, "full" );
3386 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3387 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3388 ege_select_one_action_set_icon_column( act, 2 );
3389 ege_select_one_action_set_icon_size( act, secondarySize );
3390 ege_select_one_action_set_tooltip_column( act, 1 );
3392 ege_select_one_action_set_active( act, freehandMode);
3393 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3394 }
3395 }
3396 }
3398 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3399 gint shape = ege_select_one_action_get_active( act );
3400 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3401 prefs->setInt(freehand_tool_name(dataKludge), "shape", shape);
3402 }
3404 /**
3405 * \brief Generate the list of freehand advanced shape option entries.
3406 */
3407 GList * freehand_shape_dropdown_items_list() {
3408 GList *glist = NULL;
3410 glist = g_list_append (glist, _("None"));
3411 glist = g_list_append (glist, _("Triangle in"));
3412 glist = g_list_append (glist, _("Triangle out"));
3413 glist = g_list_append (glist, _("Ellipse"));
3414 glist = g_list_append (glist, _("From clipboard"));
3416 return glist;
3417 }
3419 static void
3420 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3421 /*advanced shape options */
3422 {
3423 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3424 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3426 GList* items = 0;
3427 gint count = 0;
3428 for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3429 {
3430 GtkTreeIter iter;
3431 gtk_list_store_append( model, &iter );
3432 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3433 count++;
3434 }
3435 g_list_free( items );
3436 items = 0;
3437 EgeSelectOneAction* act1 = ege_select_one_action_new(
3438 tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3439 _("Shape:"), ("Shape"), NULL, GTK_TREE_MODEL(model));
3440 g_object_set( act1, "short_label", _("Shape:"), NULL );
3441 ege_select_one_action_set_appearance( act1, "compact" );
3442 ege_select_one_action_set_active( act1, prefs->getInt(( tool_is_pencil ? "tools.freehand.pencil" : "tools.freehand.pen" ), "shape", 0) );
3443 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3444 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3445 g_object_set_data( holder, "shape_action", act1 );
3446 }
3447 }
3449 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3450 {
3451 sp_add_freehand_mode_toggle(mainActions, holder, false);
3452 freehand_add_advanced_shape_options(mainActions, holder, false);
3453 }
3456 static void
3457 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3458 {
3459 GtkWidget *tbl = GTK_WIDGET(obj);
3461 GtkAdjustment *adj;
3463 // fixme: make settable
3464 gdouble tolerance = 4;
3466 adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3467 gtk_adjustment_set_value(adj, tolerance);
3468 gtk_adjustment_value_changed(adj);
3470 spinbutton_defocus(GTK_OBJECT(tbl));
3471 }
3473 static void
3474 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3475 {
3477 // quit if run by the attr_changed listener
3478 if (g_object_get_data( tbl, "freeze" )) {
3479 return;
3480 }
3481 // in turn, prevent listener from responding
3482 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3483 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3484 prefs->setDouble("tools.freehand.pencil", "tolerance", adj->value);
3485 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3487 }
3491 static void
3492 sp_pencil_tb_tolerance_value_changed_external(Inkscape::XML::Node */*repr*/,
3493 const gchar */*key*/,
3494 const gchar */*oldval*/,
3495 const gchar */*newval*/,
3496 bool /*is_interactive*/,
3497 void * data)
3498 {
3499 GObject* tbl = G_OBJECT(data);
3500 if (g_object_get_data( tbl, "freeze" )) {
3501 return;
3502 }
3504 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3506 GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl,
3507 "tolerance");
3509 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3510 double v = prefs->getDouble("tools.freehand.pencil", "tolerance", adj->value);
3511 gtk_adjustment_set_value(adj, v);
3512 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3514 }
3516 static Inkscape::XML::NodeEventVector pencil_node_events =
3517 {
3518 NULL,
3519 NULL,
3520 sp_pencil_tb_tolerance_value_changed_external,
3521 NULL,
3522 NULL,
3523 };
3526 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3527 {
3528 sp_add_freehand_mode_toggle(mainActions, holder, true);
3530 EgeAdjustmentAction* eact = 0;
3532 /* Tolerance */
3533 {
3534 gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
3535 gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
3536 eact = create_adjustment_action( "PencilToleranceAction",
3537 _("Smoothing:"), _("Smoothing: "),
3538 _("How much smoothing (simplifying) is applied to the line"),
3539 "tools.freehand.pencil", "tolerance",
3540 3.0,
3541 GTK_WIDGET(desktop->canvas), NULL,
3542 holder, TRUE, "altx-pencil",
3543 1, 100.0, 0.5, 0,
3544 labels, values, G_N_ELEMENTS(labels),
3545 sp_pencil_tb_tolerance_value_changed,
3546 1, 2);
3547 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3548 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3550 Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE,
3551 "tools.freehand.pencil");
3552 repr->addListener(&pencil_node_events, G_OBJECT(holder));
3553 g_object_set_data(G_OBJECT(holder), "repr", repr);
3555 }
3557 /* advanced shape options */
3558 freehand_add_advanced_shape_options(mainActions, holder, true);
3560 /* Reset */
3561 {
3562 InkAction* inky = ink_action_new( "PencilResetAction",
3563 _("Defaults"),
3564 _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3565 GTK_STOCK_CLEAR,
3566 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3567 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
3568 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3569 }
3571 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3573 }
3576 //########################
3577 //## Tweak ##
3578 //########################
3580 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3581 {
3582 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3583 prefs->setDouble( "tools.tweak", "width", adj->value * 0.01 );
3584 }
3586 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3587 {
3588 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3589 prefs->setDouble( "tools.tweak", "force", adj->value * 0.01 );
3590 }
3592 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3593 {
3594 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3595 prefs->setBool("tools.tweak", "usepressure", gtk_toggle_action_get_active(act));
3596 }
3598 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3599 {
3600 int mode = ege_select_one_action_get_active( act );
3601 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3602 prefs->setInt("tools.tweak", "mode", mode);
3604 GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3605 GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3606 GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3607 GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3608 GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3609 GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3610 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3611 if (doh) gtk_action_set_sensitive (doh, TRUE);
3612 if (dos) gtk_action_set_sensitive (dos, TRUE);
3613 if (dol) gtk_action_set_sensitive (dol, TRUE);
3614 if (doo) gtk_action_set_sensitive (doo, TRUE);
3615 if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3616 if (fid) gtk_action_set_sensitive (fid, FALSE);
3617 } else {
3618 if (doh) gtk_action_set_sensitive (doh, FALSE);
3619 if (dos) gtk_action_set_sensitive (dos, FALSE);
3620 if (dol) gtk_action_set_sensitive (dol, FALSE);
3621 if (doo) gtk_action_set_sensitive (doo, FALSE);
3622 if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3623 if (fid) gtk_action_set_sensitive (fid, TRUE);
3624 }
3625 }
3627 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3628 {
3629 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3630 prefs->setDouble( "tools.tweak", "fidelity", adj->value * 0.01 );
3631 }
3633 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3634 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3635 prefs->setBool("tools.tweak", "doh", gtk_toggle_action_get_active(act));
3636 }
3637 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3638 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3639 prefs->setBool("tools.tweak", "dos", gtk_toggle_action_get_active(act));
3640 }
3641 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3642 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3643 prefs->setBool("tools.tweak", "dol", gtk_toggle_action_get_active(act));
3644 }
3645 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3646 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3647 prefs->setBool("tools.tweak", "doo", gtk_toggle_action_get_active(act));
3648 }
3650 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3651 {
3652 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3653 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3655 {
3656 /* Width */
3657 gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3658 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3659 EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3660 _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3661 "tools.tweak", "width", 15,
3662 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3663 1, 100, 1.0, 0.0,
3664 labels, values, G_N_ELEMENTS(labels),
3665 sp_tweak_width_value_changed, 0.01, 0, 100 );
3666 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3667 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3668 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3669 }
3672 {
3673 /* Force */
3674 gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3675 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3676 EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3677 _("Force"), _("Force:"), _("The force of the tweak action"),
3678 "tools.tweak", "force", 20,
3679 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3680 1, 100, 1.0, 0.0,
3681 labels, values, G_N_ELEMENTS(labels),
3682 sp_tweak_force_value_changed, 0.01, 0, 100 );
3683 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3684 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3685 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3686 }
3688 /* Mode */
3689 {
3690 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3692 GtkTreeIter iter;
3693 gtk_list_store_append( model, &iter );
3694 gtk_list_store_set( model, &iter,
3695 0, _("Push mode"),
3696 1, _("Push parts of paths in any direction"),
3697 2, "tweak_push_mode",
3698 -1 );
3700 gtk_list_store_append( model, &iter );
3701 gtk_list_store_set( model, &iter,
3702 0, _("Shrink mode"),
3703 1, _("Shrink (inset) parts of paths"),
3704 2, "tweak_shrink_mode",
3705 -1 );
3707 gtk_list_store_append( model, &iter );
3708 gtk_list_store_set( model, &iter,
3709 0, _("Grow mode"),
3710 1, _("Grow (outset) parts of paths"),
3711 2, "tweak_grow_mode",
3712 -1 );
3714 gtk_list_store_append( model, &iter );
3715 gtk_list_store_set( model, &iter,
3716 0, _("Attract mode"),
3717 1, _("Attract parts of paths towards cursor"),
3718 2, "tweak_attract_mode",
3719 -1 );
3721 gtk_list_store_append( model, &iter );
3722 gtk_list_store_set( model, &iter,
3723 0, _("Repel mode"),
3724 1, _("Repel parts of paths from cursor"),
3725 2, "tweak_repel_mode",
3726 -1 );
3728 gtk_list_store_append( model, &iter );
3729 gtk_list_store_set( model, &iter,
3730 0, _("Roughen mode"),
3731 1, _("Roughen parts of paths"),
3732 2, "tweak_roughen_mode",
3733 -1 );
3735 gtk_list_store_append( model, &iter );
3736 gtk_list_store_set( model, &iter,
3737 0, _("Color paint mode"),
3738 1, _("Paint the tool's color upon selected objects"),
3739 2, "tweak_colorpaint_mode",
3740 -1 );
3742 gtk_list_store_append( model, &iter );
3743 gtk_list_store_set( model, &iter,
3744 0, _("Color jitter mode"),
3745 1, _("Jitter the colors of selected objects"),
3746 2, "tweak_colorjitter_mode",
3747 -1 );
3749 EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3750 g_object_set( act, "short_label", _("Mode:"), NULL );
3751 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3752 g_object_set_data( holder, "mode_action", act );
3754 ege_select_one_action_set_appearance( act, "full" );
3755 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3756 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3757 ege_select_one_action_set_icon_column( act, 2 );
3758 ege_select_one_action_set_icon_size( act, secondarySize );
3759 ege_select_one_action_set_tooltip_column( act, 1 );
3761 gint mode = prefs->getInt("tools.tweak", "mode", 0);
3762 ege_select_one_action_set_active( act, mode );
3763 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3765 g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3766 }
3768 guint mode = prefs->getInt("tools.tweak", "mode", 0);
3770 {
3771 EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3772 ege_output_action_set_use_markup( act, TRUE );
3773 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3774 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3775 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3776 g_object_set_data( holder, "tweak_channels_label", act);
3777 }
3779 {
3780 InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3781 _("Hue"),
3782 _("In color mode, act on objects' hue"),
3783 NULL,
3784 Inkscape::ICON_SIZE_DECORATION );
3785 //TRANSLATORS: "H" here stands for hue
3786 g_object_set( act, "short_label", _("H"), NULL );
3787 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3788 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3789 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getInt( "tools.tweak", "doh", 1 ) );
3790 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3791 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3792 g_object_set_data( holder, "tweak_doh", act);
3793 }
3794 {
3795 InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3796 _("Saturation"),
3797 _("In color mode, act on objects' saturation"),
3798 NULL,
3799 Inkscape::ICON_SIZE_DECORATION );
3800 //TRANSLATORS: "S" here stands for Saturation
3801 g_object_set( act, "short_label", _("S"), NULL );
3802 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3803 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3804 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getInt( "tools.tweak", "dos", 1 ) );
3805 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3806 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3807 g_object_set_data( holder, "tweak_dos", act );
3808 }
3809 {
3810 InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3811 _("Lightness"),
3812 _("In color mode, act on objects' lightness"),
3813 NULL,
3814 Inkscape::ICON_SIZE_DECORATION );
3815 //TRANSLATORS: "L" here stands for Lightness
3816 g_object_set( act, "short_label", _("L"), NULL );
3817 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3818 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3819 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getInt( "tools.tweak", "dol", 1 ) );
3820 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3821 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3822 g_object_set_data( holder, "tweak_dol", act );
3823 }
3824 {
3825 InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3826 _("Opacity"),
3827 _("In color mode, act on objects' opacity"),
3828 NULL,
3829 Inkscape::ICON_SIZE_DECORATION );
3830 //TRANSLATORS: "O" here stands for Opacity
3831 g_object_set( act, "short_label", _("O"), NULL );
3832 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3833 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3834 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getInt( "tools.tweak", "doo", 1 ) );
3835 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3836 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3837 g_object_set_data( holder, "tweak_doo", act );
3838 }
3840 { /* Fidelity */
3841 gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3842 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3843 EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3844 _("Fidelity"), _("Fidelity:"),
3845 _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3846 "tools.tweak", "fidelity", 50,
3847 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3848 1, 100, 1.0, 10.0,
3849 labels, values, G_N_ELEMENTS(labels),
3850 sp_tweak_fidelity_value_changed, 0.01, 0, 100 );
3851 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3852 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3853 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3854 gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3855 g_object_set_data( holder, "tweak_fidelity", eact );
3856 }
3859 /* Use Pressure button */
3860 {
3861 InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3862 _("Pressure"),
3863 _("Use the pressure of the input device to alter the force of tweak action"),
3864 "use_pressure",
3865 Inkscape::ICON_SIZE_DECORATION );
3866 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3867 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3868 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("tools.tweak", "usepressure", true) );
3869 }
3871 }
3874 //########################
3875 //## Calligraphy ##
3876 //########################
3877 static void update_presets_list (GObject *tbl)
3878 {
3879 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3880 if (g_object_get_data(tbl, "presets_blocked"))
3881 return;
3883 EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3884 if (!sel) {
3885 ege_select_one_action_set_active(sel, 0);
3886 return;
3887 }
3889 int total_prefs = prefs->childCount("tools.calligraphic.preset");
3891 for (int i = 0; i < total_prefs; ++i) {
3892 Glib::ustring preset_path = prefs->getNthChild("tools.calligraphic.preset", i);
3893 /// @todo Remove the use of _getNode()
3894 Inkscape::XML::Node *preset_repr = prefs->_getNode(preset_path);
3896 bool match = true;
3898 for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
3899 iter;
3900 ++iter ) {
3901 const gchar *attr_name = g_quark_to_string(iter->key);
3902 if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
3903 continue;
3904 void *widget = g_object_get_data(tbl, attr_name);
3905 if (widget) {
3906 if (GTK_IS_ADJUSTMENT(widget)) {
3907 double v = prefs->getDouble(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
3908 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
3909 //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
3910 if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
3911 match = false;
3912 break;
3913 }
3914 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
3915 bool v = prefs->getBool(preset_path, attr_name);
3916 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
3917 //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
3918 if ( static_cast<bool>(gtk_toggle_action_get_active(toggle)) != v ) {
3919 match = false;
3920 break;
3921 }
3922 }
3923 }
3924 }
3926 if (match) {
3927 // newly added item is at the same index as the
3928 // save command, so we need to change twice for it to take effect
3929 ege_select_one_action_set_active(sel, 0);
3930 ege_select_one_action_set_active(sel, i);
3931 return;
3932 }
3933 }
3935 // no match found
3936 ege_select_one_action_set_active(sel, 0);
3937 }
3939 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3940 {
3941 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3942 prefs->setDouble( "tools.calligraphic", "mass", adj->value * 0.01 );
3943 update_presets_list(tbl);
3944 }
3946 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3947 {
3948 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3949 prefs->setDouble( "tools.calligraphic", "wiggle", adj->value * 0.01 );
3950 update_presets_list(tbl);
3951 }
3953 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3954 {
3955 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3956 prefs->setDouble( "tools.calligraphic", "angle", adj->value );
3957 update_presets_list(tbl);
3958 }
3960 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3961 {
3962 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3963 prefs->setDouble( "tools.calligraphic", "width", adj->value * 0.01 );
3964 update_presets_list(tbl);
3965 }
3967 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3968 {
3969 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3970 prefs->setDouble("tools.calligraphic", "thinning", adj->value * 0.01 );
3971 update_presets_list(tbl);
3972 }
3974 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3975 {
3976 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3977 prefs->setDouble( "tools.calligraphic", "flatness", adj->value * 0.01);
3978 update_presets_list(tbl);
3979 }
3981 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3982 {
3983 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3984 prefs->setDouble( "tools.calligraphic", "tremor", adj->value * 0.01 );
3985 update_presets_list(tbl);
3986 }
3988 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3989 {
3990 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3991 prefs->setDouble( "tools.calligraphic", "cap_rounding", adj->value );
3992 update_presets_list(tbl);
3993 }
3995 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject* tbl )
3996 {
3997 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3998 prefs->setBool("tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ));
3999 update_presets_list(tbl);
4000 }
4002 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject* tbl )
4003 {
4004 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4005 prefs->setBool("tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ));
4006 update_presets_list(tbl);
4007 }
4009 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject* tbl )
4010 {
4011 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4012 GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
4013 prefs->setBool("tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ));
4014 update_presets_list(tbl);
4015 if (calligraphy_angle )
4016 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
4017 }
4020 static gchar const *const widget_names[] = {
4021 "width",
4022 "mass",
4023 "wiggle",
4024 "angle",
4025 "thinning",
4026 "tremor",
4027 "flatness",
4028 "cap_rounding",
4029 "usepressure",
4030 "tracebackground",
4031 "usetilt"
4032 };
4035 static void sp_dcc_build_presets_list(GObject *tbl)
4036 {
4037 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4039 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4040 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4041 gtk_list_store_clear (model);
4043 {
4044 GtkTreeIter iter;
4045 gtk_list_store_append( model, &iter );
4046 gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4047 }
4049 /// @todo Use public Preferences API instead of node manipulation
4050 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4051 Inkscape::XML::Node *repr = prefs->_getNode("tools.calligraphic.preset", true);
4052 Inkscape::XML::Node *child_repr = repr->firstChild();
4053 int ii=1;
4054 while (child_repr) {
4055 GtkTreeIter iter;
4056 char *preset_name = (char *) child_repr->attribute("name");
4057 gtk_list_store_append( model, &iter );
4058 gtk_list_store_set( model, &iter, 0, preset_name, 1, ii++, -1 );
4059 child_repr = child_repr->next();
4060 }
4062 {
4063 GtkTreeIter iter;
4064 gtk_list_store_append( model, &iter );
4065 gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4066 g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4067 }
4069 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4071 update_presets_list (tbl);
4072 }
4074 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4075 {
4076 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4077 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4078 if (! desktop) return;
4080 if (g_object_get_data(tbl, "presets_blocked"))
4081 return;
4083 Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
4084 if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) {
4085 // dialog cancelled
4086 update_presets_list (tbl);
4087 return;
4088 }
4089 Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
4091 if (profile_name.empty()) {
4092 // empty name entered
4093 update_presets_list (tbl);
4094 return;
4095 }
4097 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4099 int new_index = -1;
4100 Glib::ustring pref_path;
4101 int total_prefs = prefs->childCount("tools.calligraphic.preset");
4103 for (int i = 0; i < total_prefs; ++i) {
4104 Glib::ustring path = prefs->getNthChild("tools.calligraphic.preset", i);
4105 Glib::ustring name = prefs->getString(path, "name");
4106 if (!name.empty() && ( profile_name == name )) {
4107 // we already have preset with this name, replace its values
4108 new_index = i;
4109 pref_path = path;
4110 break;
4111 }
4112 }
4114 if (new_index == -1) {
4115 // no preset with this name, create
4116 /// @todo This is wrong, the name should be encoded in the key
4117 /// to allow deletion of presets
4118 new_index = total_prefs + 1;
4119 gchar *profile_id = g_strdup_printf(".dcc%d", new_index);
4120 pref_path = Glib::ustring("tools.calligraphic.preset") + profile_id;
4121 g_free(profile_id);
4122 }
4124 for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4125 gchar const *const widget_name = widget_names[i];
4126 void *widget = g_object_get_data(tbl, widget_name);
4127 if (widget) {
4128 if (GTK_IS_ADJUSTMENT(widget)) {
4129 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4130 double v = gtk_adjustment_get_value(adj);
4131 prefs->setDouble(pref_path, widget_name, v);
4132 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4133 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4134 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4135 bool v = gtk_toggle_action_get_active(toggle);
4136 prefs->setBool(pref_path, widget_name, v);
4137 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4138 } else {
4139 g_warning("Unknown widget type for preset: %s\n", widget_name);
4140 }
4141 } else {
4142 g_warning("Bad key when writing preset: %s\n", widget_name);
4143 }
4144 }
4145 prefs->setString(pref_path, "name", profile_name);
4147 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4148 sp_dcc_build_presets_list (tbl);
4149 }
4152 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4154 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4156 gint preset_index = ege_select_one_action_get_active( act );
4157 gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4159 if (preset_index == save_presets_index) {
4160 // this is the Save command
4161 sp_dcc_save_profile(NULL, tbl);
4162 return;
4163 }
4165 if (g_object_get_data(tbl, "presets_blocked"))
4166 return;
4168 // preset_index is one-based so we subtract 1
4169 Glib::ustring preset_path = prefs->getNthChild("tools.calligraphic.preset", preset_index - 1);
4171 if (!preset_path.empty()) {
4172 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
4174 /// @todo Remove the use of _getNode() in this fragment, modify
4175 /// the public interface of Preferences if necessary
4176 Inkscape::XML::Node *preset_repr = prefs->_getNode(preset_path);
4178 // Shouldn't this be std::map?
4179 for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
4180 iter;
4181 ++iter ) {
4182 const gchar *attr_name = g_quark_to_string(iter->key);
4183 if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
4184 continue;
4185 void *widget = g_object_get_data(tbl, attr_name);
4186 if (widget) {
4187 if (GTK_IS_ADJUSTMENT(widget)) {
4188 double v = prefs->getDouble(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4189 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4190 gtk_adjustment_set_value(adj, v);
4191 //std::cout << "set adj " << attr_name << " to " << v << "\n";
4192 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4193 int v = prefs->getInt(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4194 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4195 gtk_toggle_action_set_active(toggle, v);
4196 //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4197 } else {
4198 g_warning("Unknown widget type for preset: %s\n", attr_name);
4199 }
4200 } else {
4201 g_warning("Bad key found in a preset record: %s\n", attr_name);
4202 }
4203 }
4204 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4205 }
4207 }
4210 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4211 {
4212 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4213 {
4214 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4216 EgeAdjustmentAction* calligraphy_angle = 0;
4218 {
4219 /* Width */
4220 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4221 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4222 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4223 _("Pen Width"), _("Width:"),
4224 _("The width of the calligraphic pen (relative to the visible canvas area)"),
4225 "tools.calligraphic", "width", 15,
4226 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4227 1, 100, 1.0, 0.0,
4228 labels, values, G_N_ELEMENTS(labels),
4229 sp_ddc_width_value_changed, 0.01, 0, 100 );
4230 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4231 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4232 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4233 }
4235 {
4236 /* Thinning */
4237 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4238 gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4239 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4240 _("Stroke Thinning"), _("Thinning:"),
4241 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4242 "tools.calligraphic", "thinning", 10,
4243 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4244 -100, 100, 1, 0.1,
4245 labels, values, G_N_ELEMENTS(labels),
4246 sp_ddc_velthin_value_changed, 0.01, 0, 100);
4247 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4248 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4249 }
4251 {
4252 /* Angle */
4253 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4254 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4255 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4256 _("Pen Angle"), _("Angle:"),
4257 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4258 "tools.calligraphic", "angle", 30,
4259 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4260 -90.0, 90.0, 1.0, 10.0,
4261 labels, values, G_N_ELEMENTS(labels),
4262 sp_ddc_angle_value_changed, 1, 0 );
4263 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4264 g_object_set_data( holder, "angle_action", eact );
4265 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4266 calligraphy_angle = eact;
4267 }
4269 {
4270 /* Fixation */
4271 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4272 gdouble values[] = {0, 20, 40, 60, 90, 100};
4273 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4274 _("Fixation"), _("Fixation:"),
4275 _("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"),
4276 "tools.calligraphic", "flatness", 90,
4277 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4278 0.0, 100, 1.0, 10.0,
4279 labels, values, G_N_ELEMENTS(labels),
4280 sp_ddc_flatness_value_changed, 0.01, 0, 100 );
4281 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4282 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4283 }
4285 {
4286 /* Cap Rounding */
4287 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4288 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4289 // TRANSLATORS: "cap" means "end" (both start and finish) here
4290 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4291 _("Cap rounding"), _("Caps:"),
4292 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4293 "tools.calligraphic", "cap_rounding", 0.0,
4294 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4295 0.0, 5.0, 0.01, 0.1,
4296 labels, values, G_N_ELEMENTS(labels),
4297 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4298 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4299 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4300 }
4302 {
4303 /* Tremor */
4304 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4305 gdouble values[] = {0, 10, 20, 40, 60, 100};
4306 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4307 _("Stroke Tremor"), _("Tremor:"),
4308 _("Increase to make strokes rugged and trembling"),
4309 "tools.calligraphic", "tremor", 0.0,
4310 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4311 0.0, 100, 1, 0.0,
4312 labels, values, G_N_ELEMENTS(labels),
4313 sp_ddc_tremor_value_changed, 0.01, 0, 100 );
4315 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4316 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4317 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4318 }
4320 {
4321 /* Wiggle */
4322 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4323 gdouble values[] = {0, 20, 40, 60, 100};
4324 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4325 _("Pen Wiggle"), _("Wiggle:"),
4326 _("Increase to make the pen waver and wiggle"),
4327 "tools.calligraphic", "wiggle", 0.0,
4328 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4329 0.0, 100, 1, 0.0,
4330 labels, values, G_N_ELEMENTS(labels),
4331 sp_ddc_wiggle_value_changed, 0.01, 0, 100 );
4332 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4333 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4334 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4335 }
4337 {
4338 /* Mass */
4339 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4340 gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4341 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4342 _("Pen Mass"), _("Mass:"),
4343 _("Increase to make the pen drag behind, as if slowed by inertia"),
4344 "tools.calligraphic", "mass", 2.0,
4345 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4346 0.0, 100, 1, 0.0,
4347 labels, values, G_N_ELEMENTS(labels),
4348 sp_ddc_mass_value_changed, 0.01, 0, 100 );
4349 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4350 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4351 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4352 }
4355 /* Trace Background button */
4356 {
4357 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4358 _("Trace Background"),
4359 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4360 "trace_background",
4361 Inkscape::ICON_SIZE_DECORATION );
4362 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4363 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4364 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("tools.calligraphic", "tracebackground", false) );
4365 g_object_set_data( holder, "tracebackground", act );
4366 }
4368 /* Use Pressure button */
4369 {
4370 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4371 _("Pressure"),
4372 _("Use the pressure of the input device to alter the width of the pen"),
4373 "use_pressure",
4374 Inkscape::ICON_SIZE_DECORATION );
4375 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4376 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4377 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("tools.calligraphic", "usepressure", true) );
4378 g_object_set_data( holder, "usepressure", act );
4379 }
4381 /* Use Tilt button */
4382 {
4383 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4384 _("Tilt"),
4385 _("Use the tilt of the input device to alter the angle of the pen's nib"),
4386 "use_tilt",
4387 Inkscape::ICON_SIZE_DECORATION );
4388 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4389 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4390 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs->getBool("tools.calligraphic", "usetilt", true) );
4391 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("tools.calligraphic", "usetilt", true) );
4392 g_object_set_data( holder, "usetilt", act );
4393 }
4395 /*calligraphic profile */
4396 {
4397 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
4398 EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
4399 ege_select_one_action_set_appearance (act1, "compact");
4400 g_object_set_data (holder, "profile_selector", act1 );
4402 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
4404 sp_dcc_build_presets_list (holder);
4406 g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
4407 gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
4408 }
4409 }
4410 }
4413 //########################
4414 //## Circle / Arc ##
4415 //########################
4417 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4418 {
4419 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4420 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4422 if (v1 == 0 && v2 == 0) {
4423 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4424 gtk_action_set_sensitive( ocb, FALSE );
4425 gtk_action_set_sensitive( make_whole, FALSE );
4426 }
4427 } else {
4428 gtk_action_set_sensitive( ocb, TRUE );
4429 gtk_action_set_sensitive( make_whole, TRUE );
4430 }
4431 }
4433 static void
4434 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4435 {
4436 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4438 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4439 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4440 prefs->setDouble("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
4441 }
4443 // quit if run by the attr_changed listener
4444 if (g_object_get_data( tbl, "freeze" )) {
4445 return;
4446 }
4448 // in turn, prevent listener from responding
4449 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4451 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4453 bool modmade = false;
4454 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4455 items != NULL;
4456 items = items->next)
4457 {
4458 SPItem *item = SP_ITEM(items->data);
4460 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4462 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4463 SPArc *arc = SP_ARC(item);
4465 if (!strcmp(value_name, "start"))
4466 ge->start = (adj->value * M_PI)/ 180;
4467 else
4468 ge->end = (adj->value * M_PI)/ 180;
4470 sp_genericellipse_normalize(ge);
4471 ((SPObject *)arc)->updateRepr();
4472 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4474 modmade = true;
4475 }
4476 }
4478 g_free(namespaced_name);
4480 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4482 sp_arctb_sensitivize( tbl, adj->value, other->value );
4484 if (modmade) {
4485 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4486 _("Arc: Change start/end"));
4487 }
4489 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4490 }
4493 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
4494 {
4495 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
4496 }
4498 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4499 {
4500 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
4501 }
4504 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4505 {
4506 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4507 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4508 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4509 prefs->setBool("tools.shapes.arc", "open", ege_select_one_action_get_active(act) != 0);
4510 }
4512 // quit if run by the attr_changed listener
4513 if (g_object_get_data( tbl, "freeze" )) {
4514 return;
4515 }
4517 // in turn, prevent listener from responding
4518 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4520 bool modmade = false;
4522 if ( ege_select_one_action_get_active(act) != 0 ) {
4523 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4524 items != NULL;
4525 items = items->next)
4526 {
4527 if (SP_IS_ARC((SPItem *) items->data)) {
4528 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4529 repr->setAttribute("sodipodi:open", "true");
4530 SP_OBJECT((SPItem *) items->data)->updateRepr();
4531 modmade = true;
4532 }
4533 }
4534 } else {
4535 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4536 items != NULL;
4537 items = items->next)
4538 {
4539 if (SP_IS_ARC((SPItem *) items->data)) {
4540 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4541 repr->setAttribute("sodipodi:open", NULL);
4542 SP_OBJECT((SPItem *) items->data)->updateRepr();
4543 modmade = true;
4544 }
4545 }
4546 }
4548 if (modmade) {
4549 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4550 _("Arc: Change open/closed"));
4551 }
4553 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4554 }
4556 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4557 {
4558 GtkAdjustment *adj;
4559 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
4560 gtk_adjustment_set_value(adj, 0.0);
4561 gtk_adjustment_value_changed(adj);
4563 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
4564 gtk_adjustment_set_value(adj, 0.0);
4565 gtk_adjustment_value_changed(adj);
4567 spinbutton_defocus( GTK_OBJECT(obj) );
4568 }
4570 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
4571 gchar const */*old_value*/, gchar const */*new_value*/,
4572 bool /*is_interactive*/, gpointer data)
4573 {
4574 GObject *tbl = G_OBJECT(data);
4576 // quit if run by the _changed callbacks
4577 if (g_object_get_data( tbl, "freeze" )) {
4578 return;
4579 }
4581 // in turn, prevent callbacks from responding
4582 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4584 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4585 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4587 GtkAdjustment *adj1,*adj2;
4588 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4589 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4590 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4591 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4593 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4595 char const *openstr = NULL;
4596 openstr = repr->attribute("sodipodi:open");
4597 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4599 if (openstr) {
4600 ege_select_one_action_set_active( ocb, 1 );
4601 } else {
4602 ege_select_one_action_set_active( ocb, 0 );
4603 }
4605 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4606 }
4608 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4609 NULL, /* child_added */
4610 NULL, /* child_removed */
4611 arc_tb_event_attr_changed,
4612 NULL, /* content_changed */
4613 NULL /* order_changed */
4614 };
4617 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4618 {
4619 int n_selected = 0;
4620 Inkscape::XML::Node *repr = NULL;
4622 purge_repr_listener( tbl, tbl );
4624 for (GSList const *items = selection->itemList();
4625 items != NULL;
4626 items = items->next)
4627 {
4628 if (SP_IS_ARC((SPItem *) items->data)) {
4629 n_selected++;
4630 repr = SP_OBJECT_REPR((SPItem *) items->data);
4631 }
4632 }
4634 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4636 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4637 if (n_selected == 0) {
4638 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4639 } else if (n_selected == 1) {
4640 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4641 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4643 if (repr) {
4644 g_object_set_data( tbl, "repr", repr );
4645 Inkscape::GC::anchor(repr);
4646 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4647 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4648 }
4649 } else {
4650 // FIXME: implement averaging of all parameters for multiple selected
4651 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4652 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4653 sp_arctb_sensitivize( tbl, 1, 0 );
4654 }
4655 }
4658 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4659 {
4660 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4662 EgeAdjustmentAction* eact = 0;
4663 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
4666 {
4667 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4668 ege_output_action_set_use_markup( act, TRUE );
4669 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4670 g_object_set_data( holder, "mode_action", act );
4671 }
4673 /* Start */
4674 {
4675 eact = create_adjustment_action( "ArcStartAction",
4676 _("Start"), _("Start:"),
4677 _("The angle (in degrees) from the horizontal to the arc's start point"),
4678 "tools.shapes.arc", "start", 0.0,
4679 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4680 -360.0, 360.0, 1.0, 10.0,
4681 0, 0, 0,
4682 sp_arctb_start_value_changed);
4683 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4684 }
4686 /* End */
4687 {
4688 eact = create_adjustment_action( "ArcEndAction",
4689 _("End"), _("End:"),
4690 _("The angle (in degrees) from the horizontal to the arc's end point"),
4691 "tools.shapes.arc", "end", 0.0,
4692 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4693 -360.0, 360.0, 1.0, 10.0,
4694 0, 0, 0,
4695 sp_arctb_end_value_changed);
4696 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4697 }
4699 /* Segments / Pie checkbox */
4700 {
4701 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4703 GtkTreeIter iter;
4704 gtk_list_store_append( model, &iter );
4705 gtk_list_store_set( model, &iter,
4706 0, _("Closed arc"),
4707 1, _("Switch to segment (closed shape with two radii)"),
4708 2, "circle_closed_arc",
4709 -1 );
4711 gtk_list_store_append( model, &iter );
4712 gtk_list_store_set( model, &iter,
4713 0, _("Open Arc"),
4714 1, _("Switch to arc (unclosed shape)"),
4715 2, "circle_open_arc",
4716 -1 );
4718 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4719 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4720 g_object_set_data( holder, "open_action", act );
4722 ege_select_one_action_set_appearance( act, "full" );
4723 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4724 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4725 ege_select_one_action_set_icon_column( act, 2 );
4726 ege_select_one_action_set_icon_size( act, secondarySize );
4727 ege_select_one_action_set_tooltip_column( act, 1 );
4729 bool isClosed = !prefs->getBool("tools.shapes.arc", "open", false);
4730 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4731 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4732 }
4734 /* Make Whole */
4735 {
4736 InkAction* inky = ink_action_new( "ArcResetAction",
4737 _("Make whole"),
4738 _("Make the shape a whole ellipse, not arc or segment"),
4739 "reset_circle",
4740 secondarySize );
4741 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4742 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4743 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4744 g_object_set_data( holder, "make_whole", inky );
4745 }
4747 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4748 // sensitivize make whole and open checkbox
4749 {
4750 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4751 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4752 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4753 }
4756 sigc::connection *connection = new sigc::connection(
4757 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4758 );
4759 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4760 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4761 }
4766 // toggle button callbacks and updaters
4768 //########################
4769 //## Dropper ##
4770 //########################
4772 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4773 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4774 prefs->setInt( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4775 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4776 if ( set_action ) {
4777 if ( gtk_toggle_action_get_active( act ) ) {
4778 gtk_action_set_sensitive( set_action, TRUE );
4779 } else {
4780 gtk_action_set_sensitive( set_action, FALSE );
4781 }
4782 }
4784 spinbutton_defocus(GTK_OBJECT(tbl));
4785 }
4787 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4788 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4789 prefs->setInt( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4790 spinbutton_defocus(GTK_OBJECT(tbl));
4791 }
4794 /**
4795 * Dropper auxiliary toolbar construction and setup.
4796 *
4797 * TODO: Would like to add swatch of current color.
4798 * TODO: Add queue of last 5 or so colors selected with new swatches so that
4799 * can drag and drop places. Will provide a nice mixing palette.
4800 */
4801 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4802 {
4803 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4804 gint pickAlpha = prefs->getInt( "tools.dropper", "pick", 1 );
4806 {
4807 EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4808 ege_output_action_set_use_markup( act, TRUE );
4809 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4810 }
4812 {
4813 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4814 _("Pick opacity"),
4815 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4816 NULL,
4817 Inkscape::ICON_SIZE_DECORATION );
4818 g_object_set( act, "short_label", _("Pick"), NULL );
4819 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4820 g_object_set_data( holder, "pick_action", act );
4821 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4822 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4823 }
4825 {
4826 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4827 _("Assign opacity"),
4828 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4829 NULL,
4830 Inkscape::ICON_SIZE_DECORATION );
4831 g_object_set( act, "short_label", _("Assign"), NULL );
4832 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4833 g_object_set_data( holder, "set_action", act );
4834 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getInt( "tools.dropper", "setalpha", 1 ) );
4835 // make sure it's disabled if we're not picking alpha
4836 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4837 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4838 }
4839 }
4842 //########################
4843 //## LPETool ##
4844 //########################
4846 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
4848 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
4849 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
4850 {
4851 using namespace Inkscape::LivePathEffect;
4853 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
4854 SPEventContext *ec = desktop->event_context;
4855 if (!SP_IS_LPETOOL_CONTEXT(ec)) {
4856 return;
4857 }
4859 // only take action if run by the attr_changed listener
4860 if (!g_object_get_data(tbl, "freeze")) {
4861 // in turn, prevent listener from responding
4862 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
4864 gint mode = ege_select_one_action_get_active(act);
4865 EffectType type = lpesubtools[mode];
4867 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4868 bool success = lpetool_try_construction(lc, type);
4869 if (success) {
4870 // since the construction was already performed, we set the state back to inactive
4871 ege_select_one_action_set_active(act, 0);
4872 mode = 0;
4873 } else {
4874 // switch to the chosen subtool
4875 SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
4876 }
4878 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4879 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4880 prefs->setInt( "tools.lpetool", "mode", mode );
4881 }
4883 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
4884 }
4885 }
4887 void sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject */*tbl*/)
4888 {
4889 SPEventContext *ec = selection->desktop()->event_context;
4890 if (!SP_IS_LPETOOL_CONTEXT(ec))
4891 return;
4893 lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
4894 }
4896 void
4897 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
4898 {
4899 using namespace Inkscape::LivePathEffect;
4900 SPEventContext *ec = selection->desktop()->event_context;
4901 if (!SP_IS_LPETOOL_CONTEXT(ec))
4902 return;
4903 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
4905 lpetool_delete_measuring_items(lc);
4906 lpetool_create_measuring_items(lc, selection);
4908 // activate line segment combo box if a single item with LPELineSegment is selected
4909 GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
4910 SPItem *item = selection->singleItem();
4911 if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
4912 SPLPEItem *lpeitem = SP_LPE_ITEM(item);
4913 Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
4914 if (lpe && lpe->effectType() == LINE_SEGMENT) {
4915 LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
4916 g_object_set_data(tbl, "currentlpe", lpe);
4917 g_object_set_data(tbl, "currentlpeitem", lpeitem);
4918 gtk_action_set_sensitive(w, TRUE);
4919 ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
4920 } else {
4921 g_object_set_data(tbl, "currentlpe", NULL);
4922 g_object_set_data(tbl, "currentlpeitem", NULL);
4923 gtk_action_set_sensitive(w, FALSE);
4924 }
4925 } else {
4926 g_object_set_data(tbl, "currentlpe", NULL);
4927 g_object_set_data(tbl, "currentlpeitem", NULL);
4928 gtk_action_set_sensitive(w, FALSE);
4929 }
4930 }
4932 static void
4933 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
4934 SPDesktop *desktop = static_cast<SPDesktop *>(data);
4935 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4937 bool show = gtk_toggle_action_get_active( act );
4938 prefs->setInt("tools.lpetool", "show_bbox", show ? 1 : 0);
4940 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
4941 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4942 lpetool_context_reset_limiting_bbox(lc);
4943 }
4944 }
4946 static void
4947 lpetool_toggle_show_measuring_info (GtkToggleAction *act, GObject *tbl) {
4948 SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
4949 if (!tools_isactive(desktop, TOOLS_LPETOOL))
4950 return;
4952 GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
4953 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4954 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
4955 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4956 bool show = gtk_toggle_action_get_active( act );
4957 prefs->setInt("tools.lpetool", "show_measuring_info", show ? 1 : 0);
4958 lpetool_show_measuring_info(lc, show);
4959 gtk_action_set_sensitive(GTK_ACTION(unitact), show);
4960 }
4961 }
4963 static void lpetool_unit_changed(GtkAction* /*act*/, GObject* tbl) {
4964 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
4965 SPUnit const *unit = tracker->getActiveUnit();
4966 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4967 prefs->setInt("tools.lpetool", "unitid", unit->unit_id);
4969 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4970 if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
4971 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4972 lpetool_delete_measuring_items(lc);
4973 lpetool_create_measuring_items(lc);
4974 }
4975 }
4977 static void
4978 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
4979 SPDesktop *desktop = static_cast<SPDesktop *>(data);
4980 Inkscape::Selection *selection = desktop->selection;
4982 boost::optional<NR::Rect> bbox = selection->bounds();
4984 if (bbox) {
4985 Geom::Point A(bbox->min());
4986 Geom::Point B(bbox->max());
4988 A *= desktop->doc2dt();
4989 B *= desktop->doc2dt();
4991 // TODO: should we provide a way to store points in prefs?
4992 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4993 prefs->setDouble("tools.lpetool", "bbox_upperleftx", A[Geom::X]);
4994 prefs->setDouble("tools.lpetool", "bbox_upperlefty", A[Geom::Y]);
4995 prefs->setDouble("tools.lpetool", "bbox_lowerrightx", B[Geom::X]);
4996 prefs->setDouble("tools.lpetool", "bbox_lowerrighty", B[Geom::Y]);
4998 lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
4999 }
5001 gtk_toggle_action_set_active(act, false);
5002 }
5004 static void
5005 sp_line_segment_build_list(GObject *tbl)
5006 {
5007 g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
5009 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
5010 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
5011 gtk_list_store_clear (model);
5013 // TODO: we add the entries of rht combo box manually; later this should be done automatically
5014 {
5015 GtkTreeIter iter;
5016 gtk_list_store_append( model, &iter );
5017 gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
5018 gtk_list_store_append( model, &iter );
5019 gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
5020 gtk_list_store_append( model, &iter );
5021 gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
5022 gtk_list_store_append( model, &iter );
5023 gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
5024 }
5026 g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5027 }
5029 static void
5030 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
5031 using namespace Inkscape::LivePathEffect;
5033 // quit if run by the attr_changed listener
5034 if (g_object_get_data(tbl, "freeze")) {
5035 return;
5036 }
5038 // in turn, prevent listener from responding
5039 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5041 LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
5042 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5043 if (lpeitem) {
5044 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5045 lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
5046 sp_lpe_item_update_patheffect(lpeitem, true, true);
5047 }
5049 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5050 }
5052 static void
5053 lpetool_open_lpe_dialog (GtkToggleAction *act, gpointer data) {
5054 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5056 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5057 sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5058 }
5059 gtk_toggle_action_set_active(act, false);
5060 }
5062 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5063 {
5064 UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5065 tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5066 g_object_set_data(holder, "tracker", tracker);
5067 SPUnit const *unit = tracker->getActiveUnit();
5069 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5070 prefs->setInt("tools.lpetool", "unitid", unit->unit_id);
5072 /** Automatically create a list of LPEs that get added to the toolbar **/
5073 {
5074 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5076 GtkTreeIter iter;
5078 // the first toggle button represents the state that no subtool is active (remove this when
5079 // this can be modeled by EgeSelectOneAction or some other action)
5080 gtk_list_store_append( model, &iter );
5081 gtk_list_store_set( model, &iter,
5082 0, _("All inactive"),
5083 1, _("No geometric tool is active"),
5084 2, _("all_inactive"),
5085 -1 );
5087 Inkscape::LivePathEffect::EffectType type;
5088 for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5089 type = lpesubtools[i];
5090 gtk_list_store_append( model, &iter );
5091 gtk_list_store_set( model, &iter,
5092 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5093 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5094 2, Inkscape::LivePathEffect::LPETypeConverter.get_key(type).c_str(),
5095 -1 );
5096 }
5098 EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5099 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5100 g_object_set_data( holder, "lpetool_mode_action", act );
5102 ege_select_one_action_set_appearance( act, "full" );
5103 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5104 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5105 ege_select_one_action_set_icon_column( act, 2 );
5106 ege_select_one_action_set_tooltip_column( act, 1 );
5108 gint lpeToolMode = prefs->getInt("tools.lpetool", "mode", 0);
5109 ege_select_one_action_set_active( act, lpeToolMode );
5110 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5111 }
5113 /* Show limiting bounding box */
5114 {
5115 InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5116 _("Show limiting bounding box"),
5117 _("Show bounding box (used to cut infinite lines)"),
5118 "lpetool_show_bbox",
5119 Inkscape::ICON_SIZE_DECORATION );
5120 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5121 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5122 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getInt( "tools.lpetool", "show_bbox", 1 ) );
5123 }
5125 /* Set limiting bounding box to bbox of current selection */
5126 {
5127 InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5128 _("Get limiting bounding box from selection"),
5129 _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5130 "lpetool_set_bbox",
5131 Inkscape::ICON_SIZE_DECORATION );
5132 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5133 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5134 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5135 }
5138 /* Combo box to choose line segment type */
5139 {
5140 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5141 EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5142 ege_select_one_action_set_appearance (act, "compact");
5143 g_object_set_data (holder, "lpetool_line_segment_action", act );
5145 g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5147 sp_line_segment_build_list (holder);
5149 g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5150 gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5151 gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5152 }
5154 /* Display measuring info for selected items */
5155 {
5156 InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5157 _("Display measuring info"),
5158 _("Display measuring info for selected items"),
5159 "lpetool_measuring_info",
5160 Inkscape::ICON_SIZE_DECORATION );
5161 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5162 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
5163 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getInt( "tools.lpetool", "show_measuring_info", 1 ) );
5164 }
5166 // add the units menu
5167 {
5168 GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5169 gtk_action_group_add_action( mainActions, act );
5170 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5171 g_object_set_data(holder, "lpetool_units_action", act);
5172 gtk_action_set_sensitive(act, prefs->getInt("tools.lpetool", "show_measuring_info", 1));
5173 }
5175 /* Open LPE dialog (to adapt parameters numerically) */
5176 {
5177 InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
5178 _("Open LPE dialog"),
5179 _("Open LPE dialog (to adapt parameters numerically)"),
5180 "lpetool_open_lpe_dialog",
5181 Inkscape::ICON_SIZE_DECORATION );
5182 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5183 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
5184 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5185 }
5187 //watch selection
5188 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5190 sigc::connection *c_selection_modified =
5191 new sigc::connection (sp_desktop_selection (desktop)->connectModified
5192 (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
5193 pool->add_connection ("selection-modified", c_selection_modified);
5195 sigc::connection *c_selection_changed =
5196 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5197 (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5198 pool->add_connection ("selection-changed", c_selection_changed);
5199 }
5201 //########################
5202 //## Eraser ##
5203 //########################
5205 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5206 {
5207 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5208 gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
5209 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5210 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5211 prefs->setInt( "tools.eraser", "mode", eraserMode );
5212 }
5214 // only take action if run by the attr_changed listener
5215 if (!g_object_get_data( tbl, "freeze" )) {
5216 // in turn, prevent listener from responding
5217 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5219 if ( eraserMode != 0 ) {
5220 } else {
5221 }
5222 // TODO finish implementation
5224 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5225 }
5226 }
5228 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5229 {
5230 {
5231 /* Width */
5232 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5233 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5234 EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5235 _("Pen Width"), _("Width:"),
5236 _("The width of the eraser pen (relative to the visible canvas area)"),
5237 "tools.eraser", "width", 15,
5238 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5239 1, 100, 1.0, 0.0,
5240 labels, values, G_N_ELEMENTS(labels),
5241 sp_ddc_width_value_changed, 0.01, 0, 100 );
5242 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5243 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5244 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5245 }
5247 {
5248 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5250 GtkTreeIter iter;
5251 gtk_list_store_append( model, &iter );
5252 gtk_list_store_set( model, &iter,
5253 0, _("Delete"),
5254 1, _("Delete objects touched by the eraser"),
5255 2, "delete_object",
5256 -1 );
5258 gtk_list_store_append( model, &iter );
5259 gtk_list_store_set( model, &iter,
5260 0, _("Cut"),
5261 1, _("Cut out from objects"),
5262 2, "difference",
5263 -1 );
5265 EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5266 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5267 g_object_set_data( holder, "eraser_mode_action", act );
5269 ege_select_one_action_set_appearance( act, "full" );
5270 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5271 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5272 ege_select_one_action_set_icon_column( act, 2 );
5273 ege_select_one_action_set_tooltip_column( act, 1 );
5275 /// @todo Convert to boolean?
5276 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5277 gint eraserMode = (prefs->getInt("tools.eraser", "mode", 0) != 0) ? 1 : 0;
5278 ege_select_one_action_set_active( act, eraserMode );
5279 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
5280 }
5282 }
5284 //########################
5285 //## Text Toolbox ##
5286 //########################
5287 /*
5288 static void
5289 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
5290 {
5291 //Call back for letter sizing spinbutton
5292 }
5294 static void
5295 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
5296 {
5297 //Call back for line height spinbutton
5298 }
5300 static void
5301 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5302 {
5303 //Call back for horizontal kerning spinbutton
5304 }
5306 static void
5307 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5308 {
5309 //Call back for vertical kerning spinbutton
5310 }
5312 static void
5313 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
5314 {
5315 //Call back for letter rotation spinbutton
5316 }*/
5318 namespace {
5320 bool popdown_visible = false;
5321 bool popdown_hasfocus = false;
5323 void
5324 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
5325 {
5326 SPStyle *query =
5327 sp_style_new (SP_ACTIVE_DOCUMENT);
5329 // int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5331 int result_family =
5332 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5334 int result_style =
5335 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5337 int result_numbers =
5338 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5340 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5342 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5343 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
5344 // there are no texts in selection, read from prefs
5346 Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
5347 if (repr) {
5348 sp_style_read_from_repr (query, repr);
5349 if (g_object_get_data(tbl, "text_style_from_prefs")) {
5350 // do not reset the toolbar style from prefs if we already did it last time
5351 sp_style_unref(query);
5352 return;
5353 }
5354 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
5355 } else {
5356 sp_style_unref(query);
5357 return;
5358 }
5359 } else {
5360 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
5361 }
5363 if (query->text)
5364 {
5365 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
5366 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5367 gtk_entry_set_text (GTK_ENTRY (entry), "");
5369 } else if (query->text->font_specification.value || query->text->font_family.value) {
5371 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5373 // Get the font that corresponds
5374 Glib::ustring familyName;
5376 font_instance * font = font_factory::Default()->FaceFromStyle(query);
5377 if (font) {
5378 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
5379 font->Unref();
5380 font = NULL;
5381 }
5383 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
5385 Gtk::TreePath path;
5386 try {
5387 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
5388 } catch (...) {
5389 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
5390 sp_style_unref(query);
5391 return;
5392 }
5394 GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5395 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5397 g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
5399 gtk_tree_selection_select_path (tselection, path.gobj());
5400 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5402 g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
5403 }
5405 //Size
5406 {
5407 GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
5408 gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
5409 g_object_set_data(tbl, "size-block", gpointer(1));
5410 gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
5411 g_object_set_data(tbl, "size-block", gpointer(0));
5412 g_free(str);
5413 }
5415 //Anchor
5416 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
5417 {
5418 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
5419 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5420 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5421 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5422 }
5423 else
5424 {
5425 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
5426 {
5427 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
5428 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5429 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5430 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5431 }
5432 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
5433 {
5434 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
5435 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5436 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5437 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5438 }
5439 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
5440 {
5441 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
5442 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5443 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5444 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5445 }
5446 }
5448 //Style
5449 {
5450 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
5452 gboolean active = gtk_toggle_button_get_active (button);
5453 gboolean check = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
5455 if (active != check)
5456 {
5457 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5458 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5459 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5460 }
5461 }
5463 {
5464 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
5466 gboolean active = gtk_toggle_button_get_active (button);
5467 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
5469 if (active != check)
5470 {
5471 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5472 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5473 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5474 }
5475 }
5477 //Orientation
5478 //locking both buttons, changing one affect all group (both)
5479 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
5480 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5482 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
5483 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
5485 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
5486 {
5487 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5488 }
5489 else
5490 {
5491 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
5492 }
5493 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5494 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
5495 }
5497 sp_style_unref(query);
5498 }
5500 void
5501 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
5502 {
5503 sp_text_toolbox_selection_changed (selection, tbl);
5504 }
5506 void
5507 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
5508 {
5509 sp_text_toolbox_selection_changed (NULL, tbl);
5510 }
5512 void
5513 sp_text_toolbox_family_changed (GtkTreeSelection *selection,
5514 GObject *tbl)
5515 {
5516 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5517 GtkTreeModel *model = 0;
5518 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5519 GtkTreeIter iter;
5520 char *family = 0;
5522 gdk_pointer_ungrab (GDK_CURRENT_TIME);
5523 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5525 if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
5526 return;
5527 }
5529 gtk_tree_model_get (model, &iter, 0, &family, -1);
5531 if (g_object_get_data (G_OBJECT (selection), "block"))
5532 {
5533 gtk_entry_set_text (GTK_ENTRY (entry), family);
5534 return;
5535 }
5537 gtk_entry_set_text (GTK_ENTRY (entry), family);
5539 SPStyle *query =
5540 sp_style_new (SP_ACTIVE_DOCUMENT);
5542 int result_fontspec =
5543 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5545 //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5547 SPCSSAttr *css = sp_repr_css_attr_new ();
5550 // First try to get the font spec from the stored value
5551 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
5553 if (fontSpec.empty()) {
5554 // Construct a new font specification if it does not yet exist
5555 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5556 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5557 fontFromStyle->Unref();
5558 }
5560 if (!fontSpec.empty()) {
5561 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
5562 if (!newFontSpec.empty() && fontSpec != newFontSpec) {
5563 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
5564 if (font) {
5565 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5567 // Set all the these just in case they were altered when finding the best
5568 // match for the new family and old style...
5570 gchar c[256];
5572 font->Family(c, 256);
5573 sp_repr_css_set_property (css, "font-family", c);
5575 font->Attribute( "weight", c, 256);
5576 sp_repr_css_set_property (css, "font-weight", c);
5578 font->Attribute("style", c, 256);
5579 sp_repr_css_set_property (css, "font-style", c);
5581 font->Attribute("stretch", c, 256);
5582 sp_repr_css_set_property (css, "font-stretch", c);
5584 font->Attribute("variant", c, 256);
5585 sp_repr_css_set_property (css, "font-variant", c);
5587 font->Unref();
5588 }
5589 }
5590 }
5592 // If querying returned nothing, set the default style of the tool (for new texts)
5593 if (result_fontspec == QUERY_STYLE_NOTHING)
5594 {
5595 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5596 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
5597 }
5598 else
5599 {
5600 sp_desktop_set_style (desktop, css, true, true);
5601 }
5603 sp_style_unref(query);
5605 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5606 _("Text: Change font family"));
5607 sp_repr_css_attr_unref (css);
5608 g_free(family);
5609 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5611 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5612 }
5614 /* This is where execution comes when the contents of the font family box have been completed
5615 by the press of the return key */
5616 void
5617 sp_text_toolbox_family_entry_activate (GtkEntry *entry,
5618 GObject *tbl)
5619 {
5620 const char *family = gtk_entry_get_text (entry); // Fetch the requested font family
5622 // Try to match that to a known font. If not, then leave current font alone and remain focused on text box
5623 try {
5624 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
5625 GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5626 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5627 gtk_tree_selection_select_path (selection, path.gobj());
5628 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5629 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5630 } catch (...) {
5631 if (family && strlen (family))
5632 {
5633 gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5634 }
5635 }
5636 }
5638 void
5639 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
5640 gpointer data)
5641 {
5642 if (g_object_get_data (G_OBJECT (button), "block")) return;
5643 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
5644 int prop = GPOINTER_TO_INT(data);
5646 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5647 SPCSSAttr *css = sp_repr_css_attr_new ();
5649 switch (prop)
5650 {
5651 case 0:
5652 {
5653 sp_repr_css_set_property (css, "text-anchor", "start");
5654 sp_repr_css_set_property (css, "text-align", "start");
5655 break;
5656 }
5657 case 1:
5658 {
5659 sp_repr_css_set_property (css, "text-anchor", "middle");
5660 sp_repr_css_set_property (css, "text-align", "center");
5661 break;
5662 }
5664 case 2:
5665 {
5666 sp_repr_css_set_property (css, "text-anchor", "end");
5667 sp_repr_css_set_property (css, "text-align", "end");
5668 break;
5669 }
5671 case 3:
5672 {
5673 sp_repr_css_set_property (css, "text-anchor", "start");
5674 sp_repr_css_set_property (css, "text-align", "justify");
5675 break;
5676 }
5677 }
5679 SPStyle *query =
5680 sp_style_new (SP_ACTIVE_DOCUMENT);
5681 int result_numbers =
5682 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5684 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5685 if (result_numbers == QUERY_STYLE_NOTHING)
5686 {
5687 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5688 }
5690 sp_style_unref(query);
5692 sp_desktop_set_style (desktop, css, true, true);
5693 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5694 _("Text: Change alignment"));
5695 sp_repr_css_attr_unref (css);
5697 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5698 }
5700 void
5701 sp_text_toolbox_style_toggled (GtkToggleButton *button,
5702 gpointer data)
5703 {
5704 if (g_object_get_data (G_OBJECT (button), "block")) return;
5706 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5707 SPCSSAttr *css = sp_repr_css_attr_new ();
5708 int prop = GPOINTER_TO_INT(data);
5709 bool active = gtk_toggle_button_get_active (button);
5711 SPStyle *query =
5712 sp_style_new (SP_ACTIVE_DOCUMENT);
5714 int result_fontspec =
5715 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5717 //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5718 //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5719 //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5721 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
5722 Glib::ustring newFontSpec = "";
5724 if (fontSpec.empty()) {
5725 // Construct a new font specification if it does not yet exist
5726 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5727 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5728 fontFromStyle->Unref();
5729 }
5731 switch (prop)
5732 {
5733 case 0:
5734 {
5735 if (!fontSpec.empty()) {
5736 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
5737 }
5738 if (fontSpec != newFontSpec) {
5739 // Don't even set the bold if the font didn't exist on the system
5740 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
5741 }
5742 break;
5743 }
5745 case 1:
5746 {
5747 if (!fontSpec.empty()) {
5748 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
5749 }
5750 if (fontSpec != newFontSpec) {
5751 // Don't even set the italic if the font didn't exist on the system
5752 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
5753 }
5754 break;
5755 }
5756 }
5758 if (!newFontSpec.empty()) {
5759 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5760 }
5762 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5763 if (result_fontspec == QUERY_STYLE_NOTHING)
5764 {
5765 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5766 }
5768 sp_style_unref(query);
5770 sp_desktop_set_style (desktop, css, true, true);
5771 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5772 _("Text: Change font style"));
5773 sp_repr_css_attr_unref (css);
5775 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5776 }
5778 void
5779 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
5780 gpointer data)
5781 {
5782 if (g_object_get_data (G_OBJECT (button), "block")) {
5783 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5784 return;
5785 }
5787 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5788 SPCSSAttr *css = sp_repr_css_attr_new ();
5789 int prop = GPOINTER_TO_INT(data);
5791 switch (prop)
5792 {
5793 case 0:
5794 {
5795 sp_repr_css_set_property (css, "writing-mode", "lr");
5796 break;
5797 }
5799 case 1:
5800 {
5801 sp_repr_css_set_property (css, "writing-mode", "tb");
5802 break;
5803 }
5804 }
5806 SPStyle *query =
5807 sp_style_new (SP_ACTIVE_DOCUMENT);
5808 int result_numbers =
5809 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5811 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5812 if (result_numbers == QUERY_STYLE_NOTHING)
5813 {
5814 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5815 }
5817 sp_desktop_set_style (desktop, css, true, true);
5818 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5819 _("Text: Change orientation"));
5820 sp_repr_css_attr_unref (css);
5822 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5823 }
5825 gboolean
5826 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5827 {
5828 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5829 if (!desktop) return FALSE;
5831 switch (get_group0_keyval (event)) {
5832 case GDK_Escape: // defocus
5833 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5834 sp_text_toolbox_selection_changed (NULL, tbl); // update
5835 return TRUE; // I consumed the event
5836 break;
5837 }
5838 return FALSE;
5839 }
5841 gboolean
5842 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5843 {
5844 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5845 if (!desktop) return FALSE;
5847 switch (get_group0_keyval (event)) {
5848 case GDK_KP_Enter:
5849 case GDK_Return:
5850 case GDK_Escape: // defocus
5851 gtk_widget_hide (w);
5852 popdown_visible = false;
5853 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5854 return TRUE; // I consumed the event
5855 break;
5856 case GDK_w:
5857 case GDK_W:
5858 if (event->state & GDK_CONTROL_MASK) {
5859 gtk_widget_hide (w);
5860 popdown_visible = false;
5861 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5862 return TRUE; // I consumed the event
5863 }
5864 break;
5865 }
5866 return FALSE;
5867 }
5870 void
5871 sp_text_toolbox_size_changed (GtkComboBox *cbox,
5872 GObject *tbl)
5873 {
5874 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5876 if (g_object_get_data (tbl, "size-block")) return;
5878 // If this is not from selecting a size in the list (in which case get_active will give the
5879 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5880 // process this event. This fixes GTK's stupid insistence on sending an activate change every
5881 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5882 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5883 return;
5885 gdouble value = -1;
5886 {
5887 gchar *endptr;
5888 gchar *const text = gtk_combo_box_get_active_text(cbox);
5889 if (text) {
5890 value = g_strtod(text, &endptr);
5891 if (endptr == text) { // Conversion failed, non-numeric input.
5892 value = -1;
5893 }
5894 g_free(text);
5895 }
5896 }
5897 if (value <= 0) {
5898 return; // could not parse value
5899 }
5901 SPCSSAttr *css = sp_repr_css_attr_new ();
5902 Inkscape::CSSOStringStream osfs;
5903 osfs << value;
5904 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5906 SPStyle *query =
5907 sp_style_new (SP_ACTIVE_DOCUMENT);
5908 int result_numbers =
5909 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5911 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5912 if (result_numbers == QUERY_STYLE_NOTHING)
5913 {
5914 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5915 }
5917 sp_style_unref(query);
5919 sp_desktop_set_style (desktop, css, true, true);
5920 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5921 _("Text: Change font size"));
5922 sp_repr_css_attr_unref (css);
5924 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5925 }
5927 gboolean
5928 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5929 {
5930 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5931 if (!desktop) return FALSE;
5933 if (!g_object_get_data (tbl, "esc-pressed")) {
5934 g_object_set_data (tbl, "enter-pressed", gpointer(1));
5935 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5936 sp_text_toolbox_size_changed (cbox, tbl);
5937 g_object_set_data (tbl, "enter-pressed", gpointer(0));
5938 }
5939 return FALSE; // I consumed the event
5940 }
5943 gboolean
5944 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5945 {
5946 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5947 if (!desktop) return FALSE;
5949 switch (get_group0_keyval (event)) {
5950 case GDK_Escape: // defocus
5951 g_object_set_data (tbl, "esc-pressed", gpointer(1));
5952 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5953 g_object_set_data (tbl, "esc-pressed", gpointer(0));
5954 return TRUE; // I consumed the event
5955 break;
5956 case GDK_Return: // defocus
5957 case GDK_KP_Enter:
5958 g_object_set_data (tbl, "enter-pressed", gpointer(1));
5959 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5960 sp_text_toolbox_size_changed (cbox, tbl);
5961 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5962 g_object_set_data (tbl, "enter-pressed", gpointer(0));
5963 return TRUE; // I consumed the event
5964 break;
5965 }
5966 return FALSE;
5967 }
5969 void
5970 sp_text_toolbox_text_popdown_clicked (GtkButton */*button*/,
5971 GObject *tbl)
5972 {
5973 GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
5974 GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5975 int x, y;
5977 if (!popdown_visible)
5978 {
5979 gdk_window_get_origin (widget->window, &x, &y);
5980 gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
5981 gtk_widget_show_all (popdown);
5982 //sp_transientize (popdown);
5984 gdk_pointer_grab (widget->window, TRUE,
5985 GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
5986 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
5987 GDK_POINTER_MOTION_MASK),
5988 NULL, NULL, GDK_CURRENT_TIME);
5990 gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
5992 popdown_visible = true;
5993 }
5994 else
5995 {
5996 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5997 gdk_pointer_ungrab (GDK_CURRENT_TIME);
5998 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5999 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6000 gtk_widget_hide (popdown);
6001 popdown_visible = false;
6002 }
6003 }
6005 gboolean
6006 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
6007 GdkEventFocus */*event*/,
6008 GObject */*tbl*/)
6009 {
6010 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
6011 return FALSE;
6012 }
6014 gboolean
6015 sp_text_toolbox_popdown_focus_out (GtkWidget *popdown,
6016 GdkEventFocus */*event*/,
6017 GObject */*tbl*/)
6018 {
6019 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6021 if (popdown_hasfocus) {
6022 gtk_widget_hide (popdown);
6023 popdown_hasfocus = false;
6024 popdown_visible = false;
6025 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6026 return TRUE;
6027 }
6028 return FALSE;
6029 }
6031 gboolean
6032 sp_text_toolbox_popdown_focus_in (GtkWidget */*popdown*/,
6033 GdkEventFocus */*event*/,
6034 GObject */*tbl*/)
6035 {
6036 popdown_hasfocus = true;
6037 return TRUE;
6038 }
6041 void
6042 cell_data_func (GtkTreeViewColumn */*column*/,
6043 GtkCellRenderer *cell,
6044 GtkTreeModel *tree_model,
6045 GtkTreeIter *iter,
6046 gpointer /*data*/)
6047 {
6048 gchar *family;
6049 gtk_tree_model_get(tree_model, iter, 0, &family, -1);
6050 gchar *const family_escaped = g_markup_escape_text(family, -1);
6052 static char const *const sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
6053 gchar *const sample_escaped = g_markup_escape_text(sample, -1);
6055 std::stringstream markup;
6056 markup << family_escaped << " <span foreground='darkgray' font_family='"
6057 << family_escaped << "'>" << sample_escaped << "</span>";
6058 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6060 g_free(family);
6061 g_free(family_escaped);
6062 g_free(sample_escaped);
6063 }
6065 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
6066 GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
6067 if (completion) {
6068 gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
6069 g_object_unref (completion);
6070 }
6071 }
6073 GtkWidget*
6074 sp_text_toolbox_new (SPDesktop *desktop)
6075 {
6076 GtkToolbar *tbl = GTK_TOOLBAR(gtk_toolbar_new());
6077 GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
6079 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
6080 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
6082 GtkTooltips *tt = gtk_tooltips_new();
6083 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
6085 ////////////Family
6086 //Window
6087 GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
6088 gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
6090 //Entry
6091 GtkWidget *entry = gtk_entry_new ();
6092 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
6093 GtkEntryCompletion *completion = gtk_entry_completion_new ();
6094 gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
6095 gtk_entry_completion_set_text_column (completion, 0);
6096 gtk_entry_completion_set_minimum_key_length (completion, 1);
6097 g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
6098 gtk_entry_set_completion (GTK_ENTRY(entry), completion);
6099 gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
6100 gtk_toolbar_append_widget( tbl, entry, "", "" );
6101 g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
6103 //Button
6104 GtkWidget *button = gtk_button_new ();
6105 gtk_container_add (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
6106 gtk_toolbar_append_widget( tbl, button, "", "");
6108 //Popdown
6109 GtkWidget *sw = gtk_scrolled_window_new (NULL, NULL);
6110 GtkWidget *treeview = gtk_tree_view_new ();
6112 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
6113 GtkTreeViewColumn *column = gtk_tree_view_column_new ();
6114 gtk_tree_view_column_pack_start (column, cell, FALSE);
6115 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
6116 gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
6117 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
6119 gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
6120 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
6121 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
6123 //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
6125 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
6126 gtk_container_add (GTK_CONTAINER (sw), treeview);
6128 gtk_container_add (GTK_CONTAINER (window), sw);
6129 gtk_widget_set_size_request (window, 300, 450);
6131 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
6132 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
6133 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
6135 g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
6137 g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
6138 g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
6139 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
6141 GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
6142 g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6144 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
6145 g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
6146 g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
6147 g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
6148 g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
6150 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
6151 GtkWidget *box = gtk_event_box_new ();
6152 gtk_container_add (GTK_CONTAINER (box), image);
6153 gtk_toolbar_append_widget( tbl, box, "", "");
6154 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
6155 GtkTooltips *tooltips = gtk_tooltips_new ();
6156 gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
6157 gtk_widget_hide (GTK_WIDGET (box));
6158 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
6160 ////////////Size
6161 gchar const *const sizes[] = {
6162 "4", "6", "8", "9", "10", "11", "12", "13", "14",
6163 "16", "18", "20", "22", "24", "28",
6164 "32", "36", "40", "48", "56", "64", "72", "144"
6165 };
6167 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
6168 for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
6169 gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
6170 }
6171 gtk_widget_set_size_request (cbox, 80, -1);
6172 gtk_toolbar_append_widget( tbl, cbox, "", "");
6173 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
6174 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
6175 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
6176 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
6178 ////////////Text anchor
6179 GtkWidget *group = gtk_radio_button_new (NULL);
6180 GtkWidget *row = gtk_hbox_new (FALSE, 4);
6181 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
6183 // left
6184 GtkWidget *rbutton = group;
6185 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6186 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
6187 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6189 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6190 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
6191 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
6192 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
6194 // center
6195 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6196 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6197 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
6198 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6200 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6201 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
6202 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
6203 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
6205 // right
6206 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6207 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6208 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
6209 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6211 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6212 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
6213 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
6214 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
6216 // fill
6217 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6218 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6219 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
6220 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6222 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6223 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
6224 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
6225 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
6227 gtk_toolbar_append_widget( tbl, row, "", "");
6229 //spacer
6230 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6232 ////////////Text style
6233 row = gtk_hbox_new (FALSE, 4);
6235 // bold
6236 rbutton = gtk_toggle_button_new ();
6237 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6238 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
6239 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6240 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
6242 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6243 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
6244 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
6246 // italic
6247 rbutton = gtk_toggle_button_new ();
6248 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6249 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
6250 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6251 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
6253 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6254 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
6255 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
6257 gtk_toolbar_append_widget( tbl, row, "", "");
6259 //spacer
6260 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6262 ////////////Text orientation
6263 group = gtk_radio_button_new (NULL);
6264 row = gtk_hbox_new (FALSE, 4);
6265 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
6267 // horizontal
6268 rbutton = group;
6269 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6270 gtk_container_add (GTK_CONTAINER (rbutton),
6271 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
6272 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6273 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
6275 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6276 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
6277 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
6279 // vertical
6280 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6281 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6282 gtk_container_add (GTK_CONTAINER (rbutton),
6283 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
6284 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6285 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
6287 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6288 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
6289 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
6290 gtk_toolbar_append_widget( tbl, row, "", "" );
6293 //watch selection
6294 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
6296 sigc::connection *c_selection_changed =
6297 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
6298 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
6299 pool->add_connection ("selection-changed", c_selection_changed);
6301 sigc::connection *c_selection_modified =
6302 new sigc::connection (sp_desktop_selection (desktop)->connectModified
6303 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
6304 pool->add_connection ("selection-modified", c_selection_modified);
6306 sigc::connection *c_subselection_changed =
6307 new sigc::connection (desktop->connectToolSubselectionChanged
6308 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
6309 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
6311 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
6314 gtk_widget_show_all( GTK_WIDGET(tbl) );
6316 return GTK_WIDGET(tbl);
6317 } // end of sp_text_toolbox_new()
6319 }//<unnamed> namespace
6322 //#########################
6323 //## Connector ##
6324 //#########################
6326 static void sp_connector_path_set_avoid(void)
6327 {
6328 cc_selection_set_avoid(true);
6329 }
6332 static void sp_connector_path_set_ignore(void)
6333 {
6334 cc_selection_set_avoid(false);
6335 }
6339 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
6340 {
6341 // quit if run by the _changed callbacks
6342 if (g_object_get_data( tbl, "freeze" )) {
6343 return;
6344 }
6346 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
6347 SPDocument *doc = sp_desktop_document(desktop);
6349 if (!sp_document_get_undo_sensitive(doc))
6350 {
6351 return;
6352 }
6354 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6356 if ( repr->attribute("inkscape:connector-spacing") ) {
6357 gdouble priorValue = gtk_adjustment_get_value(adj);
6358 sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
6359 if ( priorValue == gtk_adjustment_get_value(adj) ) {
6360 return;
6361 }
6362 } else if ( adj->value == defaultConnSpacing ) {
6363 return;
6364 }
6366 // in turn, prevent callbacks from responding
6367 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6369 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
6370 SP_OBJECT(desktop->namedview)->updateRepr();
6372 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
6373 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
6374 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
6375 NR::Matrix m = NR::identity();
6376 avoid_item_move(&m, item);
6377 }
6379 if (items) {
6380 g_slist_free(items);
6381 }
6383 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
6384 _("Change connector spacing"));
6386 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6388 spinbutton_defocus(GTK_OBJECT(tbl));
6389 }
6391 static void sp_connector_graph_layout(void)
6392 {
6393 if (!SP_ACTIVE_DESKTOP) return;
6394 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6396 // hack for clones, see comment in align-and-distribute.cpp
6397 int saved_compensation = prefs->getInt("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
6398 prefs->setInt("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
6400 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
6402 prefs->setInt("options.clonecompensation", "value", saved_compensation);
6404 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
6405 }
6407 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6408 {
6409 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6410 prefs->setBool("tools.connector", "directedlayout",
6411 gtk_toggle_action_get_active( act ));
6412 }
6414 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6415 {
6416 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6417 prefs->setBool("tools.connector", "avoidoverlaplayout",
6418 gtk_toggle_action_get_active( act ));
6419 }
6422 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
6423 {
6424 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6425 prefs->setDouble("tools.connector", "length", adj->value);
6426 spinbutton_defocus(GTK_OBJECT(tbl));
6427 }
6429 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
6430 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
6431 bool /*is_interactive*/, gpointer data)
6432 {
6433 GtkWidget *tbl = GTK_WIDGET(data);
6435 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6436 return;
6437 }
6438 if (strcmp(name, "inkscape:connector-spacing") != 0) {
6439 return;
6440 }
6442 GtkAdjustment *adj = (GtkAdjustment*)
6443 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
6444 gdouble spacing = defaultConnSpacing;
6445 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
6447 gtk_adjustment_set_value(adj, spacing);
6448 }
6451 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
6452 NULL, /* child_added */
6453 NULL, /* child_removed */
6454 connector_tb_event_attr_changed,
6455 NULL, /* content_changed */
6456 NULL /* order_changed */
6457 };
6460 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
6461 {
6462 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6463 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
6465 {
6466 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
6467 _("Avoid"),
6468 _("Make connectors avoid selected objects"),
6469 "connector_avoid",
6470 secondarySize );
6471 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
6472 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6473 }
6475 {
6476 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
6477 _("Ignore"),
6478 _("Make connectors ignore selected objects"),
6479 "connector_ignore",
6480 secondarySize );
6481 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
6482 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6483 }
6485 EgeAdjustmentAction* eact = 0;
6487 // Spacing spinbox
6488 eact = create_adjustment_action( "ConnectorSpacingAction",
6489 _("Connector Spacing"), _("Spacing:"),
6490 _("The amount of space left around objects by auto-routing connectors"),
6491 "tools.connector", "spacing", defaultConnSpacing,
6492 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
6493 0, 100, 1.0, 10.0,
6494 0, 0, 0,
6495 connector_spacing_changed, 1, 0 );
6496 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6498 // Graph (connector network) layout
6499 {
6500 InkAction* inky = ink_action_new( "ConnectorGraphAction",
6501 _("Graph"),
6502 _("Nicely arrange selected connector network"),
6503 "graph_layout",
6504 secondarySize );
6505 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
6506 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6507 }
6509 // Default connector length spinbox
6510 eact = create_adjustment_action( "ConnectorLengthAction",
6511 _("Connector Length"), _("Length:"),
6512 _("Ideal length for connectors when layout is applied"),
6513 "tools.connector", "length", 100,
6514 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
6515 10, 1000, 10.0, 100.0,
6516 0, 0, 0,
6517 connector_length_changed, 1, 0 );
6518 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6521 // Directed edges toggle button
6522 {
6523 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
6524 _("Downwards"),
6525 _("Make connectors with end-markers (arrows) point downwards"),
6526 "directed_graph",
6527 Inkscape::ICON_SIZE_DECORATION );
6528 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6530 bool tbuttonstate = prefs->getBool("tools.connector", "directedlayout");
6531 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
6533 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
6534 }
6536 // Avoid overlaps toggle button
6537 {
6538 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
6539 _("Remove overlaps"),
6540 _("Do not allow overlapping shapes"),
6541 "remove_overlaps",
6542 Inkscape::ICON_SIZE_DECORATION );
6543 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6545 bool tbuttonstate = prefs->getBool("tools.connector", "avoidoverlaplayout");
6546 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), (tbuttonstate ? TRUE : FALSE ));
6548 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
6549 }
6551 // Code to watch for changes to the connector-spacing attribute in
6552 // the XML.
6553 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6554 g_assert(repr != NULL);
6556 purge_repr_listener( holder, holder );
6558 if (repr) {
6559 g_object_set_data( holder, "repr", repr );
6560 Inkscape::GC::anchor(repr);
6561 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
6562 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
6563 }
6564 } // end of sp_connector_toolbox_prep()
6567 //#########################
6568 //## Paintbucket ##
6569 //#########################
6571 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
6572 {
6573 gint channels = ege_select_one_action_get_active( act );
6574 flood_channels_set_channels( channels );
6575 }
6577 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
6578 {
6579 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6580 prefs->setInt("tools.paintbucket", "threshold", (gint)adj->value);
6581 }
6583 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
6584 {
6585 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6586 prefs->setInt("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
6587 }
6589 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
6590 {
6591 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
6592 SPUnit const *unit = tracker->getActiveUnit();
6593 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6595 prefs->setDouble("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
6596 prefs->setString("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
6597 }
6599 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
6600 {
6601 // FIXME: make defaults settable via Inkscape Options
6602 struct KeyValue {
6603 char const *key;
6604 double value;
6605 } const key_values[] = {
6606 {"threshold", 15},
6607 {"offset", 0.0}
6608 };
6610 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
6611 KeyValue const &kv = key_values[i];
6612 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
6613 if ( adj ) {
6614 gtk_adjustment_set_value(adj, kv.value);
6615 }
6616 }
6618 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
6619 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
6620 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
6621 ege_select_one_action_set_active( autogap_action, 0 );
6622 }
6624 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6625 {
6626 EgeAdjustmentAction* eact = 0;
6627 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6629 {
6630 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6632 GList* items = 0;
6633 gint count = 0;
6634 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
6635 {
6636 GtkTreeIter iter;
6637 gtk_list_store_append( model, &iter );
6638 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6639 count++;
6640 }
6641 g_list_free( items );
6642 items = 0;
6643 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
6644 g_object_set( act1, "short_label", _("Fill by:"), NULL );
6645 ege_select_one_action_set_appearance( act1, "compact" );
6646 ege_select_one_action_set_active( act1, prefs->getInt("tools.paintbucket", "channels", 0) );
6647 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
6648 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
6649 g_object_set_data( holder, "channels_action", act1 );
6650 }
6652 // Spacing spinbox
6653 {
6654 eact = create_adjustment_action(
6655 "ThresholdAction",
6656 _("Fill Threshold"), _("Threshold:"),
6657 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
6658 "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
6659 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
6660 0, 0, 0,
6661 paintbucket_threshold_changed, 1, 0 );
6663 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6664 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6665 }
6667 // Create the units menu.
6668 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
6669 Glib::ustring stored_unit = prefs->getString("tools.paintbucket", "offsetunits");
6670 if (!stored_unit.empty())
6671 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit.data()));
6672 g_object_set_data( holder, "tracker", tracker );
6673 {
6674 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
6675 gtk_action_group_add_action( mainActions, act );
6676 }
6678 // Offset spinbox
6679 {
6680 eact = create_adjustment_action(
6681 "OffsetAction",
6682 _("Grow/shrink by"), _("Grow/shrink by:"),
6683 _("The amount to grow (positive) or shrink (negative) the created fill path"),
6684 "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
6685 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
6686 0, 0, 0,
6687 paintbucket_offset_changed, 1, 2);
6688 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
6690 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6691 }
6693 /* Auto Gap */
6694 {
6695 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6697 GList* items = 0;
6698 gint count = 0;
6699 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
6700 {
6701 GtkTreeIter iter;
6702 gtk_list_store_append( model, &iter );
6703 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6704 count++;
6705 }
6706 g_list_free( items );
6707 items = 0;
6708 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
6709 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
6710 ege_select_one_action_set_appearance( act2, "compact" );
6711 ege_select_one_action_set_active( act2, prefs->getInt("tools.paintbucket", "autogap", 0) );
6712 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
6713 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
6714 g_object_set_data( holder, "autogap_action", act2 );
6715 }
6717 /* Reset */
6718 {
6719 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
6720 _("Defaults"),
6721 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
6722 GTK_STOCK_CLEAR );
6723 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
6724 gtk_action_group_add_action( mainActions, act );
6725 gtk_action_set_sensitive( act, TRUE );
6726 }
6728 }
6730 /*
6731 Local Variables:
6732 mode:c++
6733 c-file-style:"stroustrup"
6734 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
6735 indent-tabs-mode:nil
6736 fill-column:99
6737 End:
6738 */
6739 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :