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, _("Move mode"),
3696 1, _("Move objects in any direction"),
3697 2, "tweak_move_mode",
3698 -1 );
3700 gtk_list_store_append( model, &iter );
3701 gtk_list_store_set( model, &iter,
3702 0, _("Move in/out mode"),
3703 1, _("Move objects towards cursor; with Shift from cursor"),
3704 2, "tweak_move_mode_inout",
3705 -1 );
3707 gtk_list_store_append( model, &iter );
3708 gtk_list_store_set( model, &iter,
3709 0, _("Move jitter mode"),
3710 1, _("Move objects in random directions"),
3711 2, "tweak_move_mode_jitter",
3712 -1 );
3714 gtk_list_store_append( model, &iter );
3715 gtk_list_store_set( model, &iter,
3716 0, _("Scale mode"),
3717 1, _("Scale objects, with Shift scale up"),
3718 2, "tweak_scale_mode",
3719 -1 );
3721 gtk_list_store_append( model, &iter );
3722 gtk_list_store_set( model, &iter,
3723 0, _("Rotate mode"),
3724 1, _("Rotate objects, with Shift counterclockwise"),
3725 2, "tweak_rotate_mode",
3726 -1 );
3728 gtk_list_store_append( model, &iter );
3729 gtk_list_store_set( model, &iter,
3730 0, _("Duplicate/delete mode"),
3731 1, _("Duplicate objects, with Shift delete"),
3732 2, "tweak_moreless_mode",
3733 -1 );
3735 gtk_list_store_append( model, &iter );
3736 gtk_list_store_set( model, &iter,
3737 0, _("Push mode"),
3738 1, _("Push parts of paths in any direction"),
3739 2, "tweak_push_mode",
3740 -1 );
3742 gtk_list_store_append( model, &iter );
3743 gtk_list_store_set( model, &iter,
3744 0, _("Shrink/grow mode"),
3745 1, _("Shrink (inset) parts of paths; with Shift grow (outset)"),
3746 2, "tweak_shrink_mode",
3747 -1 );
3749 gtk_list_store_append( model, &iter );
3750 gtk_list_store_set( model, &iter,
3751 0, _("Attract/repel mode"),
3752 1, _("Attract parts of paths towards cursor; with Shift from cursor"),
3753 2, "tweak_attract_mode",
3754 -1 );
3756 gtk_list_store_append( model, &iter );
3757 gtk_list_store_set( model, &iter,
3758 0, _("Roughen mode"),
3759 1, _("Roughen parts of paths"),
3760 2, "tweak_roughen_mode",
3761 -1 );
3763 gtk_list_store_append( model, &iter );
3764 gtk_list_store_set( model, &iter,
3765 0, _("Color paint mode"),
3766 1, _("Paint the tool's color upon selected objects"),
3767 2, "tweak_colorpaint_mode",
3768 -1 );
3770 gtk_list_store_append( model, &iter );
3771 gtk_list_store_set( model, &iter,
3772 0, _("Color jitter mode"),
3773 1, _("Jitter the colors of selected objects"),
3774 2, "tweak_colorjitter_mode",
3775 -1 );
3777 gtk_list_store_append( model, &iter );
3778 gtk_list_store_set( model, &iter,
3779 0, _("Blur mode"),
3780 1, _("Blur selected objects more; with Shift, blur less"),
3781 2, "tweak_blur_mode",
3782 -1 );
3785 EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3786 g_object_set( act, "short_label", _("Mode:"), NULL );
3787 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3788 g_object_set_data( holder, "mode_action", act );
3790 ege_select_one_action_set_appearance( act, "full" );
3791 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3792 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3793 ege_select_one_action_set_icon_column( act, 2 );
3794 ege_select_one_action_set_icon_size( act, secondarySize );
3795 ege_select_one_action_set_tooltip_column( act, 1 );
3797 gint mode = prefs->getInt("tools.tweak", "mode", 0);
3798 ege_select_one_action_set_active( act, mode );
3799 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3801 g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3802 }
3804 guint mode = prefs->getInt("tools.tweak", "mode", 0);
3806 {
3807 EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3808 ege_output_action_set_use_markup( act, TRUE );
3809 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3810 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3811 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3812 g_object_set_data( holder, "tweak_channels_label", act);
3813 }
3815 {
3816 InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3817 _("Hue"),
3818 _("In color mode, act on objects' hue"),
3819 NULL,
3820 Inkscape::ICON_SIZE_DECORATION );
3821 //TRANSLATORS: "H" here stands for hue
3822 g_object_set( act, "short_label", _("H"), NULL );
3823 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3824 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3825 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getInt( "tools.tweak", "doh", 1 ) );
3826 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3827 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3828 g_object_set_data( holder, "tweak_doh", act);
3829 }
3830 {
3831 InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3832 _("Saturation"),
3833 _("In color mode, act on objects' saturation"),
3834 NULL,
3835 Inkscape::ICON_SIZE_DECORATION );
3836 //TRANSLATORS: "S" here stands for Saturation
3837 g_object_set( act, "short_label", _("S"), NULL );
3838 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3839 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3840 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getInt( "tools.tweak", "dos", 1 ) );
3841 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3842 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3843 g_object_set_data( holder, "tweak_dos", act );
3844 }
3845 {
3846 InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3847 _("Lightness"),
3848 _("In color mode, act on objects' lightness"),
3849 NULL,
3850 Inkscape::ICON_SIZE_DECORATION );
3851 //TRANSLATORS: "L" here stands for Lightness
3852 g_object_set( act, "short_label", _("L"), NULL );
3853 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3854 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3855 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getInt( "tools.tweak", "dol", 1 ) );
3856 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3857 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3858 g_object_set_data( holder, "tweak_dol", act );
3859 }
3860 {
3861 InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3862 _("Opacity"),
3863 _("In color mode, act on objects' opacity"),
3864 NULL,
3865 Inkscape::ICON_SIZE_DECORATION );
3866 //TRANSLATORS: "O" here stands for Opacity
3867 g_object_set( act, "short_label", _("O"), NULL );
3868 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3869 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3870 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getInt( "tools.tweak", "doo", 1 ) );
3871 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3872 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3873 g_object_set_data( holder, "tweak_doo", act );
3874 }
3876 { /* Fidelity */
3877 gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3878 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3879 EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3880 _("Fidelity"), _("Fidelity:"),
3881 _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3882 "tools.tweak", "fidelity", 50,
3883 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3884 1, 100, 1.0, 10.0,
3885 labels, values, G_N_ELEMENTS(labels),
3886 sp_tweak_fidelity_value_changed, 0.01, 0, 100 );
3887 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3888 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3889 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3890 gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3891 g_object_set_data( holder, "tweak_fidelity", eact );
3892 }
3895 /* Use Pressure button */
3896 {
3897 InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3898 _("Pressure"),
3899 _("Use the pressure of the input device to alter the force of tweak action"),
3900 "use_pressure",
3901 Inkscape::ICON_SIZE_DECORATION );
3902 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3903 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3904 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("tools.tweak", "usepressure", true) );
3905 }
3907 }
3910 //########################
3911 //## Calligraphy ##
3912 //########################
3913 static void update_presets_list (GObject *tbl)
3914 {
3915 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3916 if (g_object_get_data(tbl, "presets_blocked"))
3917 return;
3919 EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3920 if (!sel) {
3921 ege_select_one_action_set_active(sel, 0);
3922 return;
3923 }
3925 int total_prefs = prefs->childCount("tools.calligraphic.preset");
3927 for (int i = 0; i < total_prefs; ++i) {
3928 Glib::ustring preset_path = prefs->getNthChild("tools.calligraphic.preset", i);
3929 /// @todo Remove the use of _getNode()
3930 Inkscape::XML::Node *preset_repr = prefs->_getNode(preset_path);
3932 bool match = true;
3934 for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
3935 iter;
3936 ++iter ) {
3937 const gchar *attr_name = g_quark_to_string(iter->key);
3938 if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
3939 continue;
3940 void *widget = g_object_get_data(tbl, attr_name);
3941 if (widget) {
3942 if (GTK_IS_ADJUSTMENT(widget)) {
3943 double v = prefs->getDouble(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
3944 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
3945 //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
3946 if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
3947 match = false;
3948 break;
3949 }
3950 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
3951 bool v = prefs->getBool(preset_path, attr_name);
3952 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
3953 //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
3954 if ( static_cast<bool>(gtk_toggle_action_get_active(toggle)) != v ) {
3955 match = false;
3956 break;
3957 }
3958 }
3959 }
3960 }
3962 if (match) {
3963 // newly added item is at the same index as the
3964 // save command, so we need to change twice for it to take effect
3965 ege_select_one_action_set_active(sel, 0);
3966 ege_select_one_action_set_active(sel, i + 1); // one-based index
3967 return;
3968 }
3969 }
3971 // no match found
3972 ege_select_one_action_set_active(sel, 0);
3973 }
3975 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3976 {
3977 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3978 prefs->setDouble( "tools.calligraphic", "mass", adj->value * 0.01 );
3979 update_presets_list(tbl);
3980 }
3982 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3983 {
3984 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3985 prefs->setDouble( "tools.calligraphic", "wiggle", adj->value * 0.01 );
3986 update_presets_list(tbl);
3987 }
3989 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3990 {
3991 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3992 prefs->setDouble( "tools.calligraphic", "angle", adj->value );
3993 update_presets_list(tbl);
3994 }
3996 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3997 {
3998 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3999 prefs->setDouble( "tools.calligraphic", "width", adj->value * 0.01 );
4000 update_presets_list(tbl);
4001 }
4003 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
4004 {
4005 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4006 prefs->setDouble("tools.calligraphic", "thinning", adj->value * 0.01 );
4007 update_presets_list(tbl);
4008 }
4010 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
4011 {
4012 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4013 prefs->setDouble( "tools.calligraphic", "flatness", adj->value * 0.01);
4014 update_presets_list(tbl);
4015 }
4017 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
4018 {
4019 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4020 prefs->setDouble( "tools.calligraphic", "tremor", adj->value * 0.01 );
4021 update_presets_list(tbl);
4022 }
4024 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
4025 {
4026 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4027 prefs->setDouble( "tools.calligraphic", "cap_rounding", adj->value );
4028 update_presets_list(tbl);
4029 }
4031 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject* tbl )
4032 {
4033 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4034 prefs->setBool("tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ));
4035 update_presets_list(tbl);
4036 }
4038 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject* tbl )
4039 {
4040 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4041 prefs->setBool("tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ));
4042 update_presets_list(tbl);
4043 }
4045 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject* tbl )
4046 {
4047 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4048 GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
4049 prefs->setBool("tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ));
4050 update_presets_list(tbl);
4051 if (calligraphy_angle )
4052 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
4053 }
4056 static gchar const *const widget_names[] = {
4057 "width",
4058 "mass",
4059 "wiggle",
4060 "angle",
4061 "thinning",
4062 "tremor",
4063 "flatness",
4064 "cap_rounding",
4065 "usepressure",
4066 "tracebackground",
4067 "usetilt"
4068 };
4071 static void sp_dcc_build_presets_list(GObject *tbl)
4072 {
4073 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4075 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4076 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4077 gtk_list_store_clear (model);
4079 {
4080 GtkTreeIter iter;
4081 gtk_list_store_append( model, &iter );
4082 gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4083 }
4085 /// @todo Use public Preferences API instead of node manipulation
4086 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4087 Inkscape::XML::Node *repr = prefs->_getNode("tools.calligraphic.preset", true);
4088 Inkscape::XML::Node *child_repr = repr->firstChild();
4089 int ii=1;
4090 while (child_repr) {
4091 GtkTreeIter iter;
4092 char *preset_name = (char *) child_repr->attribute("name");
4093 gtk_list_store_append( model, &iter );
4094 gtk_list_store_set( model, &iter, 0, preset_name, 1, ii++, -1 );
4095 child_repr = child_repr->next();
4096 }
4098 {
4099 GtkTreeIter iter;
4100 gtk_list_store_append( model, &iter );
4101 gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4102 g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4103 }
4105 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4107 update_presets_list (tbl);
4108 }
4110 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4111 {
4112 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4113 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4114 if (! desktop) return;
4116 if (g_object_get_data(tbl, "presets_blocked"))
4117 return;
4119 Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
4120 if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) {
4121 // dialog cancelled
4122 update_presets_list (tbl);
4123 return;
4124 }
4125 Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
4127 if (profile_name.empty()) {
4128 // empty name entered
4129 update_presets_list (tbl);
4130 return;
4131 }
4133 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4135 int new_index = -1;
4136 Glib::ustring pref_path;
4137 int total_prefs = prefs->childCount("tools.calligraphic.preset");
4139 for (int i = 0; i < total_prefs; ++i) {
4140 Glib::ustring path = prefs->getNthChild("tools.calligraphic.preset", i);
4141 Glib::ustring name = prefs->getString(path, "name");
4142 if (!name.empty() && ( profile_name == name )) {
4143 // we already have preset with this name, replace its values
4144 new_index = i;
4145 pref_path = path;
4146 break;
4147 }
4148 }
4150 if (new_index == -1) {
4151 // no preset with this name, create
4152 /// @todo This is wrong, the name should be encoded in the key
4153 /// to allow deletion of presets
4154 new_index = total_prefs + 1;
4155 gchar *profile_id = g_strdup_printf(".dcc%d", new_index);
4156 pref_path = Glib::ustring("tools.calligraphic.preset") + profile_id;
4157 g_free(profile_id);
4158 }
4160 for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4161 gchar const *const widget_name = widget_names[i];
4162 void *widget = g_object_get_data(tbl, widget_name);
4163 if (widget) {
4164 if (GTK_IS_ADJUSTMENT(widget)) {
4165 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4166 double v = gtk_adjustment_get_value(adj);
4167 prefs->setDouble(pref_path, widget_name, v);
4168 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4169 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4170 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4171 bool v = gtk_toggle_action_get_active(toggle);
4172 prefs->setBool(pref_path, widget_name, v);
4173 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4174 } else {
4175 g_warning("Unknown widget type for preset: %s\n", widget_name);
4176 }
4177 } else {
4178 g_warning("Bad key when writing preset: %s\n", widget_name);
4179 }
4180 }
4181 prefs->setString(pref_path, "name", profile_name);
4183 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4184 sp_dcc_build_presets_list (tbl);
4185 }
4188 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4190 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4192 gint preset_index = ege_select_one_action_get_active( act );
4193 gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4195 if (preset_index == save_presets_index) {
4196 // this is the Save command
4197 sp_dcc_save_profile(NULL, tbl);
4198 return;
4199 }
4201 if (g_object_get_data(tbl, "presets_blocked"))
4202 return;
4204 // preset_index is one-based so we subtract 1
4205 Glib::ustring preset_path = prefs->getNthChild("tools.calligraphic.preset", preset_index - 1);
4207 if (!preset_path.empty()) {
4208 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
4210 /// @todo Remove the use of _getNode() in this fragment, modify
4211 /// the public interface of Preferences if necessary
4212 Inkscape::XML::Node *preset_repr = prefs->_getNode(preset_path);
4214 // Shouldn't this be std::map?
4215 for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
4216 iter;
4217 ++iter ) {
4218 const gchar *attr_name = g_quark_to_string(iter->key);
4219 if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
4220 continue;
4221 void *widget = g_object_get_data(tbl, attr_name);
4222 if (widget) {
4223 if (GTK_IS_ADJUSTMENT(widget)) {
4224 double v = prefs->getDouble(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4225 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4226 gtk_adjustment_set_value(adj, v);
4227 //std::cout << "set adj " << attr_name << " to " << v << "\n";
4228 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4229 int v = prefs->getInt(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4230 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4231 gtk_toggle_action_set_active(toggle, v);
4232 //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4233 } else {
4234 g_warning("Unknown widget type for preset: %s\n", attr_name);
4235 }
4236 } else {
4237 g_warning("Bad key found in a preset record: %s\n", attr_name);
4238 }
4239 }
4240 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4241 }
4243 }
4246 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4247 {
4248 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4249 {
4250 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4252 EgeAdjustmentAction* calligraphy_angle = 0;
4254 {
4255 /* Width */
4256 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4257 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4258 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4259 _("Pen Width"), _("Width:"),
4260 _("The width of the calligraphic pen (relative to the visible canvas area)"),
4261 "tools.calligraphic", "width", 15,
4262 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4263 1, 100, 1.0, 0.0,
4264 labels, values, G_N_ELEMENTS(labels),
4265 sp_ddc_width_value_changed, 0.01, 0, 100 );
4266 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4267 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4268 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4269 }
4271 {
4272 /* Thinning */
4273 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4274 gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4275 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4276 _("Stroke Thinning"), _("Thinning:"),
4277 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4278 "tools.calligraphic", "thinning", 10,
4279 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4280 -100, 100, 1, 0.1,
4281 labels, values, G_N_ELEMENTS(labels),
4282 sp_ddc_velthin_value_changed, 0.01, 0, 100);
4283 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4284 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4285 }
4287 {
4288 /* Angle */
4289 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4290 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4291 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4292 _("Pen Angle"), _("Angle:"),
4293 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4294 "tools.calligraphic", "angle", 30,
4295 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4296 -90.0, 90.0, 1.0, 10.0,
4297 labels, values, G_N_ELEMENTS(labels),
4298 sp_ddc_angle_value_changed, 1, 0 );
4299 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4300 g_object_set_data( holder, "angle_action", eact );
4301 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4302 calligraphy_angle = eact;
4303 }
4305 {
4306 /* Fixation */
4307 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4308 gdouble values[] = {0, 20, 40, 60, 90, 100};
4309 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4310 _("Fixation"), _("Fixation:"),
4311 _("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"),
4312 "tools.calligraphic", "flatness", 90,
4313 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4314 0.0, 100, 1.0, 10.0,
4315 labels, values, G_N_ELEMENTS(labels),
4316 sp_ddc_flatness_value_changed, 0.01, 0, 100 );
4317 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4318 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4319 }
4321 {
4322 /* Cap Rounding */
4323 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4324 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4325 // TRANSLATORS: "cap" means "end" (both start and finish) here
4326 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4327 _("Cap rounding"), _("Caps:"),
4328 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4329 "tools.calligraphic", "cap_rounding", 0.0,
4330 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4331 0.0, 5.0, 0.01, 0.1,
4332 labels, values, G_N_ELEMENTS(labels),
4333 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4334 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4335 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4336 }
4338 {
4339 /* Tremor */
4340 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4341 gdouble values[] = {0, 10, 20, 40, 60, 100};
4342 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4343 _("Stroke Tremor"), _("Tremor:"),
4344 _("Increase to make strokes rugged and trembling"),
4345 "tools.calligraphic", "tremor", 0.0,
4346 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4347 0.0, 100, 1, 0.0,
4348 labels, values, G_N_ELEMENTS(labels),
4349 sp_ddc_tremor_value_changed, 0.01, 0, 100 );
4351 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4352 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4353 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4354 }
4356 {
4357 /* Wiggle */
4358 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4359 gdouble values[] = {0, 20, 40, 60, 100};
4360 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4361 _("Pen Wiggle"), _("Wiggle:"),
4362 _("Increase to make the pen waver and wiggle"),
4363 "tools.calligraphic", "wiggle", 0.0,
4364 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4365 0.0, 100, 1, 0.0,
4366 labels, values, G_N_ELEMENTS(labels),
4367 sp_ddc_wiggle_value_changed, 0.01, 0, 100 );
4368 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4369 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4370 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4371 }
4373 {
4374 /* Mass */
4375 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4376 gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4377 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4378 _("Pen Mass"), _("Mass:"),
4379 _("Increase to make the pen drag behind, as if slowed by inertia"),
4380 "tools.calligraphic", "mass", 2.0,
4381 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4382 0.0, 100, 1, 0.0,
4383 labels, values, G_N_ELEMENTS(labels),
4384 sp_ddc_mass_value_changed, 0.01, 0, 100 );
4385 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4386 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4387 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4388 }
4391 /* Trace Background button */
4392 {
4393 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4394 _("Trace Background"),
4395 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4396 "trace_background",
4397 Inkscape::ICON_SIZE_DECORATION );
4398 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4399 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4400 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("tools.calligraphic", "tracebackground", false) );
4401 g_object_set_data( holder, "tracebackground", act );
4402 }
4404 /* Use Pressure button */
4405 {
4406 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4407 _("Pressure"),
4408 _("Use the pressure of the input device to alter the width of the pen"),
4409 "use_pressure",
4410 Inkscape::ICON_SIZE_DECORATION );
4411 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4412 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4413 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("tools.calligraphic", "usepressure", true) );
4414 g_object_set_data( holder, "usepressure", act );
4415 }
4417 /* Use Tilt button */
4418 {
4419 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4420 _("Tilt"),
4421 _("Use the tilt of the input device to alter the angle of the pen's nib"),
4422 "use_tilt",
4423 Inkscape::ICON_SIZE_DECORATION );
4424 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4425 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4426 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs->getBool("tools.calligraphic", "usetilt", true) );
4427 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("tools.calligraphic", "usetilt", true) );
4428 g_object_set_data( holder, "usetilt", act );
4429 }
4431 /*calligraphic profile */
4432 {
4433 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
4434 EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
4435 ege_select_one_action_set_appearance (act1, "compact");
4436 g_object_set_data (holder, "profile_selector", act1 );
4438 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
4440 sp_dcc_build_presets_list (holder);
4442 g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
4443 gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
4444 }
4445 }
4446 }
4449 //########################
4450 //## Circle / Arc ##
4451 //########################
4453 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4454 {
4455 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4456 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4458 if (v1 == 0 && v2 == 0) {
4459 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4460 gtk_action_set_sensitive( ocb, FALSE );
4461 gtk_action_set_sensitive( make_whole, FALSE );
4462 }
4463 } else {
4464 gtk_action_set_sensitive( ocb, TRUE );
4465 gtk_action_set_sensitive( make_whole, TRUE );
4466 }
4467 }
4469 static void
4470 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4471 {
4472 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4474 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4475 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4476 prefs->setDouble("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
4477 }
4479 // quit if run by the attr_changed listener
4480 if (g_object_get_data( tbl, "freeze" )) {
4481 return;
4482 }
4484 // in turn, prevent listener from responding
4485 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4487 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4489 bool modmade = false;
4490 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4491 items != NULL;
4492 items = items->next)
4493 {
4494 SPItem *item = SP_ITEM(items->data);
4496 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4498 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4499 SPArc *arc = SP_ARC(item);
4501 if (!strcmp(value_name, "start"))
4502 ge->start = (adj->value * M_PI)/ 180;
4503 else
4504 ge->end = (adj->value * M_PI)/ 180;
4506 sp_genericellipse_normalize(ge);
4507 ((SPObject *)arc)->updateRepr();
4508 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4510 modmade = true;
4511 }
4512 }
4514 g_free(namespaced_name);
4516 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4518 sp_arctb_sensitivize( tbl, adj->value, other->value );
4520 if (modmade) {
4521 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4522 _("Arc: Change start/end"));
4523 }
4525 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4526 }
4529 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
4530 {
4531 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
4532 }
4534 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4535 {
4536 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
4537 }
4540 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4541 {
4542 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4543 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4544 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4545 prefs->setBool("tools.shapes.arc", "open", ege_select_one_action_get_active(act) != 0);
4546 }
4548 // quit if run by the attr_changed listener
4549 if (g_object_get_data( tbl, "freeze" )) {
4550 return;
4551 }
4553 // in turn, prevent listener from responding
4554 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4556 bool modmade = false;
4558 if ( ege_select_one_action_get_active(act) != 0 ) {
4559 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4560 items != NULL;
4561 items = items->next)
4562 {
4563 if (SP_IS_ARC((SPItem *) items->data)) {
4564 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4565 repr->setAttribute("sodipodi:open", "true");
4566 SP_OBJECT((SPItem *) items->data)->updateRepr();
4567 modmade = true;
4568 }
4569 }
4570 } else {
4571 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4572 items != NULL;
4573 items = items->next)
4574 {
4575 if (SP_IS_ARC((SPItem *) items->data)) {
4576 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4577 repr->setAttribute("sodipodi:open", NULL);
4578 SP_OBJECT((SPItem *) items->data)->updateRepr();
4579 modmade = true;
4580 }
4581 }
4582 }
4584 if (modmade) {
4585 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4586 _("Arc: Change open/closed"));
4587 }
4589 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4590 }
4592 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4593 {
4594 GtkAdjustment *adj;
4595 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
4596 gtk_adjustment_set_value(adj, 0.0);
4597 gtk_adjustment_value_changed(adj);
4599 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
4600 gtk_adjustment_set_value(adj, 0.0);
4601 gtk_adjustment_value_changed(adj);
4603 spinbutton_defocus( GTK_OBJECT(obj) );
4604 }
4606 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
4607 gchar const */*old_value*/, gchar const */*new_value*/,
4608 bool /*is_interactive*/, gpointer data)
4609 {
4610 GObject *tbl = G_OBJECT(data);
4612 // quit if run by the _changed callbacks
4613 if (g_object_get_data( tbl, "freeze" )) {
4614 return;
4615 }
4617 // in turn, prevent callbacks from responding
4618 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4620 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4621 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4623 GtkAdjustment *adj1,*adj2;
4624 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4625 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4626 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4627 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4629 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4631 char const *openstr = NULL;
4632 openstr = repr->attribute("sodipodi:open");
4633 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4635 if (openstr) {
4636 ege_select_one_action_set_active( ocb, 1 );
4637 } else {
4638 ege_select_one_action_set_active( ocb, 0 );
4639 }
4641 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4642 }
4644 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4645 NULL, /* child_added */
4646 NULL, /* child_removed */
4647 arc_tb_event_attr_changed,
4648 NULL, /* content_changed */
4649 NULL /* order_changed */
4650 };
4653 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4654 {
4655 int n_selected = 0;
4656 Inkscape::XML::Node *repr = NULL;
4658 purge_repr_listener( tbl, tbl );
4660 for (GSList const *items = selection->itemList();
4661 items != NULL;
4662 items = items->next)
4663 {
4664 if (SP_IS_ARC((SPItem *) items->data)) {
4665 n_selected++;
4666 repr = SP_OBJECT_REPR((SPItem *) items->data);
4667 }
4668 }
4670 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4672 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4673 if (n_selected == 0) {
4674 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4675 } else if (n_selected == 1) {
4676 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4677 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4679 if (repr) {
4680 g_object_set_data( tbl, "repr", repr );
4681 Inkscape::GC::anchor(repr);
4682 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4683 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4684 }
4685 } else {
4686 // FIXME: implement averaging of all parameters for multiple selected
4687 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4688 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4689 sp_arctb_sensitivize( tbl, 1, 0 );
4690 }
4691 }
4694 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4695 {
4696 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4698 EgeAdjustmentAction* eact = 0;
4699 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
4702 {
4703 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4704 ege_output_action_set_use_markup( act, TRUE );
4705 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4706 g_object_set_data( holder, "mode_action", act );
4707 }
4709 /* Start */
4710 {
4711 eact = create_adjustment_action( "ArcStartAction",
4712 _("Start"), _("Start:"),
4713 _("The angle (in degrees) from the horizontal to the arc's start point"),
4714 "tools.shapes.arc", "start", 0.0,
4715 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4716 -360.0, 360.0, 1.0, 10.0,
4717 0, 0, 0,
4718 sp_arctb_start_value_changed);
4719 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4720 }
4722 /* End */
4723 {
4724 eact = create_adjustment_action( "ArcEndAction",
4725 _("End"), _("End:"),
4726 _("The angle (in degrees) from the horizontal to the arc's end point"),
4727 "tools.shapes.arc", "end", 0.0,
4728 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4729 -360.0, 360.0, 1.0, 10.0,
4730 0, 0, 0,
4731 sp_arctb_end_value_changed);
4732 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4733 }
4735 /* Segments / Pie checkbox */
4736 {
4737 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4739 GtkTreeIter iter;
4740 gtk_list_store_append( model, &iter );
4741 gtk_list_store_set( model, &iter,
4742 0, _("Closed arc"),
4743 1, _("Switch to segment (closed shape with two radii)"),
4744 2, "circle_closed_arc",
4745 -1 );
4747 gtk_list_store_append( model, &iter );
4748 gtk_list_store_set( model, &iter,
4749 0, _("Open Arc"),
4750 1, _("Switch to arc (unclosed shape)"),
4751 2, "circle_open_arc",
4752 -1 );
4754 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4755 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4756 g_object_set_data( holder, "open_action", act );
4758 ege_select_one_action_set_appearance( act, "full" );
4759 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4760 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4761 ege_select_one_action_set_icon_column( act, 2 );
4762 ege_select_one_action_set_icon_size( act, secondarySize );
4763 ege_select_one_action_set_tooltip_column( act, 1 );
4765 bool isClosed = !prefs->getBool("tools.shapes.arc", "open", false);
4766 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4767 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4768 }
4770 /* Make Whole */
4771 {
4772 InkAction* inky = ink_action_new( "ArcResetAction",
4773 _("Make whole"),
4774 _("Make the shape a whole ellipse, not arc or segment"),
4775 "reset_circle",
4776 secondarySize );
4777 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4778 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4779 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4780 g_object_set_data( holder, "make_whole", inky );
4781 }
4783 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4784 // sensitivize make whole and open checkbox
4785 {
4786 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4787 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4788 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4789 }
4792 sigc::connection *connection = new sigc::connection(
4793 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4794 );
4795 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4796 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4797 }
4802 // toggle button callbacks and updaters
4804 //########################
4805 //## Dropper ##
4806 //########################
4808 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4809 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4810 prefs->setInt( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4811 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4812 if ( set_action ) {
4813 if ( gtk_toggle_action_get_active( act ) ) {
4814 gtk_action_set_sensitive( set_action, TRUE );
4815 } else {
4816 gtk_action_set_sensitive( set_action, FALSE );
4817 }
4818 }
4820 spinbutton_defocus(GTK_OBJECT(tbl));
4821 }
4823 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4824 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4825 prefs->setInt( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4826 spinbutton_defocus(GTK_OBJECT(tbl));
4827 }
4830 /**
4831 * Dropper auxiliary toolbar construction and setup.
4832 *
4833 * TODO: Would like to add swatch of current color.
4834 * TODO: Add queue of last 5 or so colors selected with new swatches so that
4835 * can drag and drop places. Will provide a nice mixing palette.
4836 */
4837 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4838 {
4839 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4840 gint pickAlpha = prefs->getInt( "tools.dropper", "pick", 1 );
4842 {
4843 EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4844 ege_output_action_set_use_markup( act, TRUE );
4845 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4846 }
4848 {
4849 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4850 _("Pick opacity"),
4851 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4852 NULL,
4853 Inkscape::ICON_SIZE_DECORATION );
4854 g_object_set( act, "short_label", _("Pick"), NULL );
4855 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4856 g_object_set_data( holder, "pick_action", act );
4857 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4858 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4859 }
4861 {
4862 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4863 _("Assign opacity"),
4864 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4865 NULL,
4866 Inkscape::ICON_SIZE_DECORATION );
4867 g_object_set( act, "short_label", _("Assign"), NULL );
4868 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4869 g_object_set_data( holder, "set_action", act );
4870 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getInt( "tools.dropper", "setalpha", 1 ) );
4871 // make sure it's disabled if we're not picking alpha
4872 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4873 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4874 }
4875 }
4878 //########################
4879 //## LPETool ##
4880 //########################
4882 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
4884 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
4885 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
4886 {
4887 using namespace Inkscape::LivePathEffect;
4889 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
4890 SPEventContext *ec = desktop->event_context;
4891 if (!SP_IS_LPETOOL_CONTEXT(ec)) {
4892 return;
4893 }
4895 // only take action if run by the attr_changed listener
4896 if (!g_object_get_data(tbl, "freeze")) {
4897 // in turn, prevent listener from responding
4898 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
4900 gint mode = ege_select_one_action_get_active(act);
4901 EffectType type = lpesubtools[mode];
4903 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4904 bool success = lpetool_try_construction(lc, type);
4905 if (success) {
4906 // since the construction was already performed, we set the state back to inactive
4907 ege_select_one_action_set_active(act, 0);
4908 mode = 0;
4909 } else {
4910 // switch to the chosen subtool
4911 SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
4912 }
4914 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4915 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4916 prefs->setInt( "tools.lpetool", "mode", mode );
4917 }
4919 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
4920 }
4921 }
4923 void sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject */*tbl*/)
4924 {
4925 SPEventContext *ec = selection->desktop()->event_context;
4926 if (!SP_IS_LPETOOL_CONTEXT(ec))
4927 return;
4929 lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
4930 }
4932 void
4933 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
4934 {
4935 using namespace Inkscape::LivePathEffect;
4936 SPEventContext *ec = selection->desktop()->event_context;
4937 if (!SP_IS_LPETOOL_CONTEXT(ec))
4938 return;
4939 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
4941 lpetool_delete_measuring_items(lc);
4942 lpetool_create_measuring_items(lc, selection);
4944 // activate line segment combo box if a single item with LPELineSegment is selected
4945 GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
4946 SPItem *item = selection->singleItem();
4947 if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
4948 SPLPEItem *lpeitem = SP_LPE_ITEM(item);
4949 Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
4950 if (lpe && lpe->effectType() == LINE_SEGMENT) {
4951 LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
4952 g_object_set_data(tbl, "currentlpe", lpe);
4953 g_object_set_data(tbl, "currentlpeitem", lpeitem);
4954 gtk_action_set_sensitive(w, TRUE);
4955 ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
4956 } else {
4957 g_object_set_data(tbl, "currentlpe", NULL);
4958 g_object_set_data(tbl, "currentlpeitem", NULL);
4959 gtk_action_set_sensitive(w, FALSE);
4960 }
4961 } else {
4962 g_object_set_data(tbl, "currentlpe", NULL);
4963 g_object_set_data(tbl, "currentlpeitem", NULL);
4964 gtk_action_set_sensitive(w, FALSE);
4965 }
4966 }
4968 static void
4969 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
4970 SPDesktop *desktop = static_cast<SPDesktop *>(data);
4971 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4973 bool show = gtk_toggle_action_get_active( act );
4974 prefs->setInt("tools.lpetool", "show_bbox", show ? 1 : 0);
4976 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
4977 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4978 lpetool_context_reset_limiting_bbox(lc);
4979 }
4980 }
4982 static void
4983 lpetool_toggle_show_measuring_info (GtkToggleAction *act, GObject *tbl) {
4984 SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
4985 if (!tools_isactive(desktop, TOOLS_LPETOOL))
4986 return;
4988 GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
4989 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4990 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
4991 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4992 bool show = gtk_toggle_action_get_active( act );
4993 prefs->setInt("tools.lpetool", "show_measuring_info", show ? 1 : 0);
4994 lpetool_show_measuring_info(lc, show);
4995 gtk_action_set_sensitive(GTK_ACTION(unitact), show);
4996 }
4997 }
4999 static void lpetool_unit_changed(GtkAction* /*act*/, GObject* tbl) {
5000 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
5001 SPUnit const *unit = tracker->getActiveUnit();
5002 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5003 prefs->setInt("tools.lpetool", "unitid", unit->unit_id);
5005 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5006 if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
5007 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5008 lpetool_delete_measuring_items(lc);
5009 lpetool_create_measuring_items(lc);
5010 }
5011 }
5013 static void
5014 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
5015 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5016 Inkscape::Selection *selection = desktop->selection;
5018 boost::optional<Geom::Rect> bbox = selection->bounds();
5020 if (bbox) {
5021 Geom::Point A(bbox->min());
5022 Geom::Point B(bbox->max());
5024 A *= desktop->doc2dt();
5025 B *= desktop->doc2dt();
5027 // TODO: should we provide a way to store points in prefs?
5028 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5029 prefs->setDouble("tools.lpetool", "bbox_upperleftx", A[Geom::X]);
5030 prefs->setDouble("tools.lpetool", "bbox_upperlefty", A[Geom::Y]);
5031 prefs->setDouble("tools.lpetool", "bbox_lowerrightx", B[Geom::X]);
5032 prefs->setDouble("tools.lpetool", "bbox_lowerrighty", B[Geom::Y]);
5034 lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
5035 }
5037 gtk_toggle_action_set_active(act, false);
5038 }
5040 static void
5041 sp_line_segment_build_list(GObject *tbl)
5042 {
5043 g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
5045 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
5046 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
5047 gtk_list_store_clear (model);
5049 // TODO: we add the entries of rht combo box manually; later this should be done automatically
5050 {
5051 GtkTreeIter iter;
5052 gtk_list_store_append( model, &iter );
5053 gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
5054 gtk_list_store_append( model, &iter );
5055 gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
5056 gtk_list_store_append( model, &iter );
5057 gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
5058 gtk_list_store_append( model, &iter );
5059 gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
5060 }
5062 g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5063 }
5065 static void
5066 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
5067 using namespace Inkscape::LivePathEffect;
5069 // quit if run by the attr_changed listener
5070 if (g_object_get_data(tbl, "freeze")) {
5071 return;
5072 }
5074 // in turn, prevent listener from responding
5075 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5077 LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
5078 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5079 if (lpeitem) {
5080 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5081 lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
5082 sp_lpe_item_update_patheffect(lpeitem, true, true);
5083 }
5085 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5086 }
5088 static void
5089 lpetool_open_lpe_dialog (GtkToggleAction *act, gpointer data) {
5090 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5092 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5093 sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5094 }
5095 gtk_toggle_action_set_active(act, false);
5096 }
5098 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5099 {
5100 UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5101 tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5102 g_object_set_data(holder, "tracker", tracker);
5103 SPUnit const *unit = tracker->getActiveUnit();
5105 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5106 prefs->setInt("tools.lpetool", "unitid", unit->unit_id);
5108 /** Automatically create a list of LPEs that get added to the toolbar **/
5109 {
5110 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5112 GtkTreeIter iter;
5114 // the first toggle button represents the state that no subtool is active (remove this when
5115 // this can be modeled by EgeSelectOneAction or some other action)
5116 gtk_list_store_append( model, &iter );
5117 gtk_list_store_set( model, &iter,
5118 0, _("All inactive"),
5119 1, _("No geometric tool is active"),
5120 2, _("all_inactive"),
5121 -1 );
5123 Inkscape::LivePathEffect::EffectType type;
5124 for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5125 type = lpesubtools[i];
5126 gtk_list_store_append( model, &iter );
5127 gtk_list_store_set( model, &iter,
5128 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5129 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5130 2, Inkscape::LivePathEffect::LPETypeConverter.get_key(type).c_str(),
5131 -1 );
5132 }
5134 EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5135 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5136 g_object_set_data( holder, "lpetool_mode_action", act );
5138 ege_select_one_action_set_appearance( act, "full" );
5139 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5140 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5141 ege_select_one_action_set_icon_column( act, 2 );
5142 ege_select_one_action_set_tooltip_column( act, 1 );
5144 gint lpeToolMode = prefs->getInt("tools.lpetool", "mode", 0);
5145 ege_select_one_action_set_active( act, lpeToolMode );
5146 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5147 }
5149 /* Show limiting bounding box */
5150 {
5151 InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5152 _("Show limiting bounding box"),
5153 _("Show bounding box (used to cut infinite lines)"),
5154 "lpetool_show_bbox",
5155 Inkscape::ICON_SIZE_DECORATION );
5156 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5157 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5158 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getInt( "tools.lpetool", "show_bbox", 1 ) );
5159 }
5161 /* Set limiting bounding box to bbox of current selection */
5162 {
5163 InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5164 _("Get limiting bounding box from selection"),
5165 _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5166 "lpetool_set_bbox",
5167 Inkscape::ICON_SIZE_DECORATION );
5168 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5169 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5170 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5171 }
5174 /* Combo box to choose line segment type */
5175 {
5176 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5177 EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5178 ege_select_one_action_set_appearance (act, "compact");
5179 g_object_set_data (holder, "lpetool_line_segment_action", act );
5181 g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5183 sp_line_segment_build_list (holder);
5185 g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5186 gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5187 gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5188 }
5190 /* Display measuring info for selected items */
5191 {
5192 InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5193 _("Display measuring info"),
5194 _("Display measuring info for selected items"),
5195 "lpetool_measuring_info",
5196 Inkscape::ICON_SIZE_DECORATION );
5197 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5198 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
5199 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getInt( "tools.lpetool", "show_measuring_info", 1 ) );
5200 }
5202 // add the units menu
5203 {
5204 GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5205 gtk_action_group_add_action( mainActions, act );
5206 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5207 g_object_set_data(holder, "lpetool_units_action", act);
5208 gtk_action_set_sensitive(act, prefs->getInt("tools.lpetool", "show_measuring_info", 1));
5209 }
5211 /* Open LPE dialog (to adapt parameters numerically) */
5212 {
5213 InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
5214 _("Open LPE dialog"),
5215 _("Open LPE dialog (to adapt parameters numerically)"),
5216 "lpetool_open_lpe_dialog",
5217 Inkscape::ICON_SIZE_DECORATION );
5218 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5219 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
5220 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5221 }
5223 //watch selection
5224 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5226 sigc::connection *c_selection_modified =
5227 new sigc::connection (sp_desktop_selection (desktop)->connectModified
5228 (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
5229 pool->add_connection ("selection-modified", c_selection_modified);
5231 sigc::connection *c_selection_changed =
5232 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5233 (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5234 pool->add_connection ("selection-changed", c_selection_changed);
5235 }
5237 //########################
5238 //## Eraser ##
5239 //########################
5241 static void sp_erc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
5242 {
5243 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5244 prefs->setDouble( "tools.eraser", "width", adj->value * 0.01 );
5245 update_presets_list(tbl);
5246 }
5248 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5249 {
5250 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5251 gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
5252 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5253 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5254 prefs->setInt( "tools.eraser", "mode", eraserMode );
5255 }
5257 // only take action if run by the attr_changed listener
5258 if (!g_object_get_data( tbl, "freeze" )) {
5259 // in turn, prevent listener from responding
5260 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5262 if ( eraserMode != 0 ) {
5263 } else {
5264 }
5265 // TODO finish implementation
5267 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5268 }
5269 }
5271 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5272 {
5273 {
5274 /* Width */
5275 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5276 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5277 EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5278 _("Pen Width"), _("Width:"),
5279 _("The width of the eraser pen (relative to the visible canvas area)"),
5280 "tools.eraser", "width", 15,
5281 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5282 1, 100, 1.0, 0.0,
5283 labels, values, G_N_ELEMENTS(labels),
5284 sp_erc_width_value_changed, 0.01, 0, 100 );
5285 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5286 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5287 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5288 }
5290 {
5291 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5293 GtkTreeIter iter;
5294 gtk_list_store_append( model, &iter );
5295 gtk_list_store_set( model, &iter,
5296 0, _("Delete"),
5297 1, _("Delete objects touched by the eraser"),
5298 2, "delete_object",
5299 -1 );
5301 gtk_list_store_append( model, &iter );
5302 gtk_list_store_set( model, &iter,
5303 0, _("Cut"),
5304 1, _("Cut out from objects"),
5305 2, "difference",
5306 -1 );
5308 EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5309 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5310 g_object_set_data( holder, "eraser_mode_action", act );
5312 ege_select_one_action_set_appearance( act, "full" );
5313 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5314 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5315 ege_select_one_action_set_icon_column( act, 2 );
5316 ege_select_one_action_set_tooltip_column( act, 1 );
5318 /// @todo Convert to boolean?
5319 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5320 gint eraserMode = (prefs->getInt("tools.eraser", "mode", 0) != 0) ? 1 : 0;
5321 ege_select_one_action_set_active( act, eraserMode );
5322 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
5323 }
5325 }
5327 //########################
5328 //## Text Toolbox ##
5329 //########################
5330 /*
5331 static void
5332 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
5333 {
5334 //Call back for letter sizing spinbutton
5335 }
5337 static void
5338 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
5339 {
5340 //Call back for line height spinbutton
5341 }
5343 static void
5344 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5345 {
5346 //Call back for horizontal kerning spinbutton
5347 }
5349 static void
5350 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5351 {
5352 //Call back for vertical kerning spinbutton
5353 }
5355 static void
5356 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
5357 {
5358 //Call back for letter rotation spinbutton
5359 }*/
5361 namespace {
5363 bool popdown_visible = false;
5364 bool popdown_hasfocus = false;
5366 void
5367 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
5368 {
5369 SPStyle *query =
5370 sp_style_new (SP_ACTIVE_DOCUMENT);
5372 // int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5374 int result_family =
5375 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5377 int result_style =
5378 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5380 int result_numbers =
5381 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5383 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5385 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5386 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
5387 // there are no texts in selection, read from prefs
5389 Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
5390 if (repr) {
5391 sp_style_read_from_repr (query, repr);
5392 if (g_object_get_data(tbl, "text_style_from_prefs")) {
5393 // do not reset the toolbar style from prefs if we already did it last time
5394 sp_style_unref(query);
5395 return;
5396 }
5397 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
5398 } else {
5399 sp_style_unref(query);
5400 return;
5401 }
5402 } else {
5403 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
5404 }
5406 if (query->text)
5407 {
5408 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
5409 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5410 gtk_entry_set_text (GTK_ENTRY (entry), "");
5412 } else if (query->text->font_specification.value || query->text->font_family.value) {
5414 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5416 // Get the font that corresponds
5417 Glib::ustring familyName;
5419 font_instance * font = font_factory::Default()->FaceFromStyle(query);
5420 if (font) {
5421 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
5422 font->Unref();
5423 font = NULL;
5424 }
5426 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
5428 Gtk::TreePath path;
5429 try {
5430 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
5431 } catch (...) {
5432 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
5433 sp_style_unref(query);
5434 return;
5435 }
5437 GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5438 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5440 g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
5442 gtk_tree_selection_select_path (tselection, path.gobj());
5443 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5445 g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
5446 }
5448 //Size
5449 {
5450 GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
5451 gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
5452 g_object_set_data(tbl, "size-block", gpointer(1));
5453 gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
5454 g_object_set_data(tbl, "size-block", gpointer(0));
5455 g_free(str);
5456 }
5458 //Anchor
5459 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
5460 {
5461 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
5462 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5463 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5464 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5465 }
5466 else
5467 {
5468 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
5469 {
5470 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
5471 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5472 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5473 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5474 }
5475 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
5476 {
5477 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
5478 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5479 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5480 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5481 }
5482 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
5483 {
5484 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
5485 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5486 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5487 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5488 }
5489 }
5491 //Style
5492 {
5493 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
5495 gboolean active = gtk_toggle_button_get_active (button);
5496 gboolean check = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
5498 if (active != check)
5499 {
5500 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5501 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5502 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5503 }
5504 }
5506 {
5507 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
5509 gboolean active = gtk_toggle_button_get_active (button);
5510 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
5512 if (active != check)
5513 {
5514 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5515 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5516 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5517 }
5518 }
5520 //Orientation
5521 //locking both buttons, changing one affect all group (both)
5522 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
5523 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5525 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
5526 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
5528 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
5529 {
5530 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5531 }
5532 else
5533 {
5534 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
5535 }
5536 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5537 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
5538 }
5540 sp_style_unref(query);
5541 }
5543 void
5544 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
5545 {
5546 sp_text_toolbox_selection_changed (selection, tbl);
5547 }
5549 void
5550 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
5551 {
5552 sp_text_toolbox_selection_changed (NULL, tbl);
5553 }
5555 void
5556 sp_text_toolbox_family_changed (GtkTreeSelection *selection,
5557 GObject *tbl)
5558 {
5559 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5560 GtkTreeModel *model = 0;
5561 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5562 GtkTreeIter iter;
5563 char *family = 0;
5565 gdk_pointer_ungrab (GDK_CURRENT_TIME);
5566 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5568 if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
5569 return;
5570 }
5572 gtk_tree_model_get (model, &iter, 0, &family, -1);
5574 if (g_object_get_data (G_OBJECT (selection), "block"))
5575 {
5576 gtk_entry_set_text (GTK_ENTRY (entry), family);
5577 return;
5578 }
5580 gtk_entry_set_text (GTK_ENTRY (entry), family);
5582 SPStyle *query =
5583 sp_style_new (SP_ACTIVE_DOCUMENT);
5585 int result_fontspec =
5586 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5588 //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5590 SPCSSAttr *css = sp_repr_css_attr_new ();
5593 // First try to get the font spec from the stored value
5594 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
5596 if (fontSpec.empty()) {
5597 // Construct a new font specification if it does not yet exist
5598 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5599 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5600 fontFromStyle->Unref();
5601 }
5603 if (!fontSpec.empty()) {
5604 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
5605 if (!newFontSpec.empty() && fontSpec != newFontSpec) {
5606 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
5607 if (font) {
5608 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5610 // Set all the these just in case they were altered when finding the best
5611 // match for the new family and old style...
5613 gchar c[256];
5615 font->Family(c, 256);
5616 sp_repr_css_set_property (css, "font-family", c);
5618 font->Attribute( "weight", c, 256);
5619 sp_repr_css_set_property (css, "font-weight", c);
5621 font->Attribute("style", c, 256);
5622 sp_repr_css_set_property (css, "font-style", c);
5624 font->Attribute("stretch", c, 256);
5625 sp_repr_css_set_property (css, "font-stretch", c);
5627 font->Attribute("variant", c, 256);
5628 sp_repr_css_set_property (css, "font-variant", c);
5630 font->Unref();
5631 }
5632 }
5633 }
5635 // If querying returned nothing, set the default style of the tool (for new texts)
5636 if (result_fontspec == QUERY_STYLE_NOTHING)
5637 {
5638 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5639 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
5640 }
5641 else
5642 {
5643 sp_desktop_set_style (desktop, css, true, true);
5644 }
5646 sp_style_unref(query);
5648 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5649 _("Text: Change font family"));
5650 sp_repr_css_attr_unref (css);
5651 g_free(family);
5652 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5654 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5655 }
5657 /* This is where execution comes when the contents of the font family box have been completed
5658 by the press of the return key */
5659 void
5660 sp_text_toolbox_family_entry_activate (GtkEntry *entry,
5661 GObject *tbl)
5662 {
5663 const char *family = gtk_entry_get_text (entry); // Fetch the requested font family
5665 // Try to match that to a known font. If not, then leave current font alone and remain focused on text box
5666 try {
5667 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
5668 GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5669 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5670 gtk_tree_selection_select_path (selection, path.gobj());
5671 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5672 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5673 } catch (...) {
5674 if (family && strlen (family))
5675 {
5676 gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5677 }
5678 }
5679 }
5681 void
5682 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
5683 gpointer data)
5684 {
5685 if (g_object_get_data (G_OBJECT (button), "block")) return;
5686 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
5687 int prop = GPOINTER_TO_INT(data);
5689 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5690 SPCSSAttr *css = sp_repr_css_attr_new ();
5692 switch (prop)
5693 {
5694 case 0:
5695 {
5696 sp_repr_css_set_property (css, "text-anchor", "start");
5697 sp_repr_css_set_property (css, "text-align", "start");
5698 break;
5699 }
5700 case 1:
5701 {
5702 sp_repr_css_set_property (css, "text-anchor", "middle");
5703 sp_repr_css_set_property (css, "text-align", "center");
5704 break;
5705 }
5707 case 2:
5708 {
5709 sp_repr_css_set_property (css, "text-anchor", "end");
5710 sp_repr_css_set_property (css, "text-align", "end");
5711 break;
5712 }
5714 case 3:
5715 {
5716 sp_repr_css_set_property (css, "text-anchor", "start");
5717 sp_repr_css_set_property (css, "text-align", "justify");
5718 break;
5719 }
5720 }
5722 SPStyle *query =
5723 sp_style_new (SP_ACTIVE_DOCUMENT);
5724 int result_numbers =
5725 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5727 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5728 if (result_numbers == QUERY_STYLE_NOTHING)
5729 {
5730 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5731 }
5733 sp_style_unref(query);
5735 sp_desktop_set_style (desktop, css, true, true);
5736 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5737 _("Text: Change alignment"));
5738 sp_repr_css_attr_unref (css);
5740 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5741 }
5743 void
5744 sp_text_toolbox_style_toggled (GtkToggleButton *button,
5745 gpointer data)
5746 {
5747 if (g_object_get_data (G_OBJECT (button), "block")) return;
5749 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5750 SPCSSAttr *css = sp_repr_css_attr_new ();
5751 int prop = GPOINTER_TO_INT(data);
5752 bool active = gtk_toggle_button_get_active (button);
5754 SPStyle *query =
5755 sp_style_new (SP_ACTIVE_DOCUMENT);
5757 int result_fontspec =
5758 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5760 //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5761 //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5762 //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5764 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
5765 Glib::ustring newFontSpec = "";
5767 if (fontSpec.empty()) {
5768 // Construct a new font specification if it does not yet exist
5769 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5770 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5771 fontFromStyle->Unref();
5772 }
5774 switch (prop)
5775 {
5776 case 0:
5777 {
5778 if (!fontSpec.empty()) {
5779 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
5780 }
5781 if (fontSpec != newFontSpec) {
5782 // Don't even set the bold if the font didn't exist on the system
5783 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
5784 }
5785 break;
5786 }
5788 case 1:
5789 {
5790 if (!fontSpec.empty()) {
5791 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
5792 }
5793 if (fontSpec != newFontSpec) {
5794 // Don't even set the italic if the font didn't exist on the system
5795 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
5796 }
5797 break;
5798 }
5799 }
5801 if (!newFontSpec.empty()) {
5802 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5803 }
5805 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5806 if (result_fontspec == QUERY_STYLE_NOTHING)
5807 {
5808 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5809 }
5811 sp_style_unref(query);
5813 sp_desktop_set_style (desktop, css, true, true);
5814 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5815 _("Text: Change font style"));
5816 sp_repr_css_attr_unref (css);
5818 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5819 }
5821 void
5822 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
5823 gpointer data)
5824 {
5825 if (g_object_get_data (G_OBJECT (button), "block")) {
5826 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5827 return;
5828 }
5830 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5831 SPCSSAttr *css = sp_repr_css_attr_new ();
5832 int prop = GPOINTER_TO_INT(data);
5834 switch (prop)
5835 {
5836 case 0:
5837 {
5838 sp_repr_css_set_property (css, "writing-mode", "lr");
5839 break;
5840 }
5842 case 1:
5843 {
5844 sp_repr_css_set_property (css, "writing-mode", "tb");
5845 break;
5846 }
5847 }
5849 SPStyle *query =
5850 sp_style_new (SP_ACTIVE_DOCUMENT);
5851 int result_numbers =
5852 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5854 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5855 if (result_numbers == QUERY_STYLE_NOTHING)
5856 {
5857 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5858 }
5860 sp_desktop_set_style (desktop, css, true, true);
5861 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5862 _("Text: Change orientation"));
5863 sp_repr_css_attr_unref (css);
5865 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5866 }
5868 gboolean
5869 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5870 {
5871 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5872 if (!desktop) return FALSE;
5874 switch (get_group0_keyval (event)) {
5875 case GDK_Escape: // defocus
5876 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5877 sp_text_toolbox_selection_changed (NULL, tbl); // update
5878 return TRUE; // I consumed the event
5879 break;
5880 }
5881 return FALSE;
5882 }
5884 gboolean
5885 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5886 {
5887 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5888 if (!desktop) return FALSE;
5890 switch (get_group0_keyval (event)) {
5891 case GDK_KP_Enter:
5892 case GDK_Return:
5893 case GDK_Escape: // defocus
5894 gtk_widget_hide (w);
5895 popdown_visible = false;
5896 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5897 return TRUE; // I consumed the event
5898 break;
5899 case GDK_w:
5900 case GDK_W:
5901 if (event->state & GDK_CONTROL_MASK) {
5902 gtk_widget_hide (w);
5903 popdown_visible = false;
5904 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5905 return TRUE; // I consumed the event
5906 }
5907 break;
5908 }
5909 return FALSE;
5910 }
5913 void
5914 sp_text_toolbox_size_changed (GtkComboBox *cbox,
5915 GObject *tbl)
5916 {
5917 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5919 if (g_object_get_data (tbl, "size-block")) return;
5921 // If this is not from selecting a size in the list (in which case get_active will give the
5922 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5923 // process this event. This fixes GTK's stupid insistence on sending an activate change every
5924 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5925 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5926 return;
5928 gdouble value = -1;
5929 {
5930 gchar *endptr;
5931 gchar *const text = gtk_combo_box_get_active_text(cbox);
5932 if (text) {
5933 value = g_strtod(text, &endptr);
5934 if (endptr == text) { // Conversion failed, non-numeric input.
5935 value = -1;
5936 }
5937 g_free(text);
5938 }
5939 }
5940 if (value <= 0) {
5941 return; // could not parse value
5942 }
5944 SPCSSAttr *css = sp_repr_css_attr_new ();
5945 Inkscape::CSSOStringStream osfs;
5946 osfs << value;
5947 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5949 SPStyle *query =
5950 sp_style_new (SP_ACTIVE_DOCUMENT);
5951 int result_numbers =
5952 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5954 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5955 if (result_numbers == QUERY_STYLE_NOTHING)
5956 {
5957 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5958 }
5960 sp_style_unref(query);
5962 sp_desktop_set_style (desktop, css, true, true);
5963 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5964 _("Text: Change font size"));
5965 sp_repr_css_attr_unref (css);
5967 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5968 }
5970 gboolean
5971 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5972 {
5973 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5974 if (!desktop) return FALSE;
5976 if (!g_object_get_data (tbl, "esc-pressed")) {
5977 g_object_set_data (tbl, "enter-pressed", gpointer(1));
5978 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5979 sp_text_toolbox_size_changed (cbox, tbl);
5980 g_object_set_data (tbl, "enter-pressed", gpointer(0));
5981 }
5982 return FALSE; // I consumed the event
5983 }
5986 gboolean
5987 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5988 {
5989 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5990 if (!desktop) return FALSE;
5992 switch (get_group0_keyval (event)) {
5993 case GDK_Escape: // defocus
5994 g_object_set_data (tbl, "esc-pressed", gpointer(1));
5995 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5996 g_object_set_data (tbl, "esc-pressed", gpointer(0));
5997 return TRUE; // I consumed the event
5998 break;
5999 case GDK_Return: // defocus
6000 case GDK_KP_Enter:
6001 g_object_set_data (tbl, "enter-pressed", gpointer(1));
6002 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6003 sp_text_toolbox_size_changed (cbox, tbl);
6004 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6005 g_object_set_data (tbl, "enter-pressed", gpointer(0));
6006 return TRUE; // I consumed the event
6007 break;
6008 }
6009 return FALSE;
6010 }
6012 void
6013 sp_text_toolbox_text_popdown_clicked (GtkButton */*button*/,
6014 GObject *tbl)
6015 {
6016 GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
6017 GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6018 int x, y;
6020 if (!popdown_visible)
6021 {
6022 gdk_window_get_origin (widget->window, &x, &y);
6023 gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
6024 gtk_widget_show_all (popdown);
6025 //sp_transientize (popdown);
6027 gdk_pointer_grab (widget->window, TRUE,
6028 GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
6029 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
6030 GDK_POINTER_MOTION_MASK),
6031 NULL, NULL, GDK_CURRENT_TIME);
6033 gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
6035 popdown_visible = true;
6036 }
6037 else
6038 {
6039 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6040 gdk_pointer_ungrab (GDK_CURRENT_TIME);
6041 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
6042 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6043 gtk_widget_hide (popdown);
6044 popdown_visible = false;
6045 }
6046 }
6048 gboolean
6049 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
6050 GdkEventFocus */*event*/,
6051 GObject */*tbl*/)
6052 {
6053 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
6054 return FALSE;
6055 }
6057 gboolean
6058 sp_text_toolbox_popdown_focus_out (GtkWidget *popdown,
6059 GdkEventFocus */*event*/,
6060 GObject */*tbl*/)
6061 {
6062 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6064 if (popdown_hasfocus) {
6065 gtk_widget_hide (popdown);
6066 popdown_hasfocus = false;
6067 popdown_visible = false;
6068 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6069 return TRUE;
6070 }
6071 return FALSE;
6072 }
6074 gboolean
6075 sp_text_toolbox_popdown_focus_in (GtkWidget */*popdown*/,
6076 GdkEventFocus */*event*/,
6077 GObject */*tbl*/)
6078 {
6079 popdown_hasfocus = true;
6080 return TRUE;
6081 }
6084 void
6085 cell_data_func (GtkTreeViewColumn */*column*/,
6086 GtkCellRenderer *cell,
6087 GtkTreeModel *tree_model,
6088 GtkTreeIter *iter,
6089 gpointer /*data*/)
6090 {
6091 gchar *family;
6092 gtk_tree_model_get(tree_model, iter, 0, &family, -1);
6093 gchar *const family_escaped = g_markup_escape_text(family, -1);
6095 static char const *const sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
6096 gchar *const sample_escaped = g_markup_escape_text(sample, -1);
6098 std::stringstream markup;
6099 markup << family_escaped << " <span foreground='darkgray' font_family='"
6100 << family_escaped << "'>" << sample_escaped << "</span>";
6101 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6103 g_free(family);
6104 g_free(family_escaped);
6105 g_free(sample_escaped);
6106 }
6108 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
6109 GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
6110 if (completion) {
6111 gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
6112 g_object_unref (completion);
6113 }
6114 }
6116 GtkWidget*
6117 sp_text_toolbox_new (SPDesktop *desktop)
6118 {
6119 GtkToolbar *tbl = GTK_TOOLBAR(gtk_toolbar_new());
6120 GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
6122 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
6123 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
6125 GtkTooltips *tt = gtk_tooltips_new();
6126 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
6128 ////////////Family
6129 //Window
6130 GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
6131 gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
6133 //Entry
6134 GtkWidget *entry = gtk_entry_new ();
6135 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
6136 GtkEntryCompletion *completion = gtk_entry_completion_new ();
6137 gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
6138 gtk_entry_completion_set_text_column (completion, 0);
6139 gtk_entry_completion_set_minimum_key_length (completion, 1);
6140 g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
6141 gtk_entry_set_completion (GTK_ENTRY(entry), completion);
6142 gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
6143 gtk_toolbar_append_widget( tbl, entry, "", "" );
6144 g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
6146 //Button
6147 GtkWidget *button = gtk_button_new ();
6148 gtk_container_add (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
6149 gtk_toolbar_append_widget( tbl, button, "", "");
6151 //Popdown
6152 GtkWidget *sw = gtk_scrolled_window_new (NULL, NULL);
6153 GtkWidget *treeview = gtk_tree_view_new ();
6155 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
6156 GtkTreeViewColumn *column = gtk_tree_view_column_new ();
6157 gtk_tree_view_column_pack_start (column, cell, FALSE);
6158 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
6159 gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
6160 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
6162 gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
6163 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
6164 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
6166 //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
6168 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
6169 gtk_container_add (GTK_CONTAINER (sw), treeview);
6171 gtk_container_add (GTK_CONTAINER (window), sw);
6172 gtk_widget_set_size_request (window, 300, 450);
6174 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
6175 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
6176 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
6178 g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
6180 g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
6181 g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
6182 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
6184 GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
6185 g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6187 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
6188 g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
6189 g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
6190 g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
6191 g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
6193 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
6194 GtkWidget *box = gtk_event_box_new ();
6195 gtk_container_add (GTK_CONTAINER (box), image);
6196 gtk_toolbar_append_widget( tbl, box, "", "");
6197 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
6198 GtkTooltips *tooltips = gtk_tooltips_new ();
6199 gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
6200 gtk_widget_hide (GTK_WIDGET (box));
6201 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
6203 ////////////Size
6204 gchar const *const sizes[] = {
6205 "4", "6", "8", "9", "10", "11", "12", "13", "14",
6206 "16", "18", "20", "22", "24", "28",
6207 "32", "36", "40", "48", "56", "64", "72", "144"
6208 };
6210 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
6211 for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
6212 gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
6213 }
6214 gtk_widget_set_size_request (cbox, 80, -1);
6215 gtk_toolbar_append_widget( tbl, cbox, "", "");
6216 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
6217 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
6218 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
6219 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
6221 ////////////Text anchor
6222 GtkWidget *group = gtk_radio_button_new (NULL);
6223 GtkWidget *row = gtk_hbox_new (FALSE, 4);
6224 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
6226 // left
6227 GtkWidget *rbutton = group;
6228 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6229 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
6230 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6232 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6233 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
6234 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
6235 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
6237 // center
6238 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6239 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6240 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
6241 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6243 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6244 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
6245 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
6246 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
6248 // right
6249 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6250 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6251 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
6252 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6254 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6255 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
6256 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
6257 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
6259 // fill
6260 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6261 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6262 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
6263 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6265 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6266 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
6267 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
6268 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
6270 gtk_toolbar_append_widget( tbl, row, "", "");
6272 //spacer
6273 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6275 ////////////Text style
6276 row = gtk_hbox_new (FALSE, 4);
6278 // bold
6279 rbutton = gtk_toggle_button_new ();
6280 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6281 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
6282 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6283 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
6285 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6286 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
6287 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
6289 // italic
6290 rbutton = gtk_toggle_button_new ();
6291 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6292 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
6293 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6294 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
6296 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6297 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
6298 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
6300 gtk_toolbar_append_widget( tbl, row, "", "");
6302 //spacer
6303 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6305 ////////////Text orientation
6306 group = gtk_radio_button_new (NULL);
6307 row = gtk_hbox_new (FALSE, 4);
6308 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
6310 // horizontal
6311 rbutton = group;
6312 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6313 gtk_container_add (GTK_CONTAINER (rbutton),
6314 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
6315 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6316 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
6318 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6319 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
6320 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
6322 // vertical
6323 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6324 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6325 gtk_container_add (GTK_CONTAINER (rbutton),
6326 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
6327 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6328 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
6330 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6331 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
6332 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
6333 gtk_toolbar_append_widget( tbl, row, "", "" );
6336 //watch selection
6337 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
6339 sigc::connection *c_selection_changed =
6340 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
6341 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
6342 pool->add_connection ("selection-changed", c_selection_changed);
6344 sigc::connection *c_selection_modified =
6345 new sigc::connection (sp_desktop_selection (desktop)->connectModified
6346 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
6347 pool->add_connection ("selection-modified", c_selection_modified);
6349 sigc::connection *c_subselection_changed =
6350 new sigc::connection (desktop->connectToolSubselectionChanged
6351 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
6352 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
6354 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
6357 gtk_widget_show_all( GTK_WIDGET(tbl) );
6359 return GTK_WIDGET(tbl);
6360 } // end of sp_text_toolbox_new()
6362 }//<unnamed> namespace
6365 //#########################
6366 //## Connector ##
6367 //#########################
6369 static void sp_connector_path_set_avoid(void)
6370 {
6371 cc_selection_set_avoid(true);
6372 }
6375 static void sp_connector_path_set_ignore(void)
6376 {
6377 cc_selection_set_avoid(false);
6378 }
6382 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
6383 {
6384 // quit if run by the _changed callbacks
6385 if (g_object_get_data( tbl, "freeze" )) {
6386 return;
6387 }
6389 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
6390 SPDocument *doc = sp_desktop_document(desktop);
6392 if (!sp_document_get_undo_sensitive(doc))
6393 {
6394 return;
6395 }
6397 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6399 if ( repr->attribute("inkscape:connector-spacing") ) {
6400 gdouble priorValue = gtk_adjustment_get_value(adj);
6401 sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
6402 if ( priorValue == gtk_adjustment_get_value(adj) ) {
6403 return;
6404 }
6405 } else if ( adj->value == defaultConnSpacing ) {
6406 return;
6407 }
6409 // in turn, prevent callbacks from responding
6410 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6412 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
6413 SP_OBJECT(desktop->namedview)->updateRepr();
6415 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
6416 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
6417 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
6418 Geom::Matrix m = Geom::identity();
6419 avoid_item_move(&m, item);
6420 }
6422 if (items) {
6423 g_slist_free(items);
6424 }
6426 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
6427 _("Change connector spacing"));
6429 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6431 spinbutton_defocus(GTK_OBJECT(tbl));
6432 }
6434 static void sp_connector_graph_layout(void)
6435 {
6436 if (!SP_ACTIVE_DESKTOP) return;
6437 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6439 // hack for clones, see comment in align-and-distribute.cpp
6440 int saved_compensation = prefs->getInt("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
6441 prefs->setInt("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
6443 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
6445 prefs->setInt("options.clonecompensation", "value", saved_compensation);
6447 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
6448 }
6450 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6451 {
6452 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6453 prefs->setBool("tools.connector", "directedlayout",
6454 gtk_toggle_action_get_active( act ));
6455 }
6457 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6458 {
6459 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6460 prefs->setBool("tools.connector", "avoidoverlaplayout",
6461 gtk_toggle_action_get_active( act ));
6462 }
6465 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
6466 {
6467 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6468 prefs->setDouble("tools.connector", "length", adj->value);
6469 spinbutton_defocus(GTK_OBJECT(tbl));
6470 }
6472 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
6473 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
6474 bool /*is_interactive*/, gpointer data)
6475 {
6476 GtkWidget *tbl = GTK_WIDGET(data);
6478 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6479 return;
6480 }
6481 if (strcmp(name, "inkscape:connector-spacing") != 0) {
6482 return;
6483 }
6485 GtkAdjustment *adj = (GtkAdjustment*)
6486 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
6487 gdouble spacing = defaultConnSpacing;
6488 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
6490 gtk_adjustment_set_value(adj, spacing);
6491 }
6494 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
6495 NULL, /* child_added */
6496 NULL, /* child_removed */
6497 connector_tb_event_attr_changed,
6498 NULL, /* content_changed */
6499 NULL /* order_changed */
6500 };
6503 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
6504 {
6505 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6506 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
6508 {
6509 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
6510 _("Avoid"),
6511 _("Make connectors avoid selected objects"),
6512 "connector_avoid",
6513 secondarySize );
6514 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
6515 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6516 }
6518 {
6519 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
6520 _("Ignore"),
6521 _("Make connectors ignore selected objects"),
6522 "connector_ignore",
6523 secondarySize );
6524 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
6525 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6526 }
6528 EgeAdjustmentAction* eact = 0;
6530 // Spacing spinbox
6531 eact = create_adjustment_action( "ConnectorSpacingAction",
6532 _("Connector Spacing"), _("Spacing:"),
6533 _("The amount of space left around objects by auto-routing connectors"),
6534 "tools.connector", "spacing", defaultConnSpacing,
6535 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
6536 0, 100, 1.0, 10.0,
6537 0, 0, 0,
6538 connector_spacing_changed, 1, 0 );
6539 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6541 // Graph (connector network) layout
6542 {
6543 InkAction* inky = ink_action_new( "ConnectorGraphAction",
6544 _("Graph"),
6545 _("Nicely arrange selected connector network"),
6546 "graph_layout",
6547 secondarySize );
6548 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
6549 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6550 }
6552 // Default connector length spinbox
6553 eact = create_adjustment_action( "ConnectorLengthAction",
6554 _("Connector Length"), _("Length:"),
6555 _("Ideal length for connectors when layout is applied"),
6556 "tools.connector", "length", 100,
6557 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
6558 10, 1000, 10.0, 100.0,
6559 0, 0, 0,
6560 connector_length_changed, 1, 0 );
6561 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6564 // Directed edges toggle button
6565 {
6566 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
6567 _("Downwards"),
6568 _("Make connectors with end-markers (arrows) point downwards"),
6569 "directed_graph",
6570 Inkscape::ICON_SIZE_DECORATION );
6571 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6573 bool tbuttonstate = prefs->getBool("tools.connector", "directedlayout");
6574 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
6576 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
6577 }
6579 // Avoid overlaps toggle button
6580 {
6581 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
6582 _("Remove overlaps"),
6583 _("Do not allow overlapping shapes"),
6584 "remove_overlaps",
6585 Inkscape::ICON_SIZE_DECORATION );
6586 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6588 bool tbuttonstate = prefs->getBool("tools.connector", "avoidoverlaplayout");
6589 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), (tbuttonstate ? TRUE : FALSE ));
6591 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
6592 }
6594 // Code to watch for changes to the connector-spacing attribute in
6595 // the XML.
6596 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6597 g_assert(repr != NULL);
6599 purge_repr_listener( holder, holder );
6601 if (repr) {
6602 g_object_set_data( holder, "repr", repr );
6603 Inkscape::GC::anchor(repr);
6604 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
6605 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
6606 }
6607 } // end of sp_connector_toolbox_prep()
6610 //#########################
6611 //## Paintbucket ##
6612 //#########################
6614 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
6615 {
6616 gint channels = ege_select_one_action_get_active( act );
6617 flood_channels_set_channels( channels );
6618 }
6620 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
6621 {
6622 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6623 prefs->setInt("tools.paintbucket", "threshold", (gint)adj->value);
6624 }
6626 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
6627 {
6628 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6629 prefs->setInt("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
6630 }
6632 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
6633 {
6634 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
6635 SPUnit const *unit = tracker->getActiveUnit();
6636 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6638 prefs->setDouble("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
6639 prefs->setString("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
6640 }
6642 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
6643 {
6644 // FIXME: make defaults settable via Inkscape Options
6645 struct KeyValue {
6646 char const *key;
6647 double value;
6648 } const key_values[] = {
6649 {"threshold", 15},
6650 {"offset", 0.0}
6651 };
6653 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
6654 KeyValue const &kv = key_values[i];
6655 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
6656 if ( adj ) {
6657 gtk_adjustment_set_value(adj, kv.value);
6658 }
6659 }
6661 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
6662 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
6663 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
6664 ege_select_one_action_set_active( autogap_action, 0 );
6665 }
6667 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6668 {
6669 EgeAdjustmentAction* eact = 0;
6670 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6672 {
6673 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6675 GList* items = 0;
6676 gint count = 0;
6677 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
6678 {
6679 GtkTreeIter iter;
6680 gtk_list_store_append( model, &iter );
6681 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6682 count++;
6683 }
6684 g_list_free( items );
6685 items = 0;
6686 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
6687 g_object_set( act1, "short_label", _("Fill by:"), NULL );
6688 ege_select_one_action_set_appearance( act1, "compact" );
6689 ege_select_one_action_set_active( act1, prefs->getInt("tools.paintbucket", "channels", 0) );
6690 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
6691 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
6692 g_object_set_data( holder, "channels_action", act1 );
6693 }
6695 // Spacing spinbox
6696 {
6697 eact = create_adjustment_action(
6698 "ThresholdAction",
6699 _("Fill Threshold"), _("Threshold:"),
6700 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
6701 "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
6702 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
6703 0, 0, 0,
6704 paintbucket_threshold_changed, 1, 0 );
6706 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6707 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6708 }
6710 // Create the units menu.
6711 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
6712 Glib::ustring stored_unit = prefs->getString("tools.paintbucket", "offsetunits");
6713 if (!stored_unit.empty())
6714 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit.data()));
6715 g_object_set_data( holder, "tracker", tracker );
6716 {
6717 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
6718 gtk_action_group_add_action( mainActions, act );
6719 }
6721 // Offset spinbox
6722 {
6723 eact = create_adjustment_action(
6724 "OffsetAction",
6725 _("Grow/shrink by"), _("Grow/shrink by:"),
6726 _("The amount to grow (positive) or shrink (negative) the created fill path"),
6727 "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
6728 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
6729 0, 0, 0,
6730 paintbucket_offset_changed, 1, 2);
6731 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
6733 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6734 }
6736 /* Auto Gap */
6737 {
6738 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6740 GList* items = 0;
6741 gint count = 0;
6742 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
6743 {
6744 GtkTreeIter iter;
6745 gtk_list_store_append( model, &iter );
6746 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6747 count++;
6748 }
6749 g_list_free( items );
6750 items = 0;
6751 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
6752 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
6753 ege_select_one_action_set_appearance( act2, "compact" );
6754 ege_select_one_action_set_active( act2, prefs->getInt("tools.paintbucket", "autogap", 0) );
6755 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
6756 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
6757 g_object_set_data( holder, "autogap_action", act2 );
6758 }
6760 /* Reset */
6761 {
6762 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
6763 _("Defaults"),
6764 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
6765 GTK_STOCK_CLEAR );
6766 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
6767 gtk_action_group_add_action( mainActions, act );
6768 gtk_action_set_sensitive( act, TRUE );
6769 }
6771 }
6773 /*
6774 Local Variables:
6775 mode:c++
6776 c-file-style:"stroustrup"
6777 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
6778 indent-tabs-mode:nil
6779 fill-column:99
6780 End:
6781 */
6782 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :