b945765db14e852399f30234b8c895e6ea6f327e
1 /** @file
2 * @brief Controls bars for some of Inkscape's tools (for some tools,
3 * they are in their own files)
4 */
5 /* Authors:
6 * MenTaLguY <mental@rydia.net>
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 * bulia byak <buliabyak@users.sf.net>
9 * Frank Felfe <innerspace@iname.com>
10 * John Cliff <simarilius@yahoo.com>
11 * David Turner <novalis@gnu.org>
12 * Josh Andler <scislac@scislac.com>
13 * Jon A. Cruz <jon@joncruz.org>
14 * Maximilian Albert <maximilian.albert@gmail.com>
15 *
16 * Copyright (C) 2004 David Turner
17 * Copyright (C) 2003 MenTaLguY
18 * Copyright (C) 1999-2008 authors
19 * Copyright (C) 2001-2002 Ximian, Inc.
20 *
21 * Released under GNU GPL, read the file 'COPYING' for more information
22 */
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
28 #include <cstring>
29 #include <string>
31 #include <gtkmm.h>
32 #include <gtk/gtk.h>
33 #include <iostream>
34 #include <sstream>
36 #include "widgets/button.h"
37 #include "widgets/widget-sizes.h"
38 #include "widgets/spw-utilities.h"
39 #include "widgets/spinbutton-events.h"
40 #include "dialogs/text-edit.h"
41 #include "dialogs/dialog-events.h"
43 #include "ui/widget/style-swatch.h"
45 #include "verbs.h"
46 #include "sp-namedview.h"
47 #include "desktop.h"
48 #include "desktop-handles.h"
49 #include "xml/repr.h"
50 #include "xml/node-event-vector.h"
51 #include "xml/attribute-record.h"
52 #include <glibmm/i18n.h>
53 #include "helper/unit-menu.h"
54 #include "helper/units.h"
55 #include "live_effects/effect.h"
57 #include "inkscape.h"
58 #include "conn-avoid-ref.h"
61 #include "select-toolbar.h"
62 #include "gradient-toolbar.h"
64 #include "connector-context.h"
65 #include "node-context.h"
66 #include "pen-context.h"
67 #include "lpe-tool-context.h"
68 #include "live_effects/lpe-line_segment.h"
69 #include "shape-editor.h"
70 #include "tweak-context.h"
71 #include "sp-rect.h"
72 #include "box3d.h"
73 #include "box3d-context.h"
74 #include "sp-star.h"
75 #include "sp-spiral.h"
76 #include "sp-ellipse.h"
77 #include "sp-text.h"
78 #include "sp-flowtext.h"
79 #include "sp-clippath.h"
80 #include "sp-mask.h"
81 #include "style.h"
82 #include "tools-switch.h"
83 #include "selection.h"
84 #include "selection-chemistry.h"
85 #include "document-private.h"
86 #include "desktop-style.h"
87 #include "../libnrtype/font-lister.h"
88 #include "../libnrtype/font-instance.h"
89 #include "../connection-pool.h"
90 #include "../preferences.h"
91 #include "../inkscape-stock.h"
92 #include "icon.h"
93 #include "graphlayout/graphlayout.h"
94 #include "interface.h"
95 #include "shortcuts.h"
97 #include "mod360.h"
99 #include "toolbox.h"
101 #include "flood-context.h"
103 #include "ink-action.h"
104 #include "ege-adjustment-action.h"
105 #include "ege-output-action.h"
106 #include "ege-select-one-action.h"
107 #include "helper/unit-tracker.h"
108 #include "live_effects/lpe-angle_bisector.h"
110 #include "svg/css-ostringstream.h"
112 #include "widgets/calligraphic-profile-rename.h"
114 using Inkscape::UnitTracker;
116 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
117 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
119 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
120 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
121 static void sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
122 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
123 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
126 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
127 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
128 static void sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
129 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
130 static void sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
131 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
132 static void sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
133 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
134 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
135 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
137 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
140 Inkscape::IconSize prefToSize( Glib::ustring const &path, int base ) {
141 static Inkscape::IconSize sizeChoices[] = {
142 Inkscape::ICON_SIZE_LARGE_TOOLBAR,
143 Inkscape::ICON_SIZE_SMALL_TOOLBAR,
144 Inkscape::ICON_SIZE_MENU
145 };
146 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
147 int index = prefs->getIntLimited( path, base, 0, G_N_ELEMENTS(sizeChoices) );
148 return sizeChoices[index];
149 }
151 static struct {
152 gchar const *type_name;
153 gchar const *data_name;
154 sp_verb_t verb;
155 sp_verb_t doubleclick_verb;
156 } const tools[] = {
157 { "SPSelectContext", "select_tool", SP_VERB_CONTEXT_SELECT, SP_VERB_CONTEXT_SELECT_PREFS},
158 { "SPNodeContext", "node_tool", SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
159 { "SPTweakContext", "tweak_tool", SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
160 { "SPZoomContext", "zoom_tool", SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
161 { "SPRectContext", "rect_tool", SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
162 { "Box3DContext", "3dbox_tool", SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
163 { "SPArcContext", "arc_tool", SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
164 { "SPStarContext", "star_tool", SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
165 { "SPSpiralContext", "spiral_tool", SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
166 { "SPPencilContext", "pencil_tool", SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
167 { "SPPenContext", "pen_tool", SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
168 { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
169 { "SPLPEToolContext", "lpetool_tool", SP_VERB_CONTEXT_LPETOOL, SP_VERB_CONTEXT_LPETOOL_PREFS },
170 { "SPEraserContext", "eraser_tool", SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
171 { "SPFloodContext", "paintbucket_tool", SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
172 { "SPTextContext", "text_tool", SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
173 { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
174 { "SPGradientContext", "gradient_tool", SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
175 { "SPDropperContext", "dropper_tool", SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
176 { NULL, NULL, 0, 0 }
177 };
179 static struct {
180 gchar const *type_name;
181 gchar const *data_name;
182 GtkWidget *(*create_func)(SPDesktop *desktop);
183 void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
184 gchar const *ui_name;
185 gint swatch_verb_id;
186 gchar const *swatch_tool;
187 gchar const *swatch_tip;
188 } const aux_toolboxes[] = {
189 { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep, "SelectToolbar",
190 SP_VERB_INVALID, 0, 0},
191 { "SPNodeContext", "node_toolbox", 0, sp_node_toolbox_prep, "NodeToolbar",
192 SP_VERB_INVALID, 0, 0},
193 { "SPTweakContext", "tweak_toolbox", 0, sp_tweak_toolbox_prep, "TweakToolbar",
194 SP_VERB_CONTEXT_TWEAK_PREFS, "/tools/tweak", N_("Color/opacity used for color tweaking")},
195 { "SPZoomContext", "zoom_toolbox", 0, sp_zoom_toolbox_prep, "ZoomToolbar",
196 SP_VERB_INVALID, 0, 0},
197 { "SPStarContext", "star_toolbox", 0, sp_star_toolbox_prep, "StarToolbar",
198 SP_VERB_CONTEXT_STAR_PREFS, "/tools/shapes/star", N_("Style of new stars")},
199 { "SPRectContext", "rect_toolbox", 0, sp_rect_toolbox_prep, "RectToolbar",
200 SP_VERB_CONTEXT_RECT_PREFS, "/tools/shapes/rect", N_("Style of new rectangles")},
201 { "Box3DContext", "3dbox_toolbox", 0, box3d_toolbox_prep, "3DBoxToolbar",
202 SP_VERB_CONTEXT_3DBOX_PREFS, "/tools/shapes/3dbox", N_("Style of new 3D boxes")},
203 { "SPArcContext", "arc_toolbox", 0, sp_arc_toolbox_prep, "ArcToolbar",
204 SP_VERB_CONTEXT_ARC_PREFS, "/tools/shapes/arc", N_("Style of new ellipses")},
205 { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep, "SpiralToolbar",
206 SP_VERB_CONTEXT_SPIRAL_PREFS, "/tools/shapes/spiral", N_("Style of new spirals")},
207 { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep, "PencilToolbar",
208 SP_VERB_CONTEXT_PENCIL_PREFS, "/tools/freehand/pencil", N_("Style of new paths created by Pencil")},
209 { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep, "PenToolbar",
210 SP_VERB_CONTEXT_PEN_PREFS, "/tools/freehand/pen", N_("Style of new paths created by Pen")},
211 { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
212 SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "/tools/calligraphic", N_("Style of new calligraphic strokes")},
213 { "SPEraserContext", "eraser_toolbox", 0, sp_eraser_toolbox_prep,"EraserToolbar",
214 SP_VERB_CONTEXT_ERASER_PREFS, "/tools/eraser", _("TBD")},
215 { "SPLPEToolContext", "lpetool_toolbox", 0, sp_lpetool_toolbox_prep, "LPEToolToolbar",
216 SP_VERB_CONTEXT_LPETOOL_PREFS, "/tools/lpetool", _("TBD")},
217 { "SPTextContext", "text_toolbox", sp_text_toolbox_new, 0, 0,
218 SP_VERB_INVALID, 0, 0},
219 { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep, "DropperToolbar",
220 SP_VERB_INVALID, 0, 0},
221 { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0, 0,
222 SP_VERB_INVALID, 0, 0},
223 { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep, "ConnectorToolbar",
224 SP_VERB_INVALID, 0, 0},
225 { "SPFloodContext", "paintbucket_toolbox", 0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
226 SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "/tools/paintbucket", N_("Style of Paint Bucket fill objects")},
227 { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
228 };
230 #define TOOLBAR_SLIDER_HINT "full"
232 static gchar const * ui_descr =
233 "<ui>"
234 " <toolbar name='SelectToolbar'>"
235 " <toolitem action='EditSelectAll' />"
236 " <toolitem action='EditSelectAllInAllLayers' />"
237 " <toolitem action='EditDeselect' />"
238 " <separator />"
239 " <toolitem action='ObjectRotate90CCW' />"
240 " <toolitem action='ObjectRotate90' />"
241 " <toolitem action='ObjectFlipHorizontally' />"
242 " <toolitem action='ObjectFlipVertically' />"
243 " <separator />"
244 " <toolitem action='SelectionToBack' />"
245 " <toolitem action='SelectionLower' />"
246 " <toolitem action='SelectionRaise' />"
247 " <toolitem action='SelectionToFront' />"
248 " <separator />"
249 " <toolitem action='XAction' />"
250 " <toolitem action='YAction' />"
251 " <toolitem action='WidthAction' />"
252 " <toolitem action='LockAction' />"
253 " <toolitem action='HeightAction' />"
254 " <toolitem action='UnitsAction' />"
255 " <separator />"
256 " <toolitem action='transform_affect_label' />"
257 " <toolitem action='transform_stroke' />"
258 " <toolitem action='transform_corners' />"
259 " <toolitem action='transform_gradient' />"
260 " <toolitem action='transform_pattern' />"
261 " </toolbar>"
263 " <toolbar name='NodeToolbar'>"
264 " <toolitem action='NodeInsertAction' />"
265 " <toolitem action='NodeDeleteAction' />"
266 " <separator />"
267 " <toolitem action='NodeJoinAction' />"
268 " <toolitem action='NodeBreakAction' />"
269 " <separator />"
270 " <toolitem action='NodeJoinSegmentAction' />"
271 " <toolitem action='NodeDeleteSegmentAction' />"
272 " <separator />"
273 " <toolitem action='NodeCuspAction' />"
274 " <toolitem action='NodeSmoothAction' />"
275 " <toolitem action='NodeSymmetricAction' />"
276 " <toolitem action='NodeAutoAction' />"
277 " <separator />"
278 " <toolitem action='NodeLineAction' />"
279 " <toolitem action='NodeCurveAction' />"
280 " <separator />"
281 " <toolitem action='ObjectToPath' />"
282 " <toolitem action='StrokeToPath' />"
283 " <separator />"
284 " <toolitem action='NodeXAction' />"
285 " <toolitem action='NodeYAction' />"
286 " <toolitem action='NodeUnitsAction' />"
287 " <separator />"
288 " <toolitem action='ObjectEditClipPathAction' />"
289 " <toolitem action='ObjectEditMaskPathAction' />"
290 " <toolitem action='EditNextLPEParameterAction' />"
291 " <separator />"
292 " <toolitem action='NodesShowHandlesAction' />"
293 " <toolitem action='NodesShowHelperpath' />"
294 " </toolbar>"
296 " <toolbar name='TweakToolbar'>"
297 " <toolitem action='TweakWidthAction' />"
298 " <separator />"
299 " <toolitem action='TweakForceAction' />"
300 " <toolitem action='TweakPressureAction' />"
301 " <separator />"
302 " <toolitem action='TweakModeAction' />"
303 " <separator />"
304 " <toolitem action='TweakFidelityAction' />"
305 " <separator />"
306 " <toolitem action='TweakChannelsLabel' />"
307 " <toolitem action='TweakDoH' />"
308 " <toolitem action='TweakDoS' />"
309 " <toolitem action='TweakDoL' />"
310 " <toolitem action='TweakDoO' />"
311 " </toolbar>"
313 " <toolbar name='ZoomToolbar'>"
314 " <toolitem action='ZoomIn' />"
315 " <toolitem action='ZoomOut' />"
316 " <separator />"
317 " <toolitem action='Zoom1:0' />"
318 " <toolitem action='Zoom1:2' />"
319 " <toolitem action='Zoom2:1' />"
320 " <separator />"
321 " <toolitem action='ZoomSelection' />"
322 " <toolitem action='ZoomDrawing' />"
323 " <toolitem action='ZoomPage' />"
324 " <toolitem action='ZoomPageWidth' />"
325 " <separator />"
326 " <toolitem action='ZoomPrev' />"
327 " <toolitem action='ZoomNext' />"
328 " </toolbar>"
330 " <toolbar name='StarToolbar'>"
331 " <separator />"
332 " <toolitem action='StarStateAction' />"
333 " <separator />"
334 " <toolitem action='FlatAction' />"
335 " <separator />"
336 " <toolitem action='MagnitudeAction' />"
337 " <toolitem action='SpokeAction' />"
338 " <toolitem action='RoundednessAction' />"
339 " <toolitem action='RandomizationAction' />"
340 " <separator />"
341 " <toolitem action='StarResetAction' />"
342 " </toolbar>"
344 " <toolbar name='RectToolbar'>"
345 " <toolitem action='RectStateAction' />"
346 " <toolitem action='RectWidthAction' />"
347 " <toolitem action='RectHeightAction' />"
348 " <toolitem action='RadiusXAction' />"
349 " <toolitem action='RadiusYAction' />"
350 " <toolitem action='RectUnitsAction' />"
351 " <separator />"
352 " <toolitem action='RectResetAction' />"
353 " </toolbar>"
355 " <toolbar name='3DBoxToolbar'>"
356 " <toolitem action='3DBoxAngleXAction' />"
357 " <toolitem action='3DBoxVPXStateAction' />"
358 " <separator />"
359 " <toolitem action='3DBoxAngleYAction' />"
360 " <toolitem action='3DBoxVPYStateAction' />"
361 " <separator />"
362 " <toolitem action='3DBoxAngleZAction' />"
363 " <toolitem action='3DBoxVPZStateAction' />"
364 " </toolbar>"
366 " <toolbar name='SpiralToolbar'>"
367 " <toolitem action='SpiralStateAction' />"
368 " <toolitem action='SpiralRevolutionAction' />"
369 " <toolitem action='SpiralExpansionAction' />"
370 " <toolitem action='SpiralT0Action' />"
371 " <separator />"
372 " <toolitem action='SpiralResetAction' />"
373 " </toolbar>"
375 " <toolbar name='PenToolbar'>"
376 " <toolitem action='FreehandModeActionPen' />"
377 " <separator />"
378 " <toolitem action='SetPenShapeAction'/>"
379 " </toolbar>"
381 " <toolbar name='PencilToolbar'>"
382 " <toolitem action='FreehandModeActionPencil' />"
383 " <separator />"
384 " <toolitem action='PencilToleranceAction' />"
385 " <separator />"
386 " <toolitem action='PencilResetAction' />"
387 " <separator />"
388 " <toolitem action='SetPencilShapeAction'/>"
389 " </toolbar>"
391 " <toolbar name='CalligraphyToolbar'>"
392 " <separator />"
393 " <toolitem action='SetProfileAction'/>"
394 " <separator />"
395 " <toolitem action='CalligraphyWidthAction' />"
396 " <toolitem action='PressureAction' />"
397 " <toolitem action='TraceAction' />"
398 " <toolitem action='ThinningAction' />"
399 " <separator />"
400 " <toolitem action='AngleAction' />"
401 " <toolitem action='TiltAction' />"
402 " <toolitem action='FixationAction' />"
403 " <separator />"
404 " <toolitem action='CapRoundingAction' />"
405 " <separator />"
406 " <toolitem action='TremorAction' />"
407 " <toolitem action='WiggleAction' />"
408 " <toolitem action='MassAction' />"
409 " <separator />"
410 " </toolbar>"
412 " <toolbar name='ArcToolbar'>"
413 " <toolitem action='ArcStateAction' />"
414 " <separator />"
415 " <toolitem action='ArcStartAction' />"
416 " <toolitem action='ArcEndAction' />"
417 " <separator />"
418 " <toolitem action='ArcOpenAction' />"
419 " <separator />"
420 " <toolitem action='ArcResetAction' />"
421 " <separator />"
422 " </toolbar>"
424 " <toolbar name='PaintbucketToolbar'>"
425 " <toolitem action='ChannelsAction' />"
426 " <separator />"
427 " <toolitem action='ThresholdAction' />"
428 " <separator />"
429 " <toolitem action='OffsetAction' />"
430 " <toolitem action='PaintbucketUnitsAction' />"
431 " <separator />"
432 " <toolitem action='AutoGapAction' />"
433 " <separator />"
434 " <toolitem action='PaintbucketResetAction' />"
435 " </toolbar>"
437 " <toolbar name='EraserToolbar'>"
438 " <toolitem action='EraserWidthAction' />"
439 " <separator />"
440 " <toolitem action='EraserModeAction' />"
441 " </toolbar>"
443 " <toolbar name='LPEToolToolbar'>"
444 " <toolitem action='LPEToolModeAction' />"
445 " <separator />"
446 " <toolitem action='LPEShowBBoxAction' />"
447 " <toolitem action='LPEBBoxFromSelectionAction' />"
448 " <separator />"
449 " <toolitem action='LPELineSegmentAction' />"
450 " <separator />"
451 " <toolitem action='LPEMeasuringAction' />"
452 " <toolitem action='LPEToolUnitsAction' />"
453 " <separator />"
454 " <toolitem action='LPEOpenLPEDialogAction' />"
455 " </toolbar>"
457 " <toolbar name='DropperToolbar'>"
458 " <toolitem action='DropperOpacityAction' />"
459 " <toolitem action='DropperPickAlphaAction' />"
460 " <toolitem action='DropperSetAlphaAction' />"
461 " </toolbar>"
463 " <toolbar name='ConnectorToolbar'>"
464 " <toolitem action='ConnectorAvoidAction' />"
465 " <toolitem action='ConnectorIgnoreAction' />"
466 " <toolitem action='ConnectorSpacingAction' />"
467 " <toolitem action='ConnectorGraphAction' />"
468 " <toolitem action='ConnectorLengthAction' />"
469 " <toolitem action='ConnectorDirectedAction' />"
470 " <toolitem action='ConnectorOverlapAction' />"
471 " </toolbar>"
473 "</ui>"
474 ;
476 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
478 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
480 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
481 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
483 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
484 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
486 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
487 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
490 GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick( GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
491 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
492 Inkscape::UI::View::View *view, GtkTooltips *tt);
494 class VerbAction : public Gtk::Action {
495 public:
496 static Glib::RefPtr<VerbAction> create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
498 virtual ~VerbAction();
499 virtual void set_active(bool active = true);
501 protected:
502 virtual Gtk::Widget* create_menu_item_vfunc();
503 virtual Gtk::Widget* create_tool_item_vfunc();
505 virtual void connect_proxy_vfunc(Gtk::Widget* proxy);
506 virtual void disconnect_proxy_vfunc(Gtk::Widget* proxy);
508 virtual void on_activate();
510 private:
511 Inkscape::Verb* verb;
512 Inkscape::Verb* verb2;
513 Inkscape::UI::View::View *view;
514 GtkTooltips *tooltips;
515 bool active;
517 VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
518 };
521 Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips)
522 {
523 Glib::RefPtr<VerbAction> result;
524 SPAction *action = verb->get_action(view);
525 if ( action ) {
526 //SPAction* action2 = verb2 ? verb2->get_action(view) : 0;
527 result = Glib::RefPtr<VerbAction>(new VerbAction(verb, verb2, view, tooltips));
528 }
530 return result;
531 }
533 VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips) :
534 Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(verb->get_image()), Glib::ustring(_(verb->get_name())), Glib::ustring(_(verb->get_tip()))),
535 verb(verb),
536 verb2(verb2),
537 view(view),
538 tooltips(tooltips),
539 active(false)
540 {
541 }
543 VerbAction::~VerbAction()
544 {
545 }
547 Gtk::Widget* VerbAction::create_menu_item_vfunc()
548 {
549 // First call in to get the icon rendered if present in SVG
550 Gtk::Widget *widget = sp_icon_get_icon( property_stock_id().get_value().get_string(), Inkscape::ICON_SIZE_MENU );
551 delete widget;
552 widget = 0;
554 Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
555 // g_message("create_menu_item_vfunc() = %p for '%s'", widg, verb->get_id());
556 return widg;
557 }
559 Gtk::Widget* VerbAction::create_tool_item_vfunc()
560 {
561 // Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
562 Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
563 GtkWidget* toolbox = 0;
564 GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
565 SP_BUTTON_TYPE_TOGGLE,
566 verb,
567 verb2,
568 view,
569 tooltips );
570 if ( active ) {
571 sp_button_toggle_set_down( SP_BUTTON(button), active);
572 }
573 gtk_widget_show_all( button );
574 Gtk::Widget* wrapped = Glib::wrap(button);
575 Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
576 holder->add(*wrapped);
578 // g_message("create_tool_item_vfunc() = %p for '%s'", holder, verb->get_id());
579 return holder;
580 }
582 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
583 {
584 // g_message("connect_proxy_vfunc(%p) for '%s'", proxy, verb->get_id());
585 Gtk::Action::connect_proxy_vfunc(proxy);
586 }
588 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
589 {
590 // g_message("disconnect_proxy_vfunc(%p) for '%s'", proxy, verb->get_id());
591 Gtk::Action::disconnect_proxy_vfunc(proxy);
592 }
594 void VerbAction::set_active(bool active)
595 {
596 this->active = active;
597 Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
598 for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
599 Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
600 if (ti) {
601 // *should* have one child that is the SPButton
602 Gtk::Widget* child = ti->get_child();
603 if ( child && SP_IS_BUTTON(child->gobj()) ) {
604 SPButton* button = SP_BUTTON(child->gobj());
605 sp_button_toggle_set_down( button, active );
606 }
607 }
608 }
609 }
611 void VerbAction::on_activate()
612 {
613 if ( verb ) {
614 SPAction *action = verb->get_action(view);
615 if ( action ) {
616 sp_action_perform(action, 0);
617 }
618 }
619 }
621 /* Global text entry widgets necessary for update */
622 /* GtkWidget *dropper_rgb_entry,
623 *dropper_opacity_entry ; */
624 // should be made a private member once this is converted to class
626 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
627 connection->disconnect();
628 delete connection;
629 }
631 static void purge_repr_listener( GObject* obj, GObject* tbl )
632 {
633 (void)obj;
634 Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
635 if (oldrepr) { // remove old listener
636 sp_repr_remove_listener_by_data(oldrepr, tbl);
637 Inkscape::GC::release(oldrepr);
638 oldrepr = 0;
639 g_object_set_data( tbl, "repr", NULL );
640 }
641 }
643 GtkWidget *
644 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
645 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
646 Inkscape::UI::View::View *view, GtkTooltips *tt)
647 {
648 SPAction *action = verb->get_action(view);
649 if (!action) return NULL;
651 SPAction *doubleclick_action;
652 if (doubleclick_verb)
653 doubleclick_action = doubleclick_verb->get_action(view);
654 else
655 doubleclick_action = NULL;
657 /* fixme: Handle sensitive/unsensitive */
658 /* fixme: Implement sp_button_new_from_action */
659 GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
660 gtk_widget_show(b);
663 unsigned int shortcut = sp_shortcut_get_primary(verb);
664 if (shortcut) {
665 gchar key[256];
666 sp_ui_shortcut_string(shortcut, key);
667 gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
668 if ( t ) {
669 gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
670 }
671 g_free(tip);
672 } else {
673 if ( t ) {
674 gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
675 }
676 }
678 return b;
679 }
682 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
683 {
684 SPAction* targetAction = SP_ACTION(user_data);
685 if ( targetAction ) {
686 sp_action_perform( targetAction, NULL );
687 }
688 }
690 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
691 {
692 if ( data ) {
693 GtkAction* act = GTK_ACTION(data);
694 gtk_action_set_sensitive( act, sensitive );
695 }
696 }
698 static SPActionEventVector action_event_vector = {
699 {NULL},
700 NULL,
701 NULL,
702 sp_action_action_set_sensitive,
703 NULL,
704 NULL
705 };
707 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
708 {
709 GtkAction* act = 0;
711 SPAction* targetAction = verb->get_action(view);
712 InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size );
713 act = GTK_ACTION(inky);
714 gtk_action_set_sensitive( act, targetAction->sensitive );
716 g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
718 SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
719 nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
721 return act;
722 }
724 Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
725 {
726 Inkscape::UI::View::View *view = desktop;
727 gint verbsToUse[] = {
728 // disabled until we have icons for them:
729 //find
730 //SP_VERB_EDIT_TILE,
731 //SP_VERB_EDIT_UNTILE,
732 SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
733 SP_VERB_DIALOG_DISPLAY,
734 SP_VERB_DIALOG_FILL_STROKE,
735 SP_VERB_DIALOG_NAMEDVIEW,
736 SP_VERB_DIALOG_TEXT,
737 SP_VERB_DIALOG_XML_EDITOR,
738 SP_VERB_EDIT_CLONE,
739 SP_VERB_EDIT_COPY,
740 SP_VERB_EDIT_CUT,
741 SP_VERB_EDIT_DUPLICATE,
742 SP_VERB_EDIT_PASTE,
743 SP_VERB_EDIT_REDO,
744 SP_VERB_EDIT_UNDO,
745 SP_VERB_EDIT_UNLINK_CLONE,
746 SP_VERB_FILE_EXPORT,
747 SP_VERB_FILE_IMPORT,
748 SP_VERB_FILE_NEW,
749 SP_VERB_FILE_OPEN,
750 SP_VERB_FILE_PRINT,
751 SP_VERB_FILE_SAVE,
752 SP_VERB_OBJECT_TO_CURVE,
753 SP_VERB_SELECTION_GROUP,
754 SP_VERB_SELECTION_OUTLINE,
755 SP_VERB_SELECTION_UNGROUP,
756 SP_VERB_ZOOM_1_1,
757 SP_VERB_ZOOM_1_2,
758 SP_VERB_ZOOM_2_1,
759 SP_VERB_ZOOM_DRAWING,
760 SP_VERB_ZOOM_IN,
761 SP_VERB_ZOOM_NEXT,
762 SP_VERB_ZOOM_OUT,
763 SP_VERB_ZOOM_PAGE,
764 SP_VERB_ZOOM_PAGE_WIDTH,
765 SP_VERB_ZOOM_PREV,
766 SP_VERB_ZOOM_SELECTION,
767 };
769 Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
771 static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
772 Glib::RefPtr<Gtk::ActionGroup> mainActions;
773 if ( groups.find(desktop) != groups.end() ) {
774 mainActions = groups[desktop];
775 }
777 if ( !mainActions ) {
778 mainActions = Gtk::ActionGroup::create("main");
779 groups[desktop] = mainActions;
780 }
782 for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
783 Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
784 if ( verb ) {
785 if (!mainActions->get_action(verb->get_id())) {
786 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
787 mainActions->add(Glib::wrap(act));
788 }
789 }
790 }
792 if ( !mainActions->get_action("ToolZoom") ) {
793 GtkTooltips *tt = gtk_tooltips_new();
794 for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
795 Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
796 if ( va ) {
797 mainActions->add(va);
798 if ( i == 0 ) {
799 va->set_active(true);
800 }
801 }
802 }
803 }
806 return mainActions;
807 }
810 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
811 {
812 gtk_widget_set_size_request( widget,
813 widget->allocation.width,
814 widget->allocation.height );
815 }
817 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
818 {
819 gtk_widget_set_size_request( widget, -1, -1 );
820 }
824 GtkWidget *
825 sp_tool_toolbox_new()
826 {
827 GtkTooltips *tt = gtk_tooltips_new();
828 GtkWidget* tb = gtk_toolbar_new();
829 gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
830 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
832 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
833 g_object_set_data(G_OBJECT(tb), "tooltips", tt);
835 gtk_widget_set_sensitive(tb, FALSE);
837 GtkWidget *hb = gtk_handle_box_new();
838 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
839 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
840 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
842 gtk_container_add(GTK_CONTAINER(hb), tb);
843 gtk_widget_show(GTK_WIDGET(tb));
845 sigc::connection* conn = new sigc::connection;
846 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
848 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
849 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
851 return hb;
852 }
854 GtkWidget *
855 sp_aux_toolbox_new()
856 {
857 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
859 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
861 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
863 gtk_widget_set_sensitive(tb, FALSE);
865 GtkWidget *hb = gtk_handle_box_new();
866 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
867 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
868 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
870 gtk_container_add(GTK_CONTAINER(hb), tb);
871 gtk_widget_show(GTK_WIDGET(tb));
873 sigc::connection* conn = new sigc::connection;
874 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
876 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
877 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
879 return hb;
880 }
882 //####################################
883 //# Commands Bar
884 //####################################
886 GtkWidget *
887 sp_commands_toolbox_new()
888 {
889 GtkWidget *tb = gtk_toolbar_new();
891 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
892 gtk_widget_set_sensitive(tb, FALSE);
894 GtkWidget *hb = gtk_handle_box_new();
895 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
896 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
897 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
899 gtk_container_add(GTK_CONTAINER(hb), tb);
900 gtk_widget_show(GTK_WIDGET(tb));
902 sigc::connection* conn = new sigc::connection;
903 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
905 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
906 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
908 return hb;
909 }
912 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
913 gchar const *label, gchar const *shortLabel, gchar const *tooltip,
914 Glib::ustring const &path, gdouble def,
915 GtkWidget *focusTarget,
916 GtkWidget *us,
917 GObject *dataKludge,
918 gboolean altx, gchar const *altx_mark,
919 gdouble lower, gdouble upper, gdouble step, gdouble page,
920 gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
921 void (*callback)(GtkAdjustment *, GObject *),
922 gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
923 {
924 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
925 GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs->getDouble(path, def) * factor,
926 lower, upper, step, page, page ) );
927 if (us) {
928 sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
929 }
931 gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
933 EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
934 if ( shortLabel ) {
935 g_object_set( act, "short_label", shortLabel, NULL );
936 }
938 if ( (descrCount > 0) && descrLabels && descrValues ) {
939 ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
940 }
942 if ( focusTarget ) {
943 ege_adjustment_action_set_focuswidget( act, focusTarget );
944 }
946 if ( altx && altx_mark ) {
947 g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
948 }
950 if ( dataKludge ) {
951 // Rather lame, but it's the only place where we need to get the entry name
952 // but we don't have an Entry
953 g_object_set_data( dataKludge, prefs->getEntry(path).getEntryName().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 void
1064 sp_node_path_edit_auto(void)
1065 {
1066 ShapeEditor *shape_editor = get_current_shape_editor();
1067 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_AUTO);
1068 }
1070 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1071 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1072 bool show = gtk_toggle_action_get_active( act );
1073 prefs->setBool("/tools/nodes/show_handles", show);
1074 ShapeEditor *shape_editor = get_current_shape_editor();
1075 if (shape_editor) shape_editor->show_handles(show);
1076 }
1078 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1079 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1080 bool show = gtk_toggle_action_get_active( act );
1081 prefs->setBool("/tools/nodes/show_helperpath", show);
1082 ShapeEditor *shape_editor = get_current_shape_editor();
1083 if (shape_editor) shape_editor->show_helperpath(show);
1084 }
1086 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1087 sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1088 }
1090 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1091 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1092 }
1094 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1095 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1096 }
1098 /* is called when the node selection is modified */
1099 static void
1100 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1101 {
1102 GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1103 GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1104 GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1105 GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1107 // quit if run by the attr_changed listener
1108 if (g_object_get_data( tbl, "freeze" )) {
1109 return;
1110 }
1112 // in turn, prevent listener from responding
1113 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1115 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1116 SPUnit const *unit = tracker->getActiveUnit();
1118 ShapeEditor *shape_editor = get_current_shape_editor();
1119 if (shape_editor && shape_editor->has_nodepath()) {
1120 Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1121 int n_selected = 0;
1122 if (nodepath) {
1123 n_selected = nodepath->numSelected();
1124 }
1126 if (n_selected == 0) {
1127 gtk_action_set_sensitive(xact, FALSE);
1128 gtk_action_set_sensitive(yact, FALSE);
1129 } else {
1130 gtk_action_set_sensitive(xact, TRUE);
1131 gtk_action_set_sensitive(yact, TRUE);
1132 Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1133 Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1135 if (n_selected == 1) {
1136 Geom::Point sel_node = nodepath->singleSelectedCoords();
1137 if (oldx != sel_node[Geom::X] || oldy != sel_node[Geom::Y]) {
1138 gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[Geom::X], *unit));
1139 gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[Geom::Y], *unit));
1140 }
1141 } else {
1142 boost::optional<Geom::Coord> x = sp_node_selected_common_coord(nodepath, Geom::X);
1143 boost::optional<Geom::Coord> y = sp_node_selected_common_coord(nodepath, Geom::Y);
1144 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1145 /* Note: Currently x and y will always have a value, even if the coordinates of the
1146 selected nodes don't coincide (in this case we use the coordinates of the center
1147 of the bounding box). So the entries are never set to zero. */
1148 // FIXME: Maybe we should clear the entry if several nodes are selected
1149 // instead of providing a kind of average value
1150 gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1151 gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1152 }
1153 }
1154 }
1155 } else {
1156 // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1157 gtk_action_set_sensitive(xact, FALSE);
1158 gtk_action_set_sensitive(yact, FALSE);
1159 }
1161 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1162 }
1164 static void
1165 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1166 {
1167 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1168 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1170 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1171 SPUnit const *unit = tracker->getActiveUnit();
1173 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1174 prefs->setDouble(Glib::ustring("/tools/nodes/") + value_name, sp_units_get_pixels(adj->value, *unit));
1175 }
1177 // quit if run by the attr_changed listener
1178 if (g_object_get_data( tbl, "freeze" )) {
1179 return;
1180 }
1182 // in turn, prevent listener from responding
1183 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1185 ShapeEditor *shape_editor = get_current_shape_editor();
1186 if (shape_editor && shape_editor->has_nodepath()) {
1187 double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1188 if (!strcmp(value_name, "x")) {
1189 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::X);
1190 }
1191 if (!strcmp(value_name, "y")) {
1192 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::Y);
1193 }
1194 }
1196 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1197 }
1199 static void
1200 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1201 {
1202 sp_node_path_value_changed(adj, tbl, "x");
1203 }
1205 static void
1206 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1207 {
1208 sp_node_path_value_changed(adj, tbl, "y");
1209 }
1211 void
1212 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1213 {
1214 {
1215 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1216 SPItem *item = selection->singleItem();
1217 if (item && SP_IS_LPE_ITEM(item)) {
1218 if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1219 gtk_action_set_sensitive(w, TRUE);
1220 } else {
1221 gtk_action_set_sensitive(w, FALSE);
1222 }
1223 } else {
1224 gtk_action_set_sensitive(w, FALSE);
1225 }
1226 }
1228 {
1229 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1230 SPItem *item = selection->singleItem();
1231 if (item && item->clip_ref && item->clip_ref->getObject()) {
1232 gtk_action_set_sensitive(w, TRUE);
1233 } else {
1234 gtk_action_set_sensitive(w, FALSE);
1235 }
1236 }
1238 {
1239 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1240 SPItem *item = selection->singleItem();
1241 if (item && item->mask_ref && item->mask_ref->getObject()) {
1242 gtk_action_set_sensitive(w, TRUE);
1243 } else {
1244 gtk_action_set_sensitive(w, FALSE);
1245 }
1246 }
1247 }
1249 void
1250 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1251 {
1252 sp_node_toolbox_sel_changed (selection, tbl);
1253 }
1257 //################################
1258 //## Node Editing Toolbox ##
1259 //################################
1261 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1262 {
1263 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1264 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1265 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1266 g_object_set_data( holder, "tracker", tracker );
1268 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
1270 {
1271 InkAction* inky = ink_action_new( "NodeInsertAction",
1272 _("Insert node"),
1273 _("Insert new nodes into selected segments"),
1274 "node_insert",
1275 secondarySize );
1276 g_object_set( inky, "short_label", _("Insert"), NULL );
1277 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1278 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1279 }
1281 {
1282 InkAction* inky = ink_action_new( "NodeDeleteAction",
1283 _("Delete node"),
1284 _("Delete selected nodes"),
1285 "node_delete",
1286 secondarySize );
1287 g_object_set( inky, "short_label", _("Delete"), NULL );
1288 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1289 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1290 }
1292 {
1293 InkAction* inky = ink_action_new( "NodeJoinAction",
1294 _("Join endnodes"),
1295 _("Join selected endnodes"),
1296 "node_join",
1297 secondarySize );
1298 g_object_set( inky, "short_label", _("Join"), NULL );
1299 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1300 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1301 }
1303 {
1304 InkAction* inky = ink_action_new( "NodeBreakAction",
1305 _("Break nodes"),
1306 _("Break path at selected nodes"),
1307 "node_break",
1308 secondarySize );
1309 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1310 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1311 }
1314 {
1315 InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1316 _("Join with segment"),
1317 _("Join selected endnodes with a new segment"),
1318 "node_join_segment",
1319 secondarySize );
1320 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1321 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1322 }
1324 {
1325 InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1326 _("Delete segment"),
1327 _("Delete segment between two non-endpoint nodes"),
1328 "node_delete_segment",
1329 secondarySize );
1330 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1331 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1332 }
1334 {
1335 InkAction* inky = ink_action_new( "NodeCuspAction",
1336 _("Node Cusp"),
1337 _("Make selected nodes corner"),
1338 "node_cusp",
1339 secondarySize );
1340 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1341 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1342 }
1344 {
1345 InkAction* inky = ink_action_new( "NodeSmoothAction",
1346 _("Node Smooth"),
1347 _("Make selected nodes smooth"),
1348 "node_smooth",
1349 secondarySize );
1350 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1351 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1352 }
1354 {
1355 InkAction* inky = ink_action_new( "NodeSymmetricAction",
1356 _("Node Symmetric"),
1357 _("Make selected nodes symmetric"),
1358 "node_symmetric",
1359 secondarySize );
1360 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1361 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1362 }
1364 {
1365 InkAction* inky = ink_action_new( "NodeAutoAction",
1366 _("Node Auto"),
1367 _("Make selected nodes auto-smooth"),
1368 "node_auto",
1369 secondarySize );
1370 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_auto), 0 );
1371 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1372 }
1374 {
1375 InkAction* inky = ink_action_new( "NodeLineAction",
1376 _("Node Line"),
1377 _("Make selected segments lines"),
1378 "node_line",
1379 secondarySize );
1380 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1381 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1382 }
1384 {
1385 InkAction* inky = ink_action_new( "NodeCurveAction",
1386 _("Node Curve"),
1387 _("Make selected segments curves"),
1388 "node_curve",
1389 secondarySize );
1390 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1391 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1392 }
1394 {
1395 InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1396 _("Show Handles"),
1397 _("Show the Bezier handles of selected nodes"),
1398 "nodes_show_handles",
1399 Inkscape::ICON_SIZE_DECORATION );
1400 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1401 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1402 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_handles", true) );
1403 }
1405 {
1406 InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1407 _("Show Outline"),
1408 _("Show the outline of the path"),
1409 "nodes_show_helperpath",
1410 Inkscape::ICON_SIZE_DECORATION );
1411 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1412 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1413 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_helperpath", false) );
1414 }
1416 {
1417 InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1418 _("Next path effect parameter"),
1419 _("Show next path effect parameter for editing"),
1420 "edit_next_parameter",
1421 Inkscape::ICON_SIZE_DECORATION );
1422 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1423 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1424 g_object_set_data( holder, "nodes_lpeedit", inky);
1425 }
1427 {
1428 InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1429 _("Edit clipping path"),
1430 _("Edit the clipping path of the object"),
1431 "nodeedit-clippath",
1432 Inkscape::ICON_SIZE_DECORATION );
1433 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1434 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1435 g_object_set_data( holder, "nodes_clippathedit", inky);
1436 }
1438 {
1439 InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1440 _("Edit mask path"),
1441 _("Edit the mask of the object"),
1442 "nodeedit-mask",
1443 Inkscape::ICON_SIZE_DECORATION );
1444 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1445 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1446 g_object_set_data( holder, "nodes_maskedit", inky);
1447 }
1449 /* X coord of selected node(s) */
1450 {
1451 EgeAdjustmentAction* eact = 0;
1452 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1453 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1454 eact = create_adjustment_action( "NodeXAction",
1455 _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1456 "/tools/nodes/Xcoord", 0,
1457 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1458 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1459 labels, values, G_N_ELEMENTS(labels),
1460 sp_node_path_x_value_changed );
1461 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1462 g_object_set_data( holder, "nodes_x_action", eact );
1463 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1464 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1465 }
1467 /* Y coord of selected node(s) */
1468 {
1469 EgeAdjustmentAction* eact = 0;
1470 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1471 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1472 eact = create_adjustment_action( "NodeYAction",
1473 _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1474 "/tools/nodes/Ycoord", 0,
1475 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1476 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1477 labels, values, G_N_ELEMENTS(labels),
1478 sp_node_path_y_value_changed );
1479 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1480 g_object_set_data( holder, "nodes_y_action", eact );
1481 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1482 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1483 }
1485 // add the units menu
1486 {
1487 GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1488 gtk_action_group_add_action( mainActions, act );
1489 }
1492 sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1494 //watch selection
1495 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1497 sigc::connection *c_selection_changed =
1498 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1499 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1500 pool->add_connection ("selection-changed", c_selection_changed);
1502 sigc::connection *c_selection_modified =
1503 new sigc::connection (sp_desktop_selection (desktop)->connectModified
1504 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1505 pool->add_connection ("selection-modified", c_selection_modified);
1507 sigc::connection *c_subselection_changed =
1508 new sigc::connection (desktop->connectToolSubselectionChanged
1509 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1510 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1512 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1514 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1515 } // end of sp_node_toolbox_prep()
1518 //########################
1519 //## Zoom Toolbox ##
1520 //########################
1522 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1523 {
1524 // no custom GtkAction setup needed
1525 } // end of sp_zoom_toolbox_prep()
1527 void
1528 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1529 {
1530 toolbox_set_desktop(toolbox,
1531 desktop,
1532 setup_tool_toolbox,
1533 update_tool_toolbox,
1534 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1535 "event_context_connection")));
1536 }
1539 void
1540 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1541 {
1542 toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1543 desktop,
1544 setup_aux_toolbox,
1545 update_aux_toolbox,
1546 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1547 "event_context_connection")));
1548 }
1550 void
1551 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1552 {
1553 toolbox_set_desktop(toolbox,
1554 desktop,
1555 setup_commands_toolbox,
1556 update_commands_toolbox,
1557 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1558 "event_context_connection")));
1559 }
1561 static void
1562 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1563 {
1564 gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1565 SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1567 if (old_desktop) {
1568 GList *children, *iter;
1570 children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1571 for ( iter = children ; iter ; iter = iter->next ) {
1572 gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1573 }
1574 g_list_free(children);
1575 }
1577 g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1579 if (desktop) {
1580 gtk_widget_set_sensitive(toolbox, TRUE);
1581 setup_func(toolbox, desktop);
1582 update_func(desktop, desktop->event_context, toolbox);
1583 *conn = desktop->connectEventContextChanged
1584 (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1585 } else {
1586 gtk_widget_set_sensitive(toolbox, FALSE);
1587 }
1589 } // end of toolbox_set_desktop()
1592 static void
1593 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1594 {
1595 gchar const * descr =
1596 "<ui>"
1597 " <toolbar name='ToolToolbar'>"
1598 " <toolitem action='ToolSelector' />"
1599 " <toolitem action='ToolNode' />"
1600 " <toolitem action='ToolTweak' />"
1601 " <toolitem action='ToolZoom' />"
1602 " <toolitem action='ToolRect' />"
1603 " <toolitem action='Tool3DBox' />"
1604 " <toolitem action='ToolArc' />"
1605 " <toolitem action='ToolStar' />"
1606 " <toolitem action='ToolSpiral' />"
1607 " <toolitem action='ToolPencil' />"
1608 " <toolitem action='ToolPen' />"
1609 " <toolitem action='ToolCalligraphic' />"
1610 " <toolitem action='ToolEraser' />"
1611 // " <toolitem action='ToolLPETool' />"
1612 " <toolitem action='ToolPaintBucket' />"
1613 " <toolitem action='ToolText' />"
1614 " <toolitem action='ToolConnector' />"
1615 " <toolitem action='ToolGradient' />"
1616 " <toolitem action='ToolDropper' />"
1617 " </toolbar>"
1618 "</ui>";
1619 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1620 GtkUIManager* mgr = gtk_ui_manager_new();
1621 GError* errVal = 0;
1622 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1624 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1625 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1627 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" );
1628 if ( prefs->getBool("/toolbox/icononly", true) ) {
1629 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1630 }
1631 Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
1632 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1634 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
1635 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1637 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1639 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1640 if ( child ) {
1641 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1642 }
1644 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1645 // Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
1646 }
1649 static void
1650 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1651 {
1652 gchar const *const tname = ( eventcontext
1653 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1654 : NULL );
1655 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1657 for (int i = 0 ; tools[i].type_name ; i++ ) {
1658 Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1659 if ( act ) {
1660 bool setActive = tname && !strcmp(tname, tools[i].type_name);
1661 Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1662 if ( verbAct ) {
1663 verbAct->set_active(setActive);
1664 }
1665 }
1666 }
1667 }
1669 static void
1670 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1671 {
1672 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1673 GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1674 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1675 GtkUIManager* mgr = gtk_ui_manager_new();
1676 GError* errVal = 0;
1677 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1678 gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1680 std::map<std::string, GtkWidget*> dataHolders;
1682 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1683 if ( aux_toolboxes[i].prep_func ) {
1684 // converted to GtkActions and UIManager
1686 GtkWidget* kludge = gtk_toolbar_new();
1687 g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1688 g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1689 dataHolders[aux_toolboxes[i].type_name] = kludge;
1690 aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1691 } else {
1693 GtkWidget *sub_toolbox = 0;
1694 if (aux_toolboxes[i].create_func == NULL)
1695 sub_toolbox = sp_empty_toolbox_new(desktop);
1696 else {
1697 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1698 }
1700 gtk_size_group_add_widget( grouper, sub_toolbox );
1702 gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1703 g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1705 }
1706 }
1708 // Second pass to create toolbars *after* all GtkActions are created
1709 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1710 if ( aux_toolboxes[i].prep_func ) {
1711 // converted to GtkActions and UIManager
1713 GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1715 GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1716 gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1718 gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1719 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1720 g_free( tmp );
1721 tmp = 0;
1723 Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
1724 if ( prefs->getBool( "/toolbox/icononly", true) ) {
1725 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1726 }
1727 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1730 gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1732 if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1733 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1734 swatch->setDesktop( desktop );
1735 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1736 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1737 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1738 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 );
1739 }
1741 gtk_widget_show_all( holder );
1742 sp_set_font_size_smaller( holder );
1744 gtk_size_group_add_widget( grouper, holder );
1746 gtk_container_add( GTK_CONTAINER(toolbox), holder );
1747 g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1748 }
1749 }
1751 g_object_unref( G_OBJECT(grouper) );
1752 }
1754 static void
1755 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1756 {
1757 gchar const *tname = ( eventcontext
1758 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1759 : NULL );
1760 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1761 GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1762 if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1763 gtk_widget_show_all(sub_toolbox);
1764 g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1765 } else {
1766 gtk_widget_hide(sub_toolbox);
1767 }
1768 }
1769 }
1771 static void
1772 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1773 {
1774 gchar const * descr =
1775 "<ui>"
1776 " <toolbar name='CommandsToolbar'>"
1777 " <toolitem action='FileNew' />"
1778 " <toolitem action='FileOpen' />"
1779 " <toolitem action='FileSave' />"
1780 " <toolitem action='FilePrint' />"
1781 " <separator />"
1782 " <toolitem action='FileImport' />"
1783 " <toolitem action='FileExport' />"
1784 " <separator />"
1785 " <toolitem action='EditUndo' />"
1786 " <toolitem action='EditRedo' />"
1787 " <separator />"
1788 " <toolitem action='EditCopy' />"
1789 " <toolitem action='EditCut' />"
1790 " <toolitem action='EditPaste' />"
1791 " <separator />"
1792 " <toolitem action='ZoomSelection' />"
1793 " <toolitem action='ZoomDrawing' />"
1794 " <toolitem action='ZoomPage' />"
1795 " <separator />"
1796 " <toolitem action='EditDuplicate' />"
1797 " <toolitem action='EditClone' />"
1798 " <toolitem action='EditUnlinkClone' />"
1799 " <separator />"
1800 " <toolitem action='SelectionGroup' />"
1801 " <toolitem action='SelectionUnGroup' />"
1802 " <separator />"
1803 " <toolitem action='DialogFillStroke' />"
1804 " <toolitem action='DialogText' />"
1805 " <toolitem action='DialogXMLEditor' />"
1806 " <toolitem action='DialogAlignDistribute' />"
1807 " <separator />"
1808 " <toolitem action='DialogPreferences' />"
1809 " <toolitem action='DialogDocumentProperties' />"
1810 " </toolbar>"
1811 "</ui>";
1812 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1813 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1815 GtkUIManager* mgr = gtk_ui_manager_new();
1816 GError* errVal = 0;
1818 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1819 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1821 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1822 if ( prefs->getBool("/toolbox/icononly", true) ) {
1823 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1824 }
1826 Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
1827 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1829 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1830 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1833 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1835 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1836 if ( child ) {
1837 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1838 }
1840 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1841 }
1843 static void
1844 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1845 {
1846 }
1848 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1849 {
1850 gtk_widget_show(toolbox_toplevel);
1851 GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1853 GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1854 if (!shown_toolbox) {
1855 return;
1856 }
1857 gtk_widget_show(toolbox);
1859 gtk_widget_show_all(shown_toolbox);
1860 }
1862 static GtkWidget *
1863 sp_empty_toolbox_new(SPDesktop *desktop)
1864 {
1865 GtkWidget *tbl = gtk_toolbar_new();
1866 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1867 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1869 gtk_widget_show_all(tbl);
1870 sp_set_font_size_smaller (tbl);
1872 return tbl;
1873 }
1875 #define MODE_LABEL_WIDTH 70
1877 //########################
1878 //## Star ##
1879 //########################
1881 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1882 {
1883 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1885 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1886 // do not remember prefs if this call is initiated by an undo change, because undoing object
1887 // creation sets bogus values to its attributes before it is deleted
1888 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1889 prefs->setInt("/tools/shapes/star/magnitude", (gint)adj->value);
1890 }
1892 // quit if run by the attr_changed listener
1893 if (g_object_get_data( dataKludge, "freeze" )) {
1894 return;
1895 }
1897 // in turn, prevent listener from responding
1898 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1900 bool modmade = false;
1902 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1903 GSList const *items = selection->itemList();
1904 for (; items != NULL; items = items->next) {
1905 if (SP_IS_STAR((SPItem *) items->data)) {
1906 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1907 sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1908 sp_repr_set_svg_double(repr, "sodipodi:arg2",
1909 (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1910 + M_PI / (gint)adj->value));
1911 SP_OBJECT((SPItem *) items->data)->updateRepr();
1912 modmade = true;
1913 }
1914 }
1915 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1916 _("Star: Change number of corners"));
1918 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1919 }
1921 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1922 {
1923 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1925 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1926 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1927 prefs->setDouble("/tools/shapes/star/proportion", adj->value);
1928 }
1930 // quit if run by the attr_changed listener
1931 if (g_object_get_data( dataKludge, "freeze" )) {
1932 return;
1933 }
1935 // in turn, prevent listener from responding
1936 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1938 bool modmade = false;
1939 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1940 GSList const *items = selection->itemList();
1941 for (; items != NULL; items = items->next) {
1942 if (SP_IS_STAR((SPItem *) items->data)) {
1943 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1945 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1946 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1947 if (r2 < r1) {
1948 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1949 } else {
1950 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1951 }
1953 SP_OBJECT((SPItem *) items->data)->updateRepr();
1954 modmade = true;
1955 }
1956 }
1958 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1959 _("Star: Change spoke ratio"));
1961 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1962 }
1964 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1965 {
1966 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1967 bool flat = ege_select_one_action_get_active( act ) == 0;
1969 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1970 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1971 prefs->setBool( "/tools/shapes/star/isflatsided", flat);
1972 }
1974 // quit if run by the attr_changed listener
1975 if (g_object_get_data( dataKludge, "freeze" )) {
1976 return;
1977 }
1979 // in turn, prevent listener from responding
1980 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1982 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1983 GSList const *items = selection->itemList();
1984 GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1985 bool modmade = false;
1987 if ( prop_action ) {
1988 gtk_action_set_sensitive( prop_action, !flat );
1989 }
1991 for (; items != NULL; items = items->next) {
1992 if (SP_IS_STAR((SPItem *) items->data)) {
1993 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1994 repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1995 SP_OBJECT((SPItem *) items->data)->updateRepr();
1996 modmade = true;
1997 }
1998 }
2000 if (modmade) {
2001 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2002 flat ? _("Make polygon") : _("Make star"));
2003 }
2005 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2006 }
2008 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2009 {
2010 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2012 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2013 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2014 prefs->setDouble("/tools/shapes/star/rounded", (gdouble) adj->value);
2015 }
2017 // quit if run by the attr_changed listener
2018 if (g_object_get_data( dataKludge, "freeze" )) {
2019 return;
2020 }
2022 // in turn, prevent listener from responding
2023 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2025 bool modmade = false;
2027 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2028 GSList const *items = selection->itemList();
2029 for (; items != NULL; items = items->next) {
2030 if (SP_IS_STAR((SPItem *) items->data)) {
2031 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2032 sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2033 SP_OBJECT(items->data)->updateRepr();
2034 modmade = true;
2035 }
2036 }
2037 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2038 _("Star: Change rounding"));
2040 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2041 }
2043 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2044 {
2045 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2047 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2048 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2049 prefs->setDouble("/tools/shapes/star/randomized", (gdouble) adj->value);
2050 }
2052 // quit if run by the attr_changed listener
2053 if (g_object_get_data( dataKludge, "freeze" )) {
2054 return;
2055 }
2057 // in turn, prevent listener from responding
2058 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2060 bool modmade = false;
2062 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2063 GSList const *items = selection->itemList();
2064 for (; items != NULL; items = items->next) {
2065 if (SP_IS_STAR((SPItem *) items->data)) {
2066 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2067 sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2068 SP_OBJECT(items->data)->updateRepr();
2069 modmade = true;
2070 }
2071 }
2072 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2073 _("Star: Change randomization"));
2075 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2076 }
2079 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2080 gchar const */*old_value*/, gchar const */*new_value*/,
2081 bool /*is_interactive*/, gpointer data)
2082 {
2083 GtkWidget *tbl = GTK_WIDGET(data);
2085 // quit if run by the _changed callbacks
2086 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2087 return;
2088 }
2090 // in turn, prevent callbacks from responding
2091 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2093 GtkAdjustment *adj = 0;
2095 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2096 bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2098 if (!strcmp(name, "inkscape:randomized")) {
2099 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2100 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2101 } else if (!strcmp(name, "inkscape:rounded")) {
2102 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2103 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2104 } else if (!strcmp(name, "inkscape:flatsided")) {
2105 GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2106 char const *flatsides = repr->attribute("inkscape:flatsided");
2107 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2108 if ( flatsides && !strcmp(flatsides,"false") ) {
2109 ege_select_one_action_set_active( flat_action, 1 );
2110 gtk_action_set_sensitive( prop_action, TRUE );
2111 } else {
2112 ege_select_one_action_set_active( flat_action, 0 );
2113 gtk_action_set_sensitive( prop_action, FALSE );
2114 }
2115 } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2116 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2117 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2118 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2119 if (r2 < r1) {
2120 gtk_adjustment_set_value(adj, r2/r1);
2121 } else {
2122 gtk_adjustment_set_value(adj, r1/r2);
2123 }
2124 } else if (!strcmp(name, "sodipodi:sides")) {
2125 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2126 gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2127 }
2129 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2130 }
2133 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2134 {
2135 NULL, /* child_added */
2136 NULL, /* child_removed */
2137 star_tb_event_attr_changed,
2138 NULL, /* content_changed */
2139 NULL /* order_changed */
2140 };
2143 /**
2144 * \param selection Should not be NULL.
2145 */
2146 static void
2147 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2148 {
2149 int n_selected = 0;
2150 Inkscape::XML::Node *repr = NULL;
2152 purge_repr_listener( tbl, tbl );
2154 for (GSList const *items = selection->itemList();
2155 items != NULL;
2156 items = items->next)
2157 {
2158 if (SP_IS_STAR((SPItem *) items->data)) {
2159 n_selected++;
2160 repr = SP_OBJECT_REPR((SPItem *) items->data);
2161 }
2162 }
2164 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2166 if (n_selected == 0) {
2167 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2168 } else if (n_selected == 1) {
2169 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2171 if (repr) {
2172 g_object_set_data( tbl, "repr", repr );
2173 Inkscape::GC::anchor(repr);
2174 sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2175 sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2176 }
2177 } else {
2178 // FIXME: implement averaging of all parameters for multiple selected stars
2179 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2180 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2181 }
2182 }
2185 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2186 {
2187 // FIXME: in this and all other _default functions, set some flag telling the value_changed
2188 // callbacks to lump all the changes for all selected objects in one undo step
2190 GtkAdjustment *adj = 0;
2192 // fixme: make settable in prefs!
2193 gint mag = 5;
2194 gdouble prop = 0.5;
2195 gboolean flat = FALSE;
2196 gdouble randomized = 0;
2197 gdouble rounded = 0;
2199 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2200 ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2202 GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2203 gtk_action_set_sensitive( sb2, !flat );
2205 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2206 gtk_adjustment_set_value(adj, mag);
2207 gtk_adjustment_value_changed(adj);
2209 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2210 gtk_adjustment_set_value(adj, prop);
2211 gtk_adjustment_value_changed(adj);
2213 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2214 gtk_adjustment_set_value(adj, rounded);
2215 gtk_adjustment_value_changed(adj);
2217 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2218 gtk_adjustment_set_value(adj, randomized);
2219 gtk_adjustment_value_changed(adj);
2220 }
2223 void
2224 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2225 {
2226 GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2227 if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2228 GtkWidget *l = gtk_label_new(NULL);
2229 gtk_label_set_markup(GTK_LABEL(l), title);
2230 gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2231 if ( GTK_IS_TOOLBAR(tbl) ) {
2232 gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2233 } else {
2234 gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2235 }
2236 gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2237 }
2240 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2241 {
2242 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
2244 {
2245 EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2246 ege_output_action_set_use_markup( act, TRUE );
2247 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2248 g_object_set_data( holder, "mode_action", act );
2249 }
2251 {
2252 EgeAdjustmentAction* eact = 0;
2253 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2254 bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2256 /* Flatsided checkbox */
2257 {
2258 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2260 GtkTreeIter iter;
2261 gtk_list_store_append( model, &iter );
2262 gtk_list_store_set( model, &iter,
2263 0, _("Polygon"),
2264 1, _("Regular polygon (with one handle) instead of a star"),
2265 2, "star_flat",
2266 -1 );
2268 gtk_list_store_append( model, &iter );
2269 gtk_list_store_set( model, &iter,
2270 0, _("Star"),
2271 1, _("Star instead of a regular polygon (with one handle)"),
2272 2, "star_angled",
2273 -1 );
2275 EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2276 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2277 g_object_set_data( holder, "flat_action", act );
2279 ege_select_one_action_set_appearance( act, "full" );
2280 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2281 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2282 ege_select_one_action_set_icon_column( act, 2 );
2283 ege_select_one_action_set_icon_size( act, secondarySize );
2284 ege_select_one_action_set_tooltip_column( act, 1 );
2286 ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2287 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2288 }
2290 /* Magnitude */
2291 {
2292 gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2293 gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2294 eact = create_adjustment_action( "MagnitudeAction",
2295 _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2296 "/tools/shapes/star/magnitude", 3,
2297 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2298 3, 1024, 1, 5,
2299 labels, values, G_N_ELEMENTS(labels),
2300 sp_stb_magnitude_value_changed,
2301 1.0, 0 );
2302 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2303 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2304 }
2306 /* Spoke ratio */
2307 {
2308 gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2309 gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2310 eact = create_adjustment_action( "SpokeAction",
2311 _("Spoke ratio"), _("Spoke ratio:"),
2312 // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2313 // Base radius is the same for the closest handle.
2314 _("Base radius to tip radius ratio"),
2315 "/tools/shapes/star/proportion", 0.5,
2316 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2317 0.01, 1.0, 0.01, 0.1,
2318 labels, values, G_N_ELEMENTS(labels),
2319 sp_stb_proportion_value_changed );
2320 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2321 g_object_set_data( holder, "prop_action", eact );
2322 }
2324 if ( !isFlatSided ) {
2325 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2326 } else {
2327 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2328 }
2330 /* Roundedness */
2331 {
2332 gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2333 gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2334 eact = create_adjustment_action( "RoundednessAction",
2335 _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2336 "/tools/shapes/star/rounded", 0.0,
2337 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2338 -10.0, 10.0, 0.01, 0.1,
2339 labels, values, G_N_ELEMENTS(labels),
2340 sp_stb_rounded_value_changed );
2341 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2342 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2343 }
2345 /* Randomization */
2346 {
2347 gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2348 gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2349 eact = create_adjustment_action( "RandomizationAction",
2350 _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2351 "/tools/shapes/star/randomized", 0.0,
2352 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2353 -10.0, 10.0, 0.001, 0.01,
2354 labels, values, G_N_ELEMENTS(labels),
2355 sp_stb_randomized_value_changed, 0.1, 3 );
2356 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2357 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2358 }
2359 }
2361 {
2362 /* Reset */
2363 {
2364 GtkAction* act = gtk_action_new( "StarResetAction",
2365 _("Defaults"),
2366 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2367 GTK_STOCK_CLEAR );
2368 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2369 gtk_action_group_add_action( mainActions, act );
2370 gtk_action_set_sensitive( act, TRUE );
2371 }
2372 }
2374 sigc::connection *connection = new sigc::connection(
2375 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2376 );
2377 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2378 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2379 }
2382 //########################
2383 //## Rect ##
2384 //########################
2386 static void sp_rtb_sensitivize( GObject *tbl )
2387 {
2388 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2389 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2390 GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2392 if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2393 gtk_action_set_sensitive( not_rounded, FALSE );
2394 } else {
2395 gtk_action_set_sensitive( not_rounded, TRUE );
2396 }
2397 }
2400 static void
2401 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2402 void (*setter)(SPRect *, gdouble))
2403 {
2404 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2406 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2407 SPUnit const *unit = tracker->getActiveUnit();
2409 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2410 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2411 prefs->setDouble(Glib::ustring("/tools/shapes/rect/") + value_name, sp_units_get_pixels(adj->value, *unit));
2412 }
2414 // quit if run by the attr_changed listener
2415 if (g_object_get_data( tbl, "freeze" )) {
2416 return;
2417 }
2419 // in turn, prevent listener from responding
2420 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2422 bool modmade = false;
2423 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2424 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2425 if (SP_IS_RECT(items->data)) {
2426 if (adj->value != 0) {
2427 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2428 } else {
2429 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2430 }
2431 modmade = true;
2432 }
2433 }
2435 sp_rtb_sensitivize( tbl );
2437 if (modmade) {
2438 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2439 _("Change rectangle"));
2440 }
2442 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2443 }
2445 static void
2446 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2447 {
2448 sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2449 }
2451 static void
2452 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2453 {
2454 sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2455 }
2457 static void
2458 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2459 {
2460 sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2461 }
2463 static void
2464 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2465 {
2466 sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2467 }
2471 static void
2472 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2473 {
2474 GtkAdjustment *adj = 0;
2476 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2477 gtk_adjustment_set_value(adj, 0.0);
2478 // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2479 gtk_adjustment_value_changed(adj);
2481 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2482 gtk_adjustment_set_value(adj, 0.0);
2483 gtk_adjustment_value_changed(adj);
2485 sp_rtb_sensitivize( obj );
2486 }
2488 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2489 gchar const */*old_value*/, gchar const */*new_value*/,
2490 bool /*is_interactive*/, gpointer data)
2491 {
2492 GObject *tbl = G_OBJECT(data);
2494 // quit if run by the _changed callbacks
2495 if (g_object_get_data( tbl, "freeze" )) {
2496 return;
2497 }
2499 // in turn, prevent callbacks from responding
2500 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2502 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2503 SPUnit const *unit = tracker->getActiveUnit();
2505 gpointer item = g_object_get_data( tbl, "item" );
2506 if (item && SP_IS_RECT(item)) {
2507 {
2508 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2509 gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2510 gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2511 }
2513 {
2514 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2515 gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2516 gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2517 }
2519 {
2520 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2521 gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2522 gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2523 }
2525 {
2526 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2527 gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2528 gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2529 }
2530 }
2532 sp_rtb_sensitivize( tbl );
2534 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2535 }
2538 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2539 NULL, /* child_added */
2540 NULL, /* child_removed */
2541 rect_tb_event_attr_changed,
2542 NULL, /* content_changed */
2543 NULL /* order_changed */
2544 };
2546 /**
2547 * \param selection should not be NULL.
2548 */
2549 static void
2550 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2551 {
2552 int n_selected = 0;
2553 Inkscape::XML::Node *repr = NULL;
2554 SPItem *item = NULL;
2556 if ( g_object_get_data( tbl, "repr" ) ) {
2557 g_object_set_data( tbl, "item", NULL );
2558 }
2559 purge_repr_listener( tbl, tbl );
2561 for (GSList const *items = selection->itemList();
2562 items != NULL;
2563 items = items->next) {
2564 if (SP_IS_RECT((SPItem *) items->data)) {
2565 n_selected++;
2566 item = (SPItem *) items->data;
2567 repr = SP_OBJECT_REPR(item);
2568 }
2569 }
2571 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2573 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2575 if (n_selected == 0) {
2576 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2578 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2579 gtk_action_set_sensitive(w, FALSE);
2580 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2581 gtk_action_set_sensitive(h, FALSE);
2583 } else if (n_selected == 1) {
2584 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2585 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2587 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2588 gtk_action_set_sensitive(w, TRUE);
2589 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2590 gtk_action_set_sensitive(h, TRUE);
2592 if (repr) {
2593 g_object_set_data( tbl, "repr", repr );
2594 g_object_set_data( tbl, "item", item );
2595 Inkscape::GC::anchor(repr);
2596 sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2597 sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2598 }
2599 } else {
2600 // FIXME: implement averaging of all parameters for multiple selected
2601 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2602 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2603 sp_rtb_sensitivize( tbl );
2604 }
2605 }
2608 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2609 {
2610 EgeAdjustmentAction* eact = 0;
2611 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
2613 {
2614 EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2615 ege_output_action_set_use_markup( act, TRUE );
2616 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2617 g_object_set_data( holder, "mode_action", act );
2618 }
2620 // rx/ry units menu: create
2621 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2622 //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2623 // fixme: add % meaning per cent of the width/height
2624 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2625 g_object_set_data( holder, "tracker", tracker );
2627 /* W */
2628 {
2629 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2630 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2631 eact = create_adjustment_action( "RectWidthAction",
2632 _("Width"), _("W:"), _("Width of rectangle"),
2633 "/tools/shapes/rect/width", 0,
2634 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2635 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2636 labels, values, G_N_ELEMENTS(labels),
2637 sp_rtb_width_value_changed );
2638 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2639 g_object_set_data( holder, "width_action", eact );
2640 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2641 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2642 }
2644 /* H */
2645 {
2646 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2647 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2648 eact = create_adjustment_action( "RectHeightAction",
2649 _("Height"), _("H:"), _("Height of rectangle"),
2650 "/tools/shapes/rect/height", 0,
2651 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2652 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2653 labels, values, G_N_ELEMENTS(labels),
2654 sp_rtb_height_value_changed );
2655 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2656 g_object_set_data( holder, "height_action", eact );
2657 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2658 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2659 }
2661 /* rx */
2662 {
2663 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2664 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2665 eact = create_adjustment_action( "RadiusXAction",
2666 _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2667 "/tools/shapes/rect/rx", 0,
2668 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2669 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2670 labels, values, G_N_ELEMENTS(labels),
2671 sp_rtb_rx_value_changed);
2672 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2673 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2674 }
2676 /* ry */
2677 {
2678 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2679 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2680 eact = create_adjustment_action( "RadiusYAction",
2681 _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2682 "/tools/shapes/rect/ry", 0,
2683 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2684 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2685 labels, values, G_N_ELEMENTS(labels),
2686 sp_rtb_ry_value_changed);
2687 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2688 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2689 }
2691 // add the units menu
2692 {
2693 GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2694 gtk_action_group_add_action( mainActions, act );
2695 }
2697 /* Reset */
2698 {
2699 InkAction* inky = ink_action_new( "RectResetAction",
2700 _("Not rounded"),
2701 _("Make corners sharp"),
2702 "squared_corner",
2703 secondarySize );
2704 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2705 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2706 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2707 g_object_set_data( holder, "not_rounded", inky );
2708 }
2710 g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2711 sp_rtb_sensitivize( holder );
2713 sigc::connection *connection = new sigc::connection(
2714 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2715 );
2716 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2717 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2718 }
2720 //########################
2721 //## 3D Box ##
2722 //########################
2724 // normalize angle so that it lies in the interval [0,360]
2725 static double box3d_normalize_angle (double a) {
2726 double angle = a + ((int) (a/360.0))*360;
2727 if (angle < 0) {
2728 angle += 360.0;
2729 }
2730 return angle;
2731 }
2733 static void
2734 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2735 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2736 // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2737 // have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2738 // are reset).
2739 bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2741 if (is_infinite) {
2742 gtk_toggle_action_set_active(tact, TRUE);
2743 gtk_action_set_sensitive(act, TRUE);
2745 double angle = persp3d_get_infinite_angle(persp, axis);
2746 if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2747 gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2748 }
2749 } else {
2750 gtk_toggle_action_set_active(tact, FALSE);
2751 gtk_action_set_sensitive(act, FALSE);
2752 }
2753 }
2755 static void
2756 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2757 if (!persp_repr) {
2758 g_print ("No perspective given to box3d_resync_toolbar().\n");
2759 return;
2760 }
2762 GtkWidget *tbl = GTK_WIDGET(data);
2763 GtkAdjustment *adj = 0;
2764 GtkAction *act = 0;
2765 GtkToggleAction *tact = 0;
2766 Persp3D *persp = persp3d_get_from_repr(persp_repr);
2767 {
2768 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2769 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2770 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2772 box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2773 }
2774 {
2775 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2776 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2777 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2779 box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2780 }
2781 {
2782 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2783 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2784 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2786 box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2787 }
2788 }
2790 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2791 gchar const */*old_value*/, gchar const */*new_value*/,
2792 bool /*is_interactive*/, gpointer data)
2793 {
2794 GtkWidget *tbl = GTK_WIDGET(data);
2796 // quit if run by the attr_changed or selection changed listener
2797 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2798 return;
2799 }
2801 // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2802 // sp_document_maybe_done() when the document is undo insensitive)
2803 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2805 // TODO: Only update the appropriate part of the toolbar
2806 // if (!strcmp(name, "inkscape:vp_z")) {
2807 box3d_resync_toolbar(repr, G_OBJECT(tbl));
2808 // }
2810 Persp3D *persp = persp3d_get_from_repr(repr);
2811 persp3d_update_box_reprs(persp);
2813 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2814 }
2816 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2817 {
2818 NULL, /* child_added */
2819 NULL, /* child_removed */
2820 box3d_persp_tb_event_attr_changed,
2821 NULL, /* content_changed */
2822 NULL /* order_changed */
2823 };
2825 /**
2826 * \param selection Should not be NULL.
2827 */
2828 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2829 // Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2830 static void
2831 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2832 {
2833 // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2834 // disable the angle entry fields for this direction (otherwise entering a value in them should only
2835 // update the perspectives with infinite VPs and leave the other ones untouched).
2837 Inkscape::XML::Node *persp_repr = NULL;
2838 purge_repr_listener(tbl, tbl);
2840 SPItem *item = selection->singleItem();
2841 if (item && SP_IS_BOX3D(item)) {
2842 // FIXME: Also deal with multiple selected boxes
2843 SPBox3D *box = SP_BOX3D(item);
2844 Persp3D *persp = box3d_get_perspective(box);
2845 persp_repr = SP_OBJECT_REPR(persp);
2846 if (persp_repr) {
2847 g_object_set_data(tbl, "repr", persp_repr);
2848 Inkscape::GC::anchor(persp_repr);
2849 sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2850 sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2851 }
2853 inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2854 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2855 prefs->setString("/tools/shapes/3dbox/persp", persp_repr->attribute("id"));
2857 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
2858 box3d_resync_toolbar(persp_repr, tbl);
2859 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
2860 }
2861 }
2863 static void
2864 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2865 {
2866 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2867 SPDocument *document = sp_desktop_document(desktop);
2869 // quit if run by the attr_changed or selection changed listener
2870 if (g_object_get_data( dataKludge, "freeze" )) {
2871 return;
2872 }
2874 // in turn, prevent listener from responding
2875 g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
2877 //Persp3D *persp = document->current_persp3d;
2878 std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
2879 if (sel_persps.empty()) {
2880 // this can happen when the document is created; we silently ignore it
2881 return;
2882 }
2883 Persp3D *persp = sel_persps.front();
2885 persp->tmat.set_infinite_direction (axis, adj->value);
2886 SP_OBJECT(persp)->updateRepr();
2888 // TODO: use the correct axis here, too
2889 sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2891 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2892 }
2895 static void
2896 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2897 {
2898 box3d_angle_value_changed(adj, dataKludge, Proj::X);
2899 }
2901 static void
2902 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2903 {
2904 box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2905 }
2907 static void
2908 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2909 {
2910 box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2911 }
2914 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2915 {
2916 // TODO: Take all selected perspectives into account
2917 std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
2918 if (sel_persps.empty()) {
2919 // this can happen when the document is created; we silently ignore it
2920 return;
2921 }
2922 Persp3D *persp = sel_persps.front();
2924 bool set_infinite = gtk_toggle_action_get_active(act);
2925 persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2926 }
2928 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2929 {
2930 box3d_vp_state_changed(act, box3d_angle, Proj::X);
2931 }
2933 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2934 {
2935 box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2936 }
2938 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2939 {
2940 box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2941 }
2943 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2944 {
2945 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2946 EgeAdjustmentAction* eact = 0;
2947 SPDocument *document = sp_desktop_document (desktop);
2948 Persp3D *persp = document->current_persp3d;
2950 EgeAdjustmentAction* box3d_angle_x = 0;
2951 EgeAdjustmentAction* box3d_angle_y = 0;
2952 EgeAdjustmentAction* box3d_angle_z = 0;
2954 /* Angle X */
2955 {
2956 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2957 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2958 eact = create_adjustment_action( "3DBoxAngleXAction",
2959 _("Angle in X direction"), _("Angle X:"),
2960 // Translators: PL is short for 'perspective line'
2961 _("Angle of PLs in X direction"),
2962 "/tools/shapes/3dbox/box3d_angle_x", 30,
2963 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2964 -360.0, 360.0, 1.0, 10.0,
2965 labels, values, G_N_ELEMENTS(labels),
2966 box3d_angle_x_value_changed );
2967 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2968 g_object_set_data( holder, "box3d_angle_x_action", eact );
2969 box3d_angle_x = eact;
2970 }
2972 if (!persp3d_VP_is_finite(persp, Proj::X)) {
2973 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2974 } else {
2975 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2976 }
2979 /* VP X state */
2980 {
2981 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2982 // Translators: VP is short for 'vanishing point'
2983 _("State of VP in X direction"),
2984 _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2985 "toggle_vp_x",
2986 Inkscape::ICON_SIZE_DECORATION );
2987 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2988 g_object_set_data( holder, "box3d_vp_x_state_action", act );
2989 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2990 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
2991 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
2992 }
2994 /* Angle Y */
2995 {
2996 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2997 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2998 eact = create_adjustment_action( "3DBoxAngleYAction",
2999 _("Angle in Y direction"), _("Angle Y:"),
3000 // Translators: PL is short for 'perspective line'
3001 _("Angle of PLs in Y direction"),
3002 "/tools/shapes/3dbox/box3d_angle_y", 30,
3003 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3004 -360.0, 360.0, 1.0, 10.0,
3005 labels, values, G_N_ELEMENTS(labels),
3006 box3d_angle_y_value_changed );
3007 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3008 g_object_set_data( holder, "box3d_angle_y_action", eact );
3009 box3d_angle_y = eact;
3010 }
3012 if (!persp3d_VP_is_finite(persp, Proj::Y)) {
3013 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3014 } else {
3015 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3016 }
3018 /* VP Y state */
3019 {
3020 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
3021 // Translators: VP is short for 'vanishing point'
3022 _("State of VP in Y direction"),
3023 _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
3024 "toggle_vp_y",
3025 Inkscape::ICON_SIZE_DECORATION );
3026 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3027 g_object_set_data( holder, "box3d_vp_y_state_action", act );
3028 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
3029 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3030 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3031 }
3033 /* Angle Z */
3034 {
3035 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3036 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3037 eact = create_adjustment_action( "3DBoxAngleZAction",
3038 _("Angle in Z direction"), _("Angle Z:"),
3039 // Translators: PL is short for 'perspective line'
3040 _("Angle of PLs in Z direction"),
3041 "/tools/shapes/3dbox/box3d_angle_z", 30,
3042 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3043 -360.0, 360.0, 1.0, 10.0,
3044 labels, values, G_N_ELEMENTS(labels),
3045 box3d_angle_z_value_changed );
3046 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3047 g_object_set_data( holder, "box3d_angle_z_action", eact );
3048 box3d_angle_z = eact;
3049 }
3051 if (!persp3d_VP_is_finite(persp, Proj::Z)) {
3052 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3053 } else {
3054 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3055 }
3057 /* VP Z state */
3058 {
3059 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3060 // Translators: VP is short for 'vanishing point'
3061 _("State of VP in Z direction"),
3062 _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3063 "toggle_vp_z",
3064 Inkscape::ICON_SIZE_DECORATION );
3065 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3066 g_object_set_data( holder, "box3d_vp_z_state_action", act );
3067 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3068 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3069 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3070 }
3072 sigc::connection *connection = new sigc::connection(
3073 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3074 );
3075 g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3076 g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3077 }
3079 //########################
3080 //## Spiral ##
3081 //########################
3083 static void
3084 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, Glib::ustring const &value_name)
3085 {
3086 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3088 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3089 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3090 prefs->setDouble("/tools/shapes/spiral/" + value_name, adj->value);
3091 }
3093 // quit if run by the attr_changed listener
3094 if (g_object_get_data( tbl, "freeze" )) {
3095 return;
3096 }
3098 // in turn, prevent listener from responding
3099 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3101 gchar* namespaced_name = g_strconcat("sodipodi:", value_name.data(), NULL);
3103 bool modmade = false;
3104 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3105 items != NULL;
3106 items = items->next)
3107 {
3108 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3109 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3110 sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3111 SP_OBJECT((SPItem *) items->data)->updateRepr();
3112 modmade = true;
3113 }
3114 }
3116 g_free(namespaced_name);
3118 if (modmade) {
3119 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3120 _("Change spiral"));
3121 }
3123 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3124 }
3126 static void
3127 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3128 {
3129 sp_spl_tb_value_changed(adj, tbl, "revolution");
3130 }
3132 static void
3133 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3134 {
3135 sp_spl_tb_value_changed(adj, tbl, "expansion");
3136 }
3138 static void
3139 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3140 {
3141 sp_spl_tb_value_changed(adj, tbl, "t0");
3142 }
3144 static void
3145 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3146 {
3147 GtkWidget *tbl = GTK_WIDGET(obj);
3149 GtkAdjustment *adj;
3151 // fixme: make settable
3152 gdouble rev = 5;
3153 gdouble exp = 1.0;
3154 gdouble t0 = 0.0;
3156 adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3157 gtk_adjustment_set_value(adj, rev);
3158 gtk_adjustment_value_changed(adj);
3160 adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3161 gtk_adjustment_set_value(adj, exp);
3162 gtk_adjustment_value_changed(adj);
3164 adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3165 gtk_adjustment_set_value(adj, t0);
3166 gtk_adjustment_value_changed(adj);
3168 spinbutton_defocus(GTK_OBJECT(tbl));
3169 }
3172 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3173 gchar const */*old_value*/, gchar const */*new_value*/,
3174 bool /*is_interactive*/, gpointer data)
3175 {
3176 GtkWidget *tbl = GTK_WIDGET(data);
3178 // quit if run by the _changed callbacks
3179 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3180 return;
3181 }
3183 // in turn, prevent callbacks from responding
3184 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3186 GtkAdjustment *adj;
3187 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3188 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3190 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3191 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3193 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3194 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3196 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3197 }
3200 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3201 NULL, /* child_added */
3202 NULL, /* child_removed */
3203 spiral_tb_event_attr_changed,
3204 NULL, /* content_changed */
3205 NULL /* order_changed */
3206 };
3208 static void
3209 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3210 {
3211 int n_selected = 0;
3212 Inkscape::XML::Node *repr = NULL;
3214 purge_repr_listener( tbl, tbl );
3216 for (GSList const *items = selection->itemList();
3217 items != NULL;
3218 items = items->next)
3219 {
3220 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3221 n_selected++;
3222 repr = SP_OBJECT_REPR((SPItem *) items->data);
3223 }
3224 }
3226 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3228 if (n_selected == 0) {
3229 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3230 } else if (n_selected == 1) {
3231 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3233 if (repr) {
3234 g_object_set_data( tbl, "repr", repr );
3235 Inkscape::GC::anchor(repr);
3236 sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3237 sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3238 }
3239 } else {
3240 // FIXME: implement averaging of all parameters for multiple selected
3241 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3242 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3243 }
3244 }
3247 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3248 {
3249 EgeAdjustmentAction* eact = 0;
3250 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3252 {
3253 EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3254 ege_output_action_set_use_markup( act, TRUE );
3255 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3256 g_object_set_data( holder, "mode_action", act );
3257 }
3259 /* Revolution */
3260 {
3261 gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3262 gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3263 eact = create_adjustment_action( "SpiralRevolutionAction",
3264 _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3265 "/tools/shapes/spiral/revolution", 3.0,
3266 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3267 0.01, 1024.0, 0.1, 1.0,
3268 labels, values, G_N_ELEMENTS(labels),
3269 sp_spl_tb_revolution_value_changed, 1, 2);
3270 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3271 }
3273 /* Expansion */
3274 {
3275 gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3276 gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3277 eact = create_adjustment_action( "SpiralExpansionAction",
3278 _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3279 "/tools/shapes/spiral/expansion", 1.0,
3280 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3281 0.0, 1000.0, 0.01, 1.0,
3282 labels, values, G_N_ELEMENTS(labels),
3283 sp_spl_tb_expansion_value_changed);
3284 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3285 }
3287 /* T0 */
3288 {
3289 gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3290 gdouble values[] = {0, 0.5, 0.9};
3291 eact = create_adjustment_action( "SpiralT0Action",
3292 _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3293 "/tools/shapes/spiral/t0", 0.0,
3294 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3295 0.0, 0.999, 0.01, 1.0,
3296 labels, values, G_N_ELEMENTS(labels),
3297 sp_spl_tb_t0_value_changed);
3298 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3299 }
3301 /* Reset */
3302 {
3303 InkAction* inky = ink_action_new( "SpiralResetAction",
3304 _("Defaults"),
3305 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3306 GTK_STOCK_CLEAR,
3307 secondarySize );
3308 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3309 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3310 }
3313 sigc::connection *connection = new sigc::connection(
3314 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3315 );
3316 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3317 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3318 }
3320 //########################
3321 //## Pen/Pencil ##
3322 //########################
3324 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3325 static Glib::ustring const
3326 freehand_tool_name(GObject *dataKludge)
3327 {
3328 SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3329 return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3330 ? "/tools/freehand/pen"
3331 : "/tools/freehand/pencil" );
3332 }
3334 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3335 {
3336 gint mode = ege_select_one_action_get_active(act);
3338 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3339 prefs->setInt(freehand_tool_name(tbl) + "/freehand-mode", mode);
3341 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3343 // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3344 // preparatory work here
3345 if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3346 SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3347 sp_pen_context_set_polyline_mode(pc);
3348 }
3349 }
3351 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3352 {
3353 /* Freehand mode toggle buttons */
3354 {
3355 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3356 guint freehandMode = prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/freehand-mode" : "/tools/freehand/pen/freehand-mode" ), 0);
3357 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3359 {
3360 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3362 GtkTreeIter iter;
3363 gtk_list_store_append( model, &iter );
3364 gtk_list_store_set( model, &iter,
3365 0, _("Bezier"),
3366 1, _("Create regular Bezier path"),
3367 2, "bezier_mode",
3368 -1 );
3370 gtk_list_store_append( model, &iter );
3371 gtk_list_store_set( model, &iter,
3372 0, _("Spiro"),
3373 1, _("Create Spiro path"),
3374 2, "spiro_splines_mode",
3375 -1 );
3377 if (!tool_is_pencil) {
3378 gtk_list_store_append( model, &iter );
3379 gtk_list_store_set( model, &iter,
3380 0, _("Zigzag"),
3381 1, _("Create a sequence of straight line segments"),
3382 2, "polylines_mode",
3383 -1 );
3385 gtk_list_store_append( model, &iter );
3386 gtk_list_store_set( model, &iter,
3387 0, _("Paraxial"),
3388 1, _("Create a sequence of paraxial line segments"),
3389 2, "paraxial_lines_mode",
3390 -1 );
3391 }
3393 EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3394 "FreehandModeActionPencil" :
3395 "FreehandModeActionPen",
3396 (_("Mode:")), ("Mode"), NULL, GTK_TREE_MODEL(model) );
3397 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3399 ege_select_one_action_set_appearance( act, "full" );
3400 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3401 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3402 ege_select_one_action_set_icon_column( act, 2 );
3403 ege_select_one_action_set_icon_size( act, secondarySize );
3404 ege_select_one_action_set_tooltip_column( act, 1 );
3406 ege_select_one_action_set_active( act, freehandMode);
3407 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3408 }
3409 }
3410 }
3412 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3413 gint shape = ege_select_one_action_get_active( act );
3414 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3415 prefs->setInt(freehand_tool_name(dataKludge) + "/shape", shape);
3416 }
3418 /**
3419 * \brief Generate the list of freehand advanced shape option entries.
3420 */
3421 GList * freehand_shape_dropdown_items_list() {
3422 GList *glist = NULL;
3424 glist = g_list_append (glist, _("None"));
3425 glist = g_list_append (glist, _("Triangle in"));
3426 glist = g_list_append (glist, _("Triangle out"));
3427 glist = g_list_append (glist, _("Ellipse"));
3428 glist = g_list_append (glist, _("From clipboard"));
3430 return glist;
3431 }
3433 static void
3434 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3435 /*advanced shape options */
3436 {
3437 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3438 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3440 GList* items = 0;
3441 gint count = 0;
3442 for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3443 {
3444 GtkTreeIter iter;
3445 gtk_list_store_append( model, &iter );
3446 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3447 count++;
3448 }
3449 g_list_free( items );
3450 items = 0;
3451 EgeSelectOneAction* act1 = ege_select_one_action_new(
3452 tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3453 _("Shape:"), ("Shape"), NULL, GTK_TREE_MODEL(model));
3454 g_object_set( act1, "short_label", _("Shape:"), NULL );
3455 ege_select_one_action_set_appearance( act1, "compact" );
3456 ege_select_one_action_set_active( act1, prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/shape" : "/tools/freehand/pen/shape" ), 0) );
3457 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3458 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3459 g_object_set_data( holder, "shape_action", act1 );
3460 }
3461 }
3463 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3464 {
3465 sp_add_freehand_mode_toggle(mainActions, holder, false);
3466 freehand_add_advanced_shape_options(mainActions, holder, false);
3467 }
3470 static void
3471 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3472 {
3473 GtkWidget *tbl = GTK_WIDGET(obj);
3475 GtkAdjustment *adj;
3477 // fixme: make settable
3478 gdouble tolerance = 4;
3480 adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3481 gtk_adjustment_set_value(adj, tolerance);
3482 gtk_adjustment_value_changed(adj);
3484 spinbutton_defocus(GTK_OBJECT(tbl));
3485 }
3487 static void
3488 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3489 {
3490 // quit if run by the attr_changed listener
3491 if (g_object_get_data( tbl, "freeze" )) {
3492 return;
3493 }
3494 // in turn, prevent listener from responding
3495 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3496 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3497 prefs->setDouble("/tools/freehand/pencil/tolerance", adj->value);
3498 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3499 }
3501 class PencilToleranceObserver : public Inkscape::Preferences::Observer {
3502 public:
3503 PencilToleranceObserver(Glib::ustring const &path, GObject *x) : Observer(path), _obj(x)
3504 {
3505 g_object_set_data(_obj, "prefobserver", this);
3506 }
3507 virtual ~PencilToleranceObserver() {
3508 if (g_object_get_data(_obj, "prefobserver") == this) {
3509 g_object_set_data(_obj, "prefobserver", NULL);
3510 }
3511 }
3512 virtual void notify(Inkscape::Preferences::Entry const &val) {
3513 GObject* tbl = _obj;
3514 if (g_object_get_data( tbl, "freeze" )) {
3515 return;
3516 }
3517 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3519 GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl, "tolerance");
3521 double v = val.getDouble(adj->value);
3522 gtk_adjustment_set_value(adj, v);
3523 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3524 }
3525 private:
3526 GObject *_obj;
3527 };
3530 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3531 {
3532 sp_add_freehand_mode_toggle(mainActions, holder, true);
3534 EgeAdjustmentAction* eact = 0;
3536 /* Tolerance */
3537 {
3538 gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
3539 gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
3540 eact = create_adjustment_action( "PencilToleranceAction",
3541 _("Smoothing:"), _("Smoothing: "),
3542 _("How much smoothing (simplifying) is applied to the line"),
3543 "/tools/freehand/pencil/tolerance",
3544 3.0,
3545 GTK_WIDGET(desktop->canvas), NULL,
3546 holder, TRUE, "altx-pencil",
3547 1, 100.0, 0.5, 0,
3548 labels, values, G_N_ELEMENTS(labels),
3549 sp_pencil_tb_tolerance_value_changed,
3550 1, 2);
3551 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3552 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3554 PencilToleranceObserver *obs =
3555 new PencilToleranceObserver("/tools/freehand/pencil/tolerance", G_OBJECT(holder));
3556 }
3558 /* advanced shape options */
3559 freehand_add_advanced_shape_options(mainActions, holder, true);
3561 /* Reset */
3562 {
3563 InkAction* inky = ink_action_new( "PencilResetAction",
3564 _("Defaults"),
3565 _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3566 GTK_STOCK_CLEAR,
3567 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3568 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
3569 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3570 }
3572 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3574 }
3577 //########################
3578 //## Tweak ##
3579 //########################
3581 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3582 {
3583 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3584 prefs->setDouble( "/tools/tweak/width", adj->value * 0.01 );
3585 }
3587 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3588 {
3589 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3590 prefs->setDouble( "/tools/tweak/force", adj->value * 0.01 );
3591 }
3593 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3594 {
3595 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3596 prefs->setBool("/tools/tweak/usepressure", gtk_toggle_action_get_active(act));
3597 }
3599 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3600 {
3601 int mode = ege_select_one_action_get_active( act );
3602 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3603 prefs->setInt("/tools/tweak/mode", mode);
3605 GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3606 GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3607 GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3608 GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3609 GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3610 GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3611 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3612 if (doh) gtk_action_set_sensitive (doh, TRUE);
3613 if (dos) gtk_action_set_sensitive (dos, TRUE);
3614 if (dol) gtk_action_set_sensitive (dol, TRUE);
3615 if (doo) gtk_action_set_sensitive (doo, TRUE);
3616 if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3617 if (fid) gtk_action_set_sensitive (fid, FALSE);
3618 } else {
3619 if (doh) gtk_action_set_sensitive (doh, FALSE);
3620 if (dos) gtk_action_set_sensitive (dos, FALSE);
3621 if (dol) gtk_action_set_sensitive (dol, FALSE);
3622 if (doo) gtk_action_set_sensitive (doo, FALSE);
3623 if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3624 if (fid) gtk_action_set_sensitive (fid, TRUE);
3625 }
3626 }
3628 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3629 {
3630 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3631 prefs->setDouble( "/tools/tweak/fidelity", adj->value * 0.01 );
3632 }
3634 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3635 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3636 prefs->setBool("/tools/tweak/doh", gtk_toggle_action_get_active(act));
3637 }
3638 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3639 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3640 prefs->setBool("/tools/tweak/dos", gtk_toggle_action_get_active(act));
3641 }
3642 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3643 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3644 prefs->setBool("/tools/tweak/dol", gtk_toggle_action_get_active(act));
3645 }
3646 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3647 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3648 prefs->setBool("/tools/tweak/doo", gtk_toggle_action_get_active(act));
3649 }
3651 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3652 {
3653 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3654 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3656 {
3657 /* Width */
3658 gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3659 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3660 EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3661 _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3662 "/tools/tweak/width", 15,
3663 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3664 1, 100, 1.0, 0.0,
3665 labels, values, G_N_ELEMENTS(labels),
3666 sp_tweak_width_value_changed, 0.01, 0, 100 );
3667 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3668 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3669 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3670 }
3673 {
3674 /* Force */
3675 gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3676 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3677 EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3678 _("Force"), _("Force:"), _("The force of the tweak action"),
3679 "/tools/tweak/force", 20,
3680 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3681 1, 100, 1.0, 0.0,
3682 labels, values, G_N_ELEMENTS(labels),
3683 sp_tweak_force_value_changed, 0.01, 0, 100 );
3684 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3685 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3686 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3687 }
3689 /* Mode */
3690 {
3691 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3693 GtkTreeIter iter;
3694 gtk_list_store_append( model, &iter );
3695 gtk_list_store_set( model, &iter,
3696 0, _("Move mode"),
3697 1, _("Move objects in any direction"),
3698 2, "tweak_move_mode",
3699 -1 );
3701 gtk_list_store_append( model, &iter );
3702 gtk_list_store_set( model, &iter,
3703 0, _("Move in/out mode"),
3704 1, _("Move objects towards cursor; with Shift from cursor"),
3705 2, "tweak_move_mode_inout",
3706 -1 );
3708 gtk_list_store_append( model, &iter );
3709 gtk_list_store_set( model, &iter,
3710 0, _("Move jitter mode"),
3711 1, _("Move objects in random directions"),
3712 2, "tweak_move_mode_jitter",
3713 -1 );
3715 gtk_list_store_append( model, &iter );
3716 gtk_list_store_set( model, &iter,
3717 0, _("Scale mode"),
3718 1, _("Scale objects, with Shift scale up"),
3719 2, "tweak_scale_mode",
3720 -1 );
3722 gtk_list_store_append( model, &iter );
3723 gtk_list_store_set( model, &iter,
3724 0, _("Rotate mode"),
3725 1, _("Rotate objects, with Shift counterclockwise"),
3726 2, "tweak_rotate_mode",
3727 -1 );
3729 gtk_list_store_append( model, &iter );
3730 gtk_list_store_set( model, &iter,
3731 0, _("Duplicate/delete mode"),
3732 1, _("Duplicate objects, with Shift delete"),
3733 2, "tweak_moreless_mode",
3734 -1 );
3736 gtk_list_store_append( model, &iter );
3737 gtk_list_store_set( model, &iter,
3738 0, _("Push mode"),
3739 1, _("Push parts of paths in any direction"),
3740 2, "tweak_push_mode",
3741 -1 );
3743 gtk_list_store_append( model, &iter );
3744 gtk_list_store_set( model, &iter,
3745 0, _("Shrink/grow mode"),
3746 1, _("Shrink (inset) parts of paths; with Shift grow (outset)"),
3747 2, "tweak_shrink_mode",
3748 -1 );
3750 gtk_list_store_append( model, &iter );
3751 gtk_list_store_set( model, &iter,
3752 0, _("Attract/repel mode"),
3753 1, _("Attract parts of paths towards cursor; with Shift from cursor"),
3754 2, "tweak_attract_mode",
3755 -1 );
3757 gtk_list_store_append( model, &iter );
3758 gtk_list_store_set( model, &iter,
3759 0, _("Roughen mode"),
3760 1, _("Roughen parts of paths"),
3761 2, "tweak_roughen_mode",
3762 -1 );
3764 gtk_list_store_append( model, &iter );
3765 gtk_list_store_set( model, &iter,
3766 0, _("Color paint mode"),
3767 1, _("Paint the tool's color upon selected objects"),
3768 2, "tweak_colorpaint_mode",
3769 -1 );
3771 gtk_list_store_append( model, &iter );
3772 gtk_list_store_set( model, &iter,
3773 0, _("Color jitter mode"),
3774 1, _("Jitter the colors of selected objects"),
3775 2, "tweak_colorjitter_mode",
3776 -1 );
3778 gtk_list_store_append( model, &iter );
3779 gtk_list_store_set( model, &iter,
3780 0, _("Blur mode"),
3781 1, _("Blur selected objects more; with Shift, blur less"),
3782 2, "tweak_blur_mode",
3783 -1 );
3786 EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3787 g_object_set( act, "short_label", _("Mode:"), NULL );
3788 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3789 g_object_set_data( holder, "mode_action", act );
3791 ege_select_one_action_set_appearance( act, "full" );
3792 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3793 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3794 ege_select_one_action_set_icon_column( act, 2 );
3795 ege_select_one_action_set_icon_size( act, secondarySize );
3796 ege_select_one_action_set_tooltip_column( act, 1 );
3798 gint mode = prefs->getInt("/tools/tweak/mode", 0);
3799 ege_select_one_action_set_active( act, mode );
3800 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3802 g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3803 }
3805 guint mode = prefs->getInt("/tools/tweak/mode", 0);
3807 {
3808 EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3809 ege_output_action_set_use_markup( act, TRUE );
3810 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3811 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3812 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3813 g_object_set_data( holder, "tweak_channels_label", act);
3814 }
3816 {
3817 InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3818 _("Hue"),
3819 _("In color mode, act on objects' hue"),
3820 NULL,
3821 Inkscape::ICON_SIZE_DECORATION );
3822 //TRANSLATORS: "H" here stands for hue
3823 g_object_set( act, "short_label", _("H"), NULL );
3824 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3825 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3826 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doh", true) );
3827 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3828 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3829 g_object_set_data( holder, "tweak_doh", act);
3830 }
3831 {
3832 InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3833 _("Saturation"),
3834 _("In color mode, act on objects' saturation"),
3835 NULL,
3836 Inkscape::ICON_SIZE_DECORATION );
3837 //TRANSLATORS: "S" here stands for Saturation
3838 g_object_set( act, "short_label", _("S"), NULL );
3839 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3840 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3841 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dos", true) );
3842 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3843 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3844 g_object_set_data( holder, "tweak_dos", act );
3845 }
3846 {
3847 InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3848 _("Lightness"),
3849 _("In color mode, act on objects' lightness"),
3850 NULL,
3851 Inkscape::ICON_SIZE_DECORATION );
3852 //TRANSLATORS: "L" here stands for Lightness
3853 g_object_set( act, "short_label", _("L"), NULL );
3854 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3855 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3856 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dol", true) );
3857 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3858 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3859 g_object_set_data( holder, "tweak_dol", act );
3860 }
3861 {
3862 InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3863 _("Opacity"),
3864 _("In color mode, act on objects' opacity"),
3865 NULL,
3866 Inkscape::ICON_SIZE_DECORATION );
3867 //TRANSLATORS: "O" here stands for Opacity
3868 g_object_set( act, "short_label", _("O"), NULL );
3869 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3870 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3871 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doo", true) );
3872 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3873 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3874 g_object_set_data( holder, "tweak_doo", act );
3875 }
3877 { /* Fidelity */
3878 gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3879 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3880 EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3881 _("Fidelity"), _("Fidelity:"),
3882 _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3883 "/tools/tweak/fidelity", 50,
3884 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3885 1, 100, 1.0, 10.0,
3886 labels, values, G_N_ELEMENTS(labels),
3887 sp_tweak_fidelity_value_changed, 0.01, 0, 100 );
3888 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3889 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3890 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3891 gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3892 g_object_set_data( holder, "tweak_fidelity", eact );
3893 }
3896 /* Use Pressure button */
3897 {
3898 InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3899 _("Pressure"),
3900 _("Use the pressure of the input device to alter the force of tweak action"),
3901 "use_pressure",
3902 Inkscape::ICON_SIZE_DECORATION );
3903 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3904 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3905 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/usepressure", true) );
3906 }
3908 }
3911 //########################
3912 //## Calligraphy ##
3913 //########################
3914 static void update_presets_list (GObject *tbl)
3915 {
3916 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3917 if (g_object_get_data(tbl, "presets_blocked"))
3918 return;
3920 EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3921 if (!sel) {
3922 // WTF!? This will cause a segfault if ever reached
3923 //ege_select_one_action_set_active(sel, 0);
3924 return;
3925 }
3927 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
3929 int ege_index = 1;
3930 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++ege_index) {
3931 bool match = true;
3933 std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(*i);
3934 for (std::vector<Inkscape::Preferences::Entry>::iterator j = preset.begin(); j != preset.end(); ++j) {
3935 Glib::ustring entry_name = j->getEntryName();
3936 if (entry_name == "id" || entry_name == "name") continue;
3938 void *widget = g_object_get_data(tbl, entry_name.data());
3939 if (widget) {
3940 if (GTK_IS_ADJUSTMENT(widget)) {
3941 double v = j->getDouble();
3942 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
3943 //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
3944 if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
3945 match = false;
3946 break;
3947 }
3948 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
3949 bool v = j->getBool();
3950 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
3951 //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
3952 if ( static_cast<bool>(gtk_toggle_action_get_active(toggle)) != v ) {
3953 match = false;
3954 break;
3955 }
3956 }
3957 }
3958 }
3960 if (match) {
3961 // newly added item is at the same index as the
3962 // save command, so we need to change twice for it to take effect
3963 ege_select_one_action_set_active(sel, 0);
3964 ege_select_one_action_set_active(sel, ege_index); // one-based index
3965 return;
3966 }
3967 }
3969 // no match found
3970 ege_select_one_action_set_active(sel, 0);
3971 }
3973 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3974 {
3975 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3976 prefs->setDouble( "/tools/calligraphic/mass", adj->value * 0.01 );
3977 update_presets_list(tbl);
3978 }
3980 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3981 {
3982 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3983 prefs->setDouble( "/tools/calligraphic/wiggle", adj->value * 0.01 );
3984 update_presets_list(tbl);
3985 }
3987 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3988 {
3989 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3990 prefs->setDouble( "/tools/calligraphic/angle", adj->value );
3991 update_presets_list(tbl);
3992 }
3994 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3995 {
3996 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3997 prefs->setDouble( "/tools/calligraphic/width", adj->value * 0.01 );
3998 update_presets_list(tbl);
3999 }
4001 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
4002 {
4003 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4004 prefs->setDouble("/tools/calligraphic/thinning", adj->value * 0.01 );
4005 update_presets_list(tbl);
4006 }
4008 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
4009 {
4010 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4011 prefs->setDouble( "/tools/calligraphic/flatness", adj->value * 0.01);
4012 update_presets_list(tbl);
4013 }
4015 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
4016 {
4017 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4018 prefs->setDouble( "/tools/calligraphic/tremor", adj->value * 0.01 );
4019 update_presets_list(tbl);
4020 }
4022 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
4023 {
4024 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4025 prefs->setDouble( "/tools/calligraphic/cap_rounding", adj->value );
4026 update_presets_list(tbl);
4027 }
4029 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject* tbl )
4030 {
4031 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4032 prefs->setBool("/tools/calligraphic/usepressure", gtk_toggle_action_get_active( act ));
4033 update_presets_list(tbl);
4034 }
4036 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject* tbl )
4037 {
4038 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4039 prefs->setBool("/tools/calligraphic/tracebackground", gtk_toggle_action_get_active( act ));
4040 update_presets_list(tbl);
4041 }
4043 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject* tbl )
4044 {
4045 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4046 GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
4047 prefs->setBool("/tools/calligraphic/usetilt", gtk_toggle_action_get_active( act ));
4048 update_presets_list(tbl);
4049 if (calligraphy_angle )
4050 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
4051 }
4054 static gchar const *const widget_names[] = {
4055 "width",
4056 "mass",
4057 "wiggle",
4058 "angle",
4059 "thinning",
4060 "tremor",
4061 "flatness",
4062 "cap_rounding",
4063 "usepressure",
4064 "tracebackground",
4065 "usetilt"
4066 };
4069 static void sp_dcc_build_presets_list(GObject *tbl)
4070 {
4071 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4073 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4074 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4075 gtk_list_store_clear (model);
4077 {
4078 GtkTreeIter iter;
4079 gtk_list_store_append( model, &iter );
4080 gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4081 }
4083 // iterate over all presets to populate the list
4084 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4085 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4086 int ii=1;
4088 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i) {
4089 GtkTreeIter iter;
4090 Glib::ustring preset_name = prefs->getString(*i + "/name");
4091 gtk_list_store_append( model, &iter );
4092 gtk_list_store_set( model, &iter, 0, preset_name.data(), 1, ii++, -1 );
4093 }
4095 {
4096 GtkTreeIter iter;
4097 gtk_list_store_append( model, &iter );
4098 gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4099 g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4100 }
4102 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4104 update_presets_list (tbl);
4105 }
4107 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4108 {
4109 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4110 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4111 if (! desktop) return;
4113 if (g_object_get_data(tbl, "presets_blocked"))
4114 return;
4116 Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
4117 if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) {
4118 // dialog cancelled
4119 update_presets_list (tbl);
4120 return;
4121 }
4122 Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
4124 if (profile_name.empty()) {
4125 // empty name entered
4126 update_presets_list (tbl);
4127 return;
4128 }
4130 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4132 // If there's a preset with the given name, find it and set save_path appropriately
4133 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4134 int total_presets = presets.size();
4135 int new_index = -1;
4136 Glib::ustring save_path; // profile pref path without a trailing slash
4138 int temp_index = 0;
4139 for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++temp_index) {
4140 Glib::ustring name = prefs->getString(*i + "/name");
4141 if (!name.empty() && profile_name == name) {
4142 new_index = temp_index;
4143 save_path = *i;
4144 break;
4145 }
4146 }
4148 if (new_index == -1) {
4149 // no preset with this name, create
4150 new_index = total_presets + 1;
4151 gchar *profile_id = g_strdup_printf("/dcc%d", new_index);
4152 save_path = Glib::ustring("/tools/calligraphic/preset") + profile_id;
4153 g_free(profile_id);
4154 }
4156 for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4157 gchar const *const widget_name = widget_names[i];
4158 void *widget = g_object_get_data(tbl, widget_name);
4159 if (widget) {
4160 if (GTK_IS_ADJUSTMENT(widget)) {
4161 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4162 prefs->setDouble(save_path + "/" + widget_name, gtk_adjustment_get_value(adj));
4163 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4164 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4165 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4166 prefs->setBool(save_path + "/" + widget_name, gtk_toggle_action_get_active(toggle));
4167 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4168 } else {
4169 g_warning("Unknown widget type for preset: %s\n", widget_name);
4170 }
4171 } else {
4172 g_warning("Bad key when writing preset: %s\n", widget_name);
4173 }
4174 }
4175 prefs->setString(save_path + "/name", profile_name);
4177 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4178 sp_dcc_build_presets_list (tbl);
4179 }
4182 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4184 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4186 gint preset_index = ege_select_one_action_get_active( act );
4187 // This is necessary because EgeSelectOneAction spams us with GObject "changed" signal calls
4188 // even when the preset is not changed. It would be good to replace it with something more
4189 // modern. Index 0 means "No preset", so we don't do anything.
4190 if (preset_index == 0) return;
4192 gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4194 if (preset_index == save_presets_index) {
4195 // this is the Save command
4196 sp_dcc_save_profile(NULL, tbl);
4197 return;
4198 }
4200 if (g_object_get_data(tbl, "presets_blocked"))
4201 return;
4203 // preset_index is one-based so we subtract 1
4204 std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4205 Glib::ustring preset_path = presets.at(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 std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(preset_path);
4212 // Shouldn't this be std::map?
4213 for (std::vector<Inkscape::Preferences::Entry>::iterator i = preset.begin(); i != preset.end(); ++i) {
4214 Glib::ustring entry_name = i->getEntryName();
4215 if (entry_name == "id" || entry_name == "name") continue;
4216 void *widget = g_object_get_data(tbl, entry_name.data());
4217 if (widget) {
4218 if (GTK_IS_ADJUSTMENT(widget)) {
4219 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4220 gtk_adjustment_set_value(adj, i->getDouble());
4221 //std::cout << "set adj " << attr_name << " to " << v << "\n";
4222 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4223 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4224 gtk_toggle_action_set_active(toggle, i->getBool());
4225 //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4226 } else {
4227 g_warning("Unknown widget type for preset: %s\n", entry_name.data());
4228 }
4229 } else {
4230 g_warning("Bad key found in a preset record: %s\n", entry_name.data());
4231 }
4232 }
4233 g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4234 }
4235 }
4238 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4239 {
4240 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4241 {
4242 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4244 EgeAdjustmentAction* calligraphy_angle = 0;
4246 {
4247 /* Width */
4248 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4249 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4250 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4251 _("Pen Width"), _("Width:"),
4252 _("The width of the calligraphic pen (relative to the visible canvas area)"),
4253 "/tools/calligraphic/width", 15,
4254 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4255 1, 100, 1.0, 0.0,
4256 labels, values, G_N_ELEMENTS(labels),
4257 sp_ddc_width_value_changed, 0.01, 0, 100 );
4258 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4259 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4260 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4261 }
4263 {
4264 /* Thinning */
4265 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4266 gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4267 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4268 _("Stroke Thinning"), _("Thinning:"),
4269 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4270 "/tools/calligraphic/thinning", 10,
4271 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4272 -100, 100, 1, 0.1,
4273 labels, values, G_N_ELEMENTS(labels),
4274 sp_ddc_velthin_value_changed, 0.01, 0, 100);
4275 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4276 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4277 }
4279 {
4280 /* Angle */
4281 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4282 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4283 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4284 _("Pen Angle"), _("Angle:"),
4285 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4286 "/tools/calligraphic/angle", 30,
4287 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4288 -90.0, 90.0, 1.0, 10.0,
4289 labels, values, G_N_ELEMENTS(labels),
4290 sp_ddc_angle_value_changed, 1, 0 );
4291 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4292 g_object_set_data( holder, "angle_action", eact );
4293 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4294 calligraphy_angle = eact;
4295 }
4297 {
4298 /* Fixation */
4299 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4300 gdouble values[] = {0, 20, 40, 60, 90, 100};
4301 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4302 _("Fixation"), _("Fixation:"),
4303 _("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"),
4304 "/tools/calligraphic/flatness", 90,
4305 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4306 0.0, 100, 1.0, 10.0,
4307 labels, values, G_N_ELEMENTS(labels),
4308 sp_ddc_flatness_value_changed, 0.01, 0, 100 );
4309 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4310 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4311 }
4313 {
4314 /* Cap Rounding */
4315 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4316 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4317 // TRANSLATORS: "cap" means "end" (both start and finish) here
4318 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4319 _("Cap rounding"), _("Caps:"),
4320 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4321 "/tools/calligraphic/cap_rounding", 0.0,
4322 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4323 0.0, 5.0, 0.01, 0.1,
4324 labels, values, G_N_ELEMENTS(labels),
4325 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4326 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4327 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4328 }
4330 {
4331 /* Tremor */
4332 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4333 gdouble values[] = {0, 10, 20, 40, 60, 100};
4334 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4335 _("Stroke Tremor"), _("Tremor:"),
4336 _("Increase to make strokes rugged and trembling"),
4337 "/tools/calligraphic/tremor", 0.0,
4338 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4339 0.0, 100, 1, 0.0,
4340 labels, values, G_N_ELEMENTS(labels),
4341 sp_ddc_tremor_value_changed, 0.01, 0, 100 );
4343 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4344 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4345 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4346 }
4348 {
4349 /* Wiggle */
4350 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4351 gdouble values[] = {0, 20, 40, 60, 100};
4352 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4353 _("Pen Wiggle"), _("Wiggle:"),
4354 _("Increase to make the pen waver and wiggle"),
4355 "/tools/calligraphic/wiggle", 0.0,
4356 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4357 0.0, 100, 1, 0.0,
4358 labels, values, G_N_ELEMENTS(labels),
4359 sp_ddc_wiggle_value_changed, 0.01, 0, 100 );
4360 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4361 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4362 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4363 }
4365 {
4366 /* Mass */
4367 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4368 gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4369 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4370 _("Pen Mass"), _("Mass:"),
4371 _("Increase to make the pen drag behind, as if slowed by inertia"),
4372 "/tools/calligraphic/mass", 2.0,
4373 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4374 0.0, 100, 1, 0.0,
4375 labels, values, G_N_ELEMENTS(labels),
4376 sp_ddc_mass_value_changed, 0.01, 0, 100 );
4377 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4378 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4379 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4380 }
4383 /* Trace Background button */
4384 {
4385 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4386 _("Trace Background"),
4387 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4388 "trace_background",
4389 Inkscape::ICON_SIZE_DECORATION );
4390 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4391 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4392 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/tracebackground", false) );
4393 g_object_set_data( holder, "tracebackground", act );
4394 }
4396 /* Use Pressure button */
4397 {
4398 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4399 _("Pressure"),
4400 _("Use the pressure of the input device to alter the width of the pen"),
4401 "use_pressure",
4402 Inkscape::ICON_SIZE_DECORATION );
4403 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4404 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4405 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usepressure", true) );
4406 g_object_set_data( holder, "usepressure", act );
4407 }
4409 /* Use Tilt button */
4410 {
4411 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4412 _("Tilt"),
4413 _("Use the tilt of the input device to alter the angle of the pen's nib"),
4414 "use_tilt",
4415 Inkscape::ICON_SIZE_DECORATION );
4416 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4417 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4418 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs->getBool("/tools/calligraphic/usetilt", true) );
4419 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usetilt", true) );
4420 g_object_set_data( holder, "usetilt", act );
4421 }
4423 /*calligraphic profile */
4424 {
4425 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
4426 EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
4427 ege_select_one_action_set_appearance (act1, "compact");
4428 g_object_set_data (holder, "profile_selector", act1 );
4430 g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
4432 sp_dcc_build_presets_list (holder);
4434 g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
4435 gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
4436 }
4437 }
4438 }
4441 //########################
4442 //## Circle / Arc ##
4443 //########################
4445 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4446 {
4447 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4448 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4450 if (v1 == 0 && v2 == 0) {
4451 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4452 gtk_action_set_sensitive( ocb, FALSE );
4453 gtk_action_set_sensitive( make_whole, FALSE );
4454 }
4455 } else {
4456 gtk_action_set_sensitive( ocb, TRUE );
4457 gtk_action_set_sensitive( make_whole, TRUE );
4458 }
4459 }
4461 static void
4462 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4463 {
4464 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4466 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4467 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4468 prefs->setDouble(Glib::ustring("/tools/shapes/arc") + value_name, (adj->value * M_PI)/ 180);
4469 }
4471 // quit if run by the attr_changed listener
4472 if (g_object_get_data( tbl, "freeze" )) {
4473 return;
4474 }
4476 // in turn, prevent listener from responding
4477 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4479 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4481 bool modmade = false;
4482 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4483 items != NULL;
4484 items = items->next)
4485 {
4486 SPItem *item = SP_ITEM(items->data);
4488 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4490 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4491 SPArc *arc = SP_ARC(item);
4493 if (!strcmp(value_name, "start"))
4494 ge->start = (adj->value * M_PI)/ 180;
4495 else
4496 ge->end = (adj->value * M_PI)/ 180;
4498 sp_genericellipse_normalize(ge);
4499 ((SPObject *)arc)->updateRepr();
4500 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4502 modmade = true;
4503 }
4504 }
4506 g_free(namespaced_name);
4508 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4510 sp_arctb_sensitivize( tbl, adj->value, other->value );
4512 if (modmade) {
4513 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4514 _("Arc: Change start/end"));
4515 }
4517 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4518 }
4521 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
4522 {
4523 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
4524 }
4526 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4527 {
4528 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
4529 }
4532 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4533 {
4534 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4535 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4536 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4537 prefs->setBool("/tools/shapes/arc/open", ege_select_one_action_get_active(act) != 0);
4538 }
4540 // quit if run by the attr_changed listener
4541 if (g_object_get_data( tbl, "freeze" )) {
4542 return;
4543 }
4545 // in turn, prevent listener from responding
4546 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4548 bool modmade = false;
4550 if ( ege_select_one_action_get_active(act) != 0 ) {
4551 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4552 items != NULL;
4553 items = items->next)
4554 {
4555 if (SP_IS_ARC((SPItem *) items->data)) {
4556 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4557 repr->setAttribute("sodipodi:open", "true");
4558 SP_OBJECT((SPItem *) items->data)->updateRepr();
4559 modmade = true;
4560 }
4561 }
4562 } else {
4563 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4564 items != NULL;
4565 items = items->next)
4566 {
4567 if (SP_IS_ARC((SPItem *) items->data)) {
4568 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4569 repr->setAttribute("sodipodi:open", NULL);
4570 SP_OBJECT((SPItem *) items->data)->updateRepr();
4571 modmade = true;
4572 }
4573 }
4574 }
4576 if (modmade) {
4577 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4578 _("Arc: Change open/closed"));
4579 }
4581 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4582 }
4584 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4585 {
4586 GtkAdjustment *adj;
4587 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
4588 gtk_adjustment_set_value(adj, 0.0);
4589 gtk_adjustment_value_changed(adj);
4591 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
4592 gtk_adjustment_set_value(adj, 0.0);
4593 gtk_adjustment_value_changed(adj);
4595 spinbutton_defocus( GTK_OBJECT(obj) );
4596 }
4598 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
4599 gchar const */*old_value*/, gchar const */*new_value*/,
4600 bool /*is_interactive*/, gpointer data)
4601 {
4602 GObject *tbl = G_OBJECT(data);
4604 // quit if run by the _changed callbacks
4605 if (g_object_get_data( tbl, "freeze" )) {
4606 return;
4607 }
4609 // in turn, prevent callbacks from responding
4610 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4612 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4613 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4615 GtkAdjustment *adj1,*adj2;
4616 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4617 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4618 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4619 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4621 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4623 char const *openstr = NULL;
4624 openstr = repr->attribute("sodipodi:open");
4625 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4627 if (openstr) {
4628 ege_select_one_action_set_active( ocb, 1 );
4629 } else {
4630 ege_select_one_action_set_active( ocb, 0 );
4631 }
4633 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4634 }
4636 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4637 NULL, /* child_added */
4638 NULL, /* child_removed */
4639 arc_tb_event_attr_changed,
4640 NULL, /* content_changed */
4641 NULL /* order_changed */
4642 };
4645 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4646 {
4647 int n_selected = 0;
4648 Inkscape::XML::Node *repr = NULL;
4650 purge_repr_listener( tbl, tbl );
4652 for (GSList const *items = selection->itemList();
4653 items != NULL;
4654 items = items->next)
4655 {
4656 if (SP_IS_ARC((SPItem *) items->data)) {
4657 n_selected++;
4658 repr = SP_OBJECT_REPR((SPItem *) items->data);
4659 }
4660 }
4662 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4664 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4665 if (n_selected == 0) {
4666 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4667 } else if (n_selected == 1) {
4668 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4669 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4671 if (repr) {
4672 g_object_set_data( tbl, "repr", repr );
4673 Inkscape::GC::anchor(repr);
4674 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4675 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4676 }
4677 } else {
4678 // FIXME: implement averaging of all parameters for multiple selected
4679 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4680 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4681 sp_arctb_sensitivize( tbl, 1, 0 );
4682 }
4683 }
4686 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4687 {
4688 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4690 EgeAdjustmentAction* eact = 0;
4691 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
4694 {
4695 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4696 ege_output_action_set_use_markup( act, TRUE );
4697 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4698 g_object_set_data( holder, "mode_action", act );
4699 }
4701 /* Start */
4702 {
4703 eact = create_adjustment_action( "ArcStartAction",
4704 _("Start"), _("Start:"),
4705 _("The angle (in degrees) from the horizontal to the arc's start point"),
4706 "/tools/shapes/arc/start", 0.0,
4707 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4708 -360.0, 360.0, 1.0, 10.0,
4709 0, 0, 0,
4710 sp_arctb_start_value_changed);
4711 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4712 }
4714 /* End */
4715 {
4716 eact = create_adjustment_action( "ArcEndAction",
4717 _("End"), _("End:"),
4718 _("The angle (in degrees) from the horizontal to the arc's end point"),
4719 "/tools/shapes/arc/end", 0.0,
4720 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4721 -360.0, 360.0, 1.0, 10.0,
4722 0, 0, 0,
4723 sp_arctb_end_value_changed);
4724 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4725 }
4727 /* Segments / Pie checkbox */
4728 {
4729 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4731 GtkTreeIter iter;
4732 gtk_list_store_append( model, &iter );
4733 gtk_list_store_set( model, &iter,
4734 0, _("Closed arc"),
4735 1, _("Switch to segment (closed shape with two radii)"),
4736 2, "circle_closed_arc",
4737 -1 );
4739 gtk_list_store_append( model, &iter );
4740 gtk_list_store_set( model, &iter,
4741 0, _("Open Arc"),
4742 1, _("Switch to arc (unclosed shape)"),
4743 2, "circle_open_arc",
4744 -1 );
4746 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4747 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4748 g_object_set_data( holder, "open_action", act );
4750 ege_select_one_action_set_appearance( act, "full" );
4751 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4752 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4753 ege_select_one_action_set_icon_column( act, 2 );
4754 ege_select_one_action_set_icon_size( act, secondarySize );
4755 ege_select_one_action_set_tooltip_column( act, 1 );
4757 bool isClosed = !prefs->getBool("/tools/shapes/arc/open", false);
4758 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4759 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4760 }
4762 /* Make Whole */
4763 {
4764 InkAction* inky = ink_action_new( "ArcResetAction",
4765 _("Make whole"),
4766 _("Make the shape a whole ellipse, not arc or segment"),
4767 "reset_circle",
4768 secondarySize );
4769 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4770 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4771 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4772 g_object_set_data( holder, "make_whole", inky );
4773 }
4775 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4776 // sensitivize make whole and open checkbox
4777 {
4778 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4779 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4780 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4781 }
4784 sigc::connection *connection = new sigc::connection(
4785 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4786 );
4787 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4788 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4789 }
4794 // toggle button callbacks and updaters
4796 //########################
4797 //## Dropper ##
4798 //########################
4800 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4801 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4802 prefs->setInt( "/tools/dropper/pick", gtk_toggle_action_get_active( act ) );
4803 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4804 if ( set_action ) {
4805 if ( gtk_toggle_action_get_active( act ) ) {
4806 gtk_action_set_sensitive( set_action, TRUE );
4807 } else {
4808 gtk_action_set_sensitive( set_action, FALSE );
4809 }
4810 }
4812 spinbutton_defocus(GTK_OBJECT(tbl));
4813 }
4815 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4816 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4817 prefs->setBool( "/tools/dropper/setalpha", gtk_toggle_action_get_active( act ) );
4818 spinbutton_defocus(GTK_OBJECT(tbl));
4819 }
4822 /**
4823 * Dropper auxiliary toolbar construction and setup.
4824 *
4825 * TODO: Would like to add swatch of current color.
4826 * TODO: Add queue of last 5 or so colors selected with new swatches so that
4827 * can drag and drop places. Will provide a nice mixing palette.
4828 */
4829 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4830 {
4831 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4832 gint pickAlpha = prefs->getInt( "/tools/dropper/pick", 1 );
4834 {
4835 EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4836 ege_output_action_set_use_markup( act, TRUE );
4837 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4838 }
4840 {
4841 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4842 _("Pick opacity"),
4843 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4844 NULL,
4845 Inkscape::ICON_SIZE_DECORATION );
4846 g_object_set( act, "short_label", _("Pick"), NULL );
4847 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4848 g_object_set_data( holder, "pick_action", act );
4849 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4850 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4851 }
4853 {
4854 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4855 _("Assign opacity"),
4856 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4857 NULL,
4858 Inkscape::ICON_SIZE_DECORATION );
4859 g_object_set( act, "short_label", _("Assign"), NULL );
4860 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4861 g_object_set_data( holder, "set_action", act );
4862 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/dropper/setalpha", true) );
4863 // make sure it's disabled if we're not picking alpha
4864 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4865 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4866 }
4867 }
4870 //########################
4871 //## LPETool ##
4872 //########################
4874 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
4876 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
4877 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
4878 {
4879 using namespace Inkscape::LivePathEffect;
4881 SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
4882 SPEventContext *ec = desktop->event_context;
4883 if (!SP_IS_LPETOOL_CONTEXT(ec)) {
4884 return;
4885 }
4887 // only take action if run by the attr_changed listener
4888 if (!g_object_get_data(tbl, "freeze")) {
4889 // in turn, prevent listener from responding
4890 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
4892 gint mode = ege_select_one_action_get_active(act);
4893 EffectType type = lpesubtools[mode];
4895 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4896 bool success = lpetool_try_construction(lc, type);
4897 if (success) {
4898 // since the construction was already performed, we set the state back to inactive
4899 ege_select_one_action_set_active(act, 0);
4900 mode = 0;
4901 } else {
4902 // switch to the chosen subtool
4903 SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
4904 }
4906 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4907 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4908 prefs->setInt( "/tools/lpetool/mode", mode );
4909 }
4911 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
4912 }
4913 }
4915 void sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject */*tbl*/)
4916 {
4917 SPEventContext *ec = selection->desktop()->event_context;
4918 if (!SP_IS_LPETOOL_CONTEXT(ec))
4919 return;
4921 lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
4922 }
4924 void
4925 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
4926 {
4927 using namespace Inkscape::LivePathEffect;
4928 SPEventContext *ec = selection->desktop()->event_context;
4929 if (!SP_IS_LPETOOL_CONTEXT(ec))
4930 return;
4931 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
4933 lpetool_delete_measuring_items(lc);
4934 lpetool_create_measuring_items(lc, selection);
4936 // activate line segment combo box if a single item with LPELineSegment is selected
4937 GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
4938 SPItem *item = selection->singleItem();
4939 if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
4940 SPLPEItem *lpeitem = SP_LPE_ITEM(item);
4941 Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
4942 if (lpe && lpe->effectType() == LINE_SEGMENT) {
4943 LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
4944 g_object_set_data(tbl, "currentlpe", lpe);
4945 g_object_set_data(tbl, "currentlpeitem", lpeitem);
4946 gtk_action_set_sensitive(w, TRUE);
4947 ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
4948 } else {
4949 g_object_set_data(tbl, "currentlpe", NULL);
4950 g_object_set_data(tbl, "currentlpeitem", NULL);
4951 gtk_action_set_sensitive(w, FALSE);
4952 }
4953 } else {
4954 g_object_set_data(tbl, "currentlpe", NULL);
4955 g_object_set_data(tbl, "currentlpeitem", NULL);
4956 gtk_action_set_sensitive(w, FALSE);
4957 }
4958 }
4960 static void
4961 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
4962 SPDesktop *desktop = static_cast<SPDesktop *>(data);
4963 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4965 bool show = gtk_toggle_action_get_active( act );
4966 prefs->setBool("/tools/lpetool/show_bbox", show);
4968 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
4969 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4970 lpetool_context_reset_limiting_bbox(lc);
4971 }
4972 }
4974 static void
4975 lpetool_toggle_show_measuring_info (GtkToggleAction *act, GObject *tbl) {
4976 SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
4977 if (!tools_isactive(desktop, TOOLS_LPETOOL))
4978 return;
4980 GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
4981 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4982 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
4983 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4984 bool show = gtk_toggle_action_get_active( act );
4985 prefs->setBool("/tools/lpetool/show_measuring_info", show);
4986 lpetool_show_measuring_info(lc, show);
4987 gtk_action_set_sensitive(GTK_ACTION(unitact), show);
4988 }
4989 }
4991 static void lpetool_unit_changed(GtkAction* /*act*/, GObject* tbl) {
4992 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
4993 SPUnit const *unit = tracker->getActiveUnit();
4994 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4995 prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
4997 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4998 if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
4999 SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5000 lpetool_delete_measuring_items(lc);
5001 lpetool_create_measuring_items(lc);
5002 }
5003 }
5005 static void
5006 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
5007 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5008 Inkscape::Selection *selection = desktop->selection;
5010 boost::optional<Geom::Rect> bbox = selection->bounds();
5012 if (bbox) {
5013 Geom::Point A(bbox->min());
5014 Geom::Point B(bbox->max());
5016 A *= desktop->doc2dt();
5017 B *= desktop->doc2dt();
5019 // TODO: should we provide a way to store points in prefs?
5020 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5021 prefs->setDouble("/tools/lpetool/bbox_upperleftx", A[Geom::X]);
5022 prefs->setDouble("/tools/lpetool/bbox_upperlefty", A[Geom::Y]);
5023 prefs->setDouble("/tools/lpetool/bbox_lowerrightx", B[Geom::X]);
5024 prefs->setDouble("/tools/lpetool/bbox_lowerrighty", B[Geom::Y]);
5026 lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
5027 }
5029 gtk_toggle_action_set_active(act, false);
5030 }
5032 static void
5033 sp_line_segment_build_list(GObject *tbl)
5034 {
5035 g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
5037 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
5038 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
5039 gtk_list_store_clear (model);
5041 // TODO: we add the entries of rht combo box manually; later this should be done automatically
5042 {
5043 GtkTreeIter iter;
5044 gtk_list_store_append( model, &iter );
5045 gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
5046 gtk_list_store_append( model, &iter );
5047 gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
5048 gtk_list_store_append( model, &iter );
5049 gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
5050 gtk_list_store_append( model, &iter );
5051 gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
5052 }
5054 g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5055 }
5057 static void
5058 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
5059 using namespace Inkscape::LivePathEffect;
5061 // quit if run by the attr_changed listener
5062 if (g_object_get_data(tbl, "freeze")) {
5063 return;
5064 }
5066 // in turn, prevent listener from responding
5067 g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5069 LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
5070 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5071 if (lpeitem) {
5072 SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5073 lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
5074 sp_lpe_item_update_patheffect(lpeitem, true, true);
5075 }
5077 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5078 }
5080 static void
5081 lpetool_open_lpe_dialog (GtkToggleAction *act, gpointer data) {
5082 SPDesktop *desktop = static_cast<SPDesktop *>(data);
5084 if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5085 sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5086 }
5087 gtk_toggle_action_set_active(act, false);
5088 }
5090 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5091 {
5092 UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5093 tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5094 g_object_set_data(holder, "tracker", tracker);
5095 SPUnit const *unit = tracker->getActiveUnit();
5097 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5098 prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5100 /** Automatically create a list of LPEs that get added to the toolbar **/
5101 {
5102 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5104 GtkTreeIter iter;
5106 // the first toggle button represents the state that no subtool is active (remove this when
5107 // this can be modeled by EgeSelectOneAction or some other action)
5108 gtk_list_store_append( model, &iter );
5109 gtk_list_store_set( model, &iter,
5110 0, _("All inactive"),
5111 1, _("No geometric tool is active"),
5112 2, _("all_inactive"),
5113 -1 );
5115 Inkscape::LivePathEffect::EffectType type;
5116 for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5117 type = lpesubtools[i];
5118 gtk_list_store_append( model, &iter );
5119 gtk_list_store_set( model, &iter,
5120 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5121 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5122 2, Inkscape::LivePathEffect::LPETypeConverter.get_key(type).c_str(),
5123 -1 );
5124 }
5126 EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5127 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5128 g_object_set_data( holder, "lpetool_mode_action", act );
5130 ege_select_one_action_set_appearance( act, "full" );
5131 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5132 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5133 ege_select_one_action_set_icon_column( act, 2 );
5134 ege_select_one_action_set_tooltip_column( act, 1 );
5136 gint lpeToolMode = prefs->getInt("/tools/lpetool/mode", 0);
5137 ege_select_one_action_set_active( act, lpeToolMode );
5138 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5139 }
5141 /* Show limiting bounding box */
5142 {
5143 InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5144 _("Show limiting bounding box"),
5145 _("Show bounding box (used to cut infinite lines)"),
5146 "lpetool_show_bbox",
5147 Inkscape::ICON_SIZE_DECORATION );
5148 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5149 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5150 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_bbox", true ) );
5151 }
5153 /* Set limiting bounding box to bbox of current selection */
5154 {
5155 InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5156 _("Get limiting bounding box from selection"),
5157 _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5158 "lpetool_set_bbox",
5159 Inkscape::ICON_SIZE_DECORATION );
5160 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5161 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5162 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5163 }
5166 /* Combo box to choose line segment type */
5167 {
5168 GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5169 EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5170 ege_select_one_action_set_appearance (act, "compact");
5171 g_object_set_data (holder, "lpetool_line_segment_action", act );
5173 g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5175 sp_line_segment_build_list (holder);
5177 g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5178 gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5179 gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5180 }
5182 /* Display measuring info for selected items */
5183 {
5184 InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5185 _("Display measuring info"),
5186 _("Display measuring info for selected items"),
5187 "lpetool_measuring_info",
5188 Inkscape::ICON_SIZE_DECORATION );
5189 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5190 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
5191 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_measuring_info", true ) );
5192 }
5194 // add the units menu
5195 {
5196 GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5197 gtk_action_group_add_action( mainActions, act );
5198 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5199 g_object_set_data(holder, "lpetool_units_action", act);
5200 gtk_action_set_sensitive(act, prefs->getBool("/tools/lpetool/show_measuring_info", true));
5201 }
5203 /* Open LPE dialog (to adapt parameters numerically) */
5204 {
5205 InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
5206 _("Open LPE dialog"),
5207 _("Open LPE dialog (to adapt parameters numerically)"),
5208 "lpetool_open_lpe_dialog",
5209 Inkscape::ICON_SIZE_DECORATION );
5210 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5211 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
5212 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5213 }
5215 //watch selection
5216 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5218 sigc::connection *c_selection_modified =
5219 new sigc::connection (sp_desktop_selection (desktop)->connectModified
5220 (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
5221 pool->add_connection ("selection-modified", c_selection_modified);
5223 sigc::connection *c_selection_changed =
5224 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5225 (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5226 pool->add_connection ("selection-changed", c_selection_changed);
5227 }
5229 //########################
5230 //## Eraser ##
5231 //########################
5233 static void sp_erc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
5234 {
5235 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5236 prefs->setDouble( "/tools/eraser/width", adj->value * 0.01 );
5237 update_presets_list(tbl);
5238 }
5240 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5241 {
5242 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5243 bool eraserMode = ege_select_one_action_get_active( act ) != 0;
5244 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5245 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5246 prefs->setBool( "/tools/eraser/mode", eraserMode );
5247 }
5249 // only take action if run by the attr_changed listener
5250 if (!g_object_get_data( tbl, "freeze" )) {
5251 // in turn, prevent listener from responding
5252 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5254 if ( eraserMode != 0 ) {
5255 } else {
5256 }
5257 // TODO finish implementation
5259 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5260 }
5261 }
5263 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5264 {
5265 {
5266 /* Width */
5267 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5268 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5269 EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5270 _("Pen Width"), _("Width:"),
5271 _("The width of the eraser pen (relative to the visible canvas area)"),
5272 "/tools/eraser/width", 15,
5273 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5274 1, 100, 1.0, 0.0,
5275 labels, values, G_N_ELEMENTS(labels),
5276 sp_erc_width_value_changed, 0.01, 0, 100 );
5277 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5278 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5279 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5280 }
5282 {
5283 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5285 GtkTreeIter iter;
5286 gtk_list_store_append( model, &iter );
5287 gtk_list_store_set( model, &iter,
5288 0, _("Delete"),
5289 1, _("Delete objects touched by the eraser"),
5290 2, "delete_object",
5291 -1 );
5293 gtk_list_store_append( model, &iter );
5294 gtk_list_store_set( model, &iter,
5295 0, _("Cut"),
5296 1, _("Cut out from objects"),
5297 2, "difference",
5298 -1 );
5300 EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5301 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5302 g_object_set_data( holder, "eraser_mode_action", act );
5304 ege_select_one_action_set_appearance( act, "full" );
5305 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5306 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5307 ege_select_one_action_set_icon_column( act, 2 );
5308 ege_select_one_action_set_tooltip_column( act, 1 );
5310 /// @todo Convert to boolean?
5311 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5312 gint eraserMode = prefs->getBool("/tools/eraser/mode") ? 1 : 0;
5313 ege_select_one_action_set_active( act, eraserMode );
5314 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
5315 }
5317 }
5319 //########################
5320 //## Text Toolbox ##
5321 //########################
5322 /*
5323 static void
5324 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
5325 {
5326 //Call back for letter sizing spinbutton
5327 }
5329 static void
5330 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
5331 {
5332 //Call back for line height spinbutton
5333 }
5335 static void
5336 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5337 {
5338 //Call back for horizontal kerning spinbutton
5339 }
5341 static void
5342 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5343 {
5344 //Call back for vertical kerning spinbutton
5345 }
5347 static void
5348 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
5349 {
5350 //Call back for letter rotation spinbutton
5351 }*/
5353 namespace {
5355 bool popdown_visible = false;
5356 bool popdown_hasfocus = false;
5358 void
5359 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
5360 {
5361 SPStyle *query =
5362 sp_style_new (SP_ACTIVE_DOCUMENT);
5364 // int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5366 int result_family =
5367 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5369 int result_style =
5370 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5372 int result_numbers =
5373 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5375 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5377 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5378 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
5379 // there are no texts in selection, read from prefs
5381 sp_style_read_from_prefs(query, "/tools/text");
5383 if (g_object_get_data(tbl, "text_style_from_prefs")) {
5384 // do not reset the toolbar style from prefs if we already did it last time
5385 sp_style_unref(query);
5386 return;
5387 }
5388 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
5389 } else {
5390 g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
5391 }
5393 if (query->text)
5394 {
5395 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
5396 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5397 gtk_entry_set_text (GTK_ENTRY (entry), "");
5399 } else if (query->text->font_specification.value || query->text->font_family.value) {
5401 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5403 // Get the font that corresponds
5404 Glib::ustring familyName;
5406 font_instance * font = font_factory::Default()->FaceFromStyle(query);
5407 if (font) {
5408 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
5409 font->Unref();
5410 font = NULL;
5411 }
5413 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
5415 Gtk::TreePath path;
5416 try {
5417 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
5418 } catch (...) {
5419 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
5420 sp_style_unref(query);
5421 return;
5422 }
5424 GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5425 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5427 g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
5429 gtk_tree_selection_select_path (tselection, path.gobj());
5430 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5432 g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
5433 }
5435 //Size
5436 {
5437 GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
5438 gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
5439 g_object_set_data(tbl, "size-block", gpointer(1));
5440 gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
5441 g_object_set_data(tbl, "size-block", gpointer(0));
5442 g_free(str);
5443 }
5445 //Anchor
5446 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
5447 {
5448 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
5449 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5450 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5451 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5452 }
5453 else
5454 {
5455 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
5456 {
5457 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
5458 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5459 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5460 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5461 }
5462 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
5463 {
5464 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
5465 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5466 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5467 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5468 }
5469 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
5470 {
5471 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
5472 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5473 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5474 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5475 }
5476 }
5478 //Style
5479 {
5480 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
5482 gboolean active = gtk_toggle_button_get_active (button);
5483 gboolean check = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
5485 if (active != check)
5486 {
5487 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5488 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5489 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5490 }
5491 }
5493 {
5494 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
5496 gboolean active = gtk_toggle_button_get_active (button);
5497 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
5499 if (active != check)
5500 {
5501 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5502 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5503 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5504 }
5505 }
5507 //Orientation
5508 //locking both buttons, changing one affect all group (both)
5509 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
5510 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5512 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
5513 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
5515 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
5516 {
5517 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5518 }
5519 else
5520 {
5521 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
5522 }
5523 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5524 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
5525 }
5527 sp_style_unref(query);
5528 }
5530 void
5531 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
5532 {
5533 sp_text_toolbox_selection_changed (selection, tbl);
5534 }
5536 void
5537 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
5538 {
5539 sp_text_toolbox_selection_changed (NULL, tbl);
5540 }
5542 void
5543 sp_text_toolbox_family_changed (GtkTreeSelection *selection,
5544 GObject *tbl)
5545 {
5546 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5547 GtkTreeModel *model = 0;
5548 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5549 GtkTreeIter iter;
5550 char *family = 0;
5552 gdk_pointer_ungrab (GDK_CURRENT_TIME);
5553 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5555 if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
5556 return;
5557 }
5559 gtk_tree_model_get (model, &iter, 0, &family, -1);
5561 if (g_object_get_data (G_OBJECT (selection), "block"))
5562 {
5563 gtk_entry_set_text (GTK_ENTRY (entry), family);
5564 return;
5565 }
5567 gtk_entry_set_text (GTK_ENTRY (entry), family);
5569 SPStyle *query =
5570 sp_style_new (SP_ACTIVE_DOCUMENT);
5572 int result_fontspec =
5573 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5575 //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5577 SPCSSAttr *css = sp_repr_css_attr_new ();
5580 // First try to get the font spec from the stored value
5581 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
5583 if (fontSpec.empty()) {
5584 // Construct a new font specification if it does not yet exist
5585 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5586 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5587 fontFromStyle->Unref();
5588 }
5590 if (!fontSpec.empty()) {
5591 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
5592 if (!newFontSpec.empty() && fontSpec != newFontSpec) {
5593 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
5594 if (font) {
5595 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5597 // Set all the these just in case they were altered when finding the best
5598 // match for the new family and old style...
5600 gchar c[256];
5602 font->Family(c, 256);
5603 sp_repr_css_set_property (css, "font-family", c);
5605 font->Attribute( "weight", c, 256);
5606 sp_repr_css_set_property (css, "font-weight", c);
5608 font->Attribute("style", c, 256);
5609 sp_repr_css_set_property (css, "font-style", c);
5611 font->Attribute("stretch", c, 256);
5612 sp_repr_css_set_property (css, "font-stretch", c);
5614 font->Attribute("variant", c, 256);
5615 sp_repr_css_set_property (css, "font-variant", c);
5617 font->Unref();
5618 }
5619 }
5620 }
5622 // If querying returned nothing, set the default style of the tool (for new texts)
5623 if (result_fontspec == QUERY_STYLE_NOTHING)
5624 {
5625 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5626 prefs->setStyle("/tools/text/style", css);
5627 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
5628 }
5629 else
5630 {
5631 sp_desktop_set_style (desktop, css, true, true);
5632 }
5634 sp_style_unref(query);
5636 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5637 _("Text: Change font family"));
5638 sp_repr_css_attr_unref (css);
5639 g_free(family);
5640 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5642 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5643 }
5645 /* This is where execution comes when the contents of the font family box have been completed
5646 by the press of the return key */
5647 void
5648 sp_text_toolbox_family_entry_activate (GtkEntry *entry,
5649 GObject *tbl)
5650 {
5651 const char *family = gtk_entry_get_text (entry); // Fetch the requested font family
5653 // Try to match that to a known font. If not, then leave current font alone and remain focused on text box
5654 try {
5655 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
5656 GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5657 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5658 gtk_tree_selection_select_path (selection, path.gobj());
5659 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5660 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5661 } catch (...) {
5662 if (family && strlen (family))
5663 {
5664 gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5665 }
5666 }
5667 }
5669 void
5670 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
5671 gpointer data)
5672 {
5673 if (g_object_get_data (G_OBJECT (button), "block")) return;
5674 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
5675 int prop = GPOINTER_TO_INT(data);
5677 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5678 SPCSSAttr *css = sp_repr_css_attr_new ();
5680 switch (prop)
5681 {
5682 case 0:
5683 {
5684 sp_repr_css_set_property (css, "text-anchor", "start");
5685 sp_repr_css_set_property (css, "text-align", "start");
5686 break;
5687 }
5688 case 1:
5689 {
5690 sp_repr_css_set_property (css, "text-anchor", "middle");
5691 sp_repr_css_set_property (css, "text-align", "center");
5692 break;
5693 }
5695 case 2:
5696 {
5697 sp_repr_css_set_property (css, "text-anchor", "end");
5698 sp_repr_css_set_property (css, "text-align", "end");
5699 break;
5700 }
5702 case 3:
5703 {
5704 sp_repr_css_set_property (css, "text-anchor", "start");
5705 sp_repr_css_set_property (css, "text-align", "justify");
5706 break;
5707 }
5708 }
5710 SPStyle *query =
5711 sp_style_new (SP_ACTIVE_DOCUMENT);
5712 int result_numbers =
5713 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5715 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5716 if (result_numbers == QUERY_STYLE_NOTHING)
5717 {
5718 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5719 prefs->setStyle("/tools/text/style", css);
5720 }
5722 sp_style_unref(query);
5724 sp_desktop_set_style (desktop, css, true, true);
5725 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5726 _("Text: Change alignment"));
5727 sp_repr_css_attr_unref (css);
5729 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5730 }
5732 void
5733 sp_text_toolbox_style_toggled (GtkToggleButton *button,
5734 gpointer data)
5735 {
5736 if (g_object_get_data (G_OBJECT (button), "block")) return;
5738 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5739 SPCSSAttr *css = sp_repr_css_attr_new ();
5740 int prop = GPOINTER_TO_INT(data);
5741 bool active = gtk_toggle_button_get_active (button);
5743 SPStyle *query =
5744 sp_style_new (SP_ACTIVE_DOCUMENT);
5746 int result_fontspec =
5747 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5749 //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5750 //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5751 //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5753 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
5754 Glib::ustring newFontSpec = "";
5756 if (fontSpec.empty()) {
5757 // Construct a new font specification if it does not yet exist
5758 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5759 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5760 fontFromStyle->Unref();
5761 }
5763 switch (prop)
5764 {
5765 case 0:
5766 {
5767 if (!fontSpec.empty()) {
5768 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
5769 }
5770 if (fontSpec != newFontSpec) {
5771 // Don't even set the bold if the font didn't exist on the system
5772 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
5773 }
5774 break;
5775 }
5777 case 1:
5778 {
5779 if (!fontSpec.empty()) {
5780 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
5781 }
5782 if (fontSpec != newFontSpec) {
5783 // Don't even set the italic if the font didn't exist on the system
5784 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
5785 }
5786 break;
5787 }
5788 }
5790 if (!newFontSpec.empty()) {
5791 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5792 }
5794 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5795 if (result_fontspec == QUERY_STYLE_NOTHING)
5796 {
5797 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5798 prefs->setStyle("/tools/text/style", css);
5799 }
5801 sp_style_unref(query);
5803 sp_desktop_set_style (desktop, css, true, true);
5804 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5805 _("Text: Change font style"));
5806 sp_repr_css_attr_unref (css);
5808 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5809 }
5811 void
5812 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
5813 gpointer data)
5814 {
5815 if (g_object_get_data (G_OBJECT (button), "block")) {
5816 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5817 return;
5818 }
5820 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5821 SPCSSAttr *css = sp_repr_css_attr_new ();
5822 int prop = GPOINTER_TO_INT(data);
5824 switch (prop)
5825 {
5826 case 0:
5827 {
5828 sp_repr_css_set_property (css, "writing-mode", "lr");
5829 break;
5830 }
5832 case 1:
5833 {
5834 sp_repr_css_set_property (css, "writing-mode", "tb");
5835 break;
5836 }
5837 }
5839 SPStyle *query =
5840 sp_style_new (SP_ACTIVE_DOCUMENT);
5841 int result_numbers =
5842 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5844 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5845 if (result_numbers == QUERY_STYLE_NOTHING)
5846 {
5847 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5848 prefs->setStyle("/tools/text/style", css);
5849 }
5851 sp_desktop_set_style (desktop, css, true, true);
5852 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5853 _("Text: Change orientation"));
5854 sp_repr_css_attr_unref (css);
5856 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5857 }
5859 gboolean
5860 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5861 {
5862 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5863 if (!desktop) return FALSE;
5865 switch (get_group0_keyval (event)) {
5866 case GDK_Escape: // defocus
5867 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5868 sp_text_toolbox_selection_changed (NULL, tbl); // update
5869 return TRUE; // I consumed the event
5870 break;
5871 }
5872 return FALSE;
5873 }
5875 gboolean
5876 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5877 {
5878 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5879 if (!desktop) return FALSE;
5881 switch (get_group0_keyval (event)) {
5882 case GDK_KP_Enter:
5883 case GDK_Return:
5884 case GDK_Escape: // defocus
5885 gtk_widget_hide (w);
5886 popdown_visible = false;
5887 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5888 return TRUE; // I consumed the event
5889 break;
5890 case GDK_w:
5891 case GDK_W:
5892 if (event->state & GDK_CONTROL_MASK) {
5893 gtk_widget_hide (w);
5894 popdown_visible = false;
5895 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5896 return TRUE; // I consumed the event
5897 }
5898 break;
5899 }
5900 return FALSE;
5901 }
5904 void
5905 sp_text_toolbox_size_changed (GtkComboBox *cbox,
5906 GObject *tbl)
5907 {
5908 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5910 if (g_object_get_data (tbl, "size-block")) return;
5912 // If this is not from selecting a size in the list (in which case get_active will give the
5913 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5914 // process this event. This fixes GTK's stupid insistence on sending an activate change every
5915 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5916 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5917 return;
5919 gdouble value = -1;
5920 {
5921 gchar *endptr;
5922 gchar *const text = gtk_combo_box_get_active_text(cbox);
5923 if (text) {
5924 value = g_strtod(text, &endptr);
5925 if (endptr == text) { // Conversion failed, non-numeric input.
5926 value = -1;
5927 }
5928 g_free(text);
5929 }
5930 }
5931 if (value <= 0) {
5932 return; // could not parse value
5933 }
5935 SPCSSAttr *css = sp_repr_css_attr_new ();
5936 Inkscape::CSSOStringStream osfs;
5937 osfs << value;
5938 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5940 SPStyle *query =
5941 sp_style_new (SP_ACTIVE_DOCUMENT);
5942 int result_numbers =
5943 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5945 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5946 if (result_numbers == QUERY_STYLE_NOTHING)
5947 {
5948 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5949 prefs->setStyle("/tools/text/style", css);
5950 }
5952 sp_style_unref(query);
5954 sp_desktop_set_style (desktop, css, true, true);
5955 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5956 _("Text: Change font size"));
5957 sp_repr_css_attr_unref (css);
5959 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5960 }
5962 gboolean
5963 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5964 {
5965 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5966 if (!desktop) return FALSE;
5968 if (!g_object_get_data (tbl, "esc-pressed")) {
5969 g_object_set_data (tbl, "enter-pressed", gpointer(1));
5970 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5971 sp_text_toolbox_size_changed (cbox, tbl);
5972 g_object_set_data (tbl, "enter-pressed", gpointer(0));
5973 }
5974 return FALSE; // I consumed the event
5975 }
5978 gboolean
5979 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5980 {
5981 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5982 if (!desktop) return FALSE;
5984 switch (get_group0_keyval (event)) {
5985 case GDK_Escape: // defocus
5986 g_object_set_data (tbl, "esc-pressed", gpointer(1));
5987 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5988 g_object_set_data (tbl, "esc-pressed", gpointer(0));
5989 return TRUE; // I consumed the event
5990 break;
5991 case GDK_Return: // defocus
5992 case GDK_KP_Enter:
5993 g_object_set_data (tbl, "enter-pressed", gpointer(1));
5994 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5995 sp_text_toolbox_size_changed (cbox, tbl);
5996 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5997 g_object_set_data (tbl, "enter-pressed", gpointer(0));
5998 return TRUE; // I consumed the event
5999 break;
6000 }
6001 return FALSE;
6002 }
6004 void
6005 sp_text_toolbox_text_popdown_clicked (GtkButton */*button*/,
6006 GObject *tbl)
6007 {
6008 GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
6009 GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6010 int x, y;
6012 if (!popdown_visible)
6013 {
6014 gdk_window_get_origin (widget->window, &x, &y);
6015 gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
6016 gtk_widget_show_all (popdown);
6017 //sp_transientize (popdown);
6019 gdk_pointer_grab (widget->window, TRUE,
6020 GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
6021 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
6022 GDK_POINTER_MOTION_MASK),
6023 NULL, NULL, GDK_CURRENT_TIME);
6025 gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
6027 popdown_visible = true;
6028 }
6029 else
6030 {
6031 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6032 gdk_pointer_ungrab (GDK_CURRENT_TIME);
6033 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
6034 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6035 gtk_widget_hide (popdown);
6036 popdown_visible = false;
6037 }
6038 }
6040 gboolean
6041 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
6042 GdkEventFocus */*event*/,
6043 GObject */*tbl*/)
6044 {
6045 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
6046 return FALSE;
6047 }
6049 gboolean
6050 sp_text_toolbox_popdown_focus_out (GtkWidget *popdown,
6051 GdkEventFocus */*event*/,
6052 GObject */*tbl*/)
6053 {
6054 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6056 if (popdown_hasfocus) {
6057 gtk_widget_hide (popdown);
6058 popdown_hasfocus = false;
6059 popdown_visible = false;
6060 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6061 return TRUE;
6062 }
6063 return FALSE;
6064 }
6066 gboolean
6067 sp_text_toolbox_popdown_focus_in (GtkWidget */*popdown*/,
6068 GdkEventFocus */*event*/,
6069 GObject */*tbl*/)
6070 {
6071 popdown_hasfocus = true;
6072 return TRUE;
6073 }
6076 void
6077 cell_data_func (GtkTreeViewColumn */*column*/,
6078 GtkCellRenderer *cell,
6079 GtkTreeModel *tree_model,
6080 GtkTreeIter *iter,
6081 gpointer /*data*/)
6082 {
6083 gchar *family;
6084 gtk_tree_model_get(tree_model, iter, 0, &family, -1);
6085 gchar *const family_escaped = g_markup_escape_text(family, -1);
6087 static char const *const sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
6088 gchar *const sample_escaped = g_markup_escape_text(sample, -1);
6090 std::stringstream markup;
6091 markup << family_escaped << " <span foreground='darkgray' font_family='"
6092 << family_escaped << "'>" << sample_escaped << "</span>";
6093 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6095 g_free(family);
6096 g_free(family_escaped);
6097 g_free(sample_escaped);
6098 }
6100 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
6101 GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
6102 if (completion) {
6103 gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
6104 g_object_unref (completion);
6105 }
6106 }
6108 GtkWidget*
6109 sp_text_toolbox_new (SPDesktop *desktop)
6110 {
6111 GtkToolbar *tbl = GTK_TOOLBAR(gtk_toolbar_new());
6112 GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("/toolbox/secondary", 1));
6114 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
6115 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
6117 GtkTooltips *tt = gtk_tooltips_new();
6118 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
6120 ////////////Family
6121 //Window
6122 GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
6123 gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
6125 //Entry
6126 GtkWidget *entry = gtk_entry_new ();
6127 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
6128 GtkEntryCompletion *completion = gtk_entry_completion_new ();
6129 gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
6130 gtk_entry_completion_set_text_column (completion, 0);
6131 gtk_entry_completion_set_minimum_key_length (completion, 1);
6132 g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
6133 gtk_entry_set_completion (GTK_ENTRY(entry), completion);
6134 gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
6135 gtk_toolbar_append_widget( tbl, entry, "", "" );
6136 g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
6138 //Button
6139 GtkWidget *button = gtk_button_new ();
6140 gtk_container_add (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
6141 gtk_toolbar_append_widget( tbl, button, "", "");
6143 //Popdown
6144 GtkWidget *sw = gtk_scrolled_window_new (NULL, NULL);
6145 GtkWidget *treeview = gtk_tree_view_new ();
6147 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
6148 GtkTreeViewColumn *column = gtk_tree_view_column_new ();
6149 gtk_tree_view_column_pack_start (column, cell, FALSE);
6150 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
6151 gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
6152 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
6154 gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
6155 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
6156 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
6158 //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
6160 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
6161 gtk_container_add (GTK_CONTAINER (sw), treeview);
6163 gtk_container_add (GTK_CONTAINER (window), sw);
6164 gtk_widget_set_size_request (window, 300, 450);
6166 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
6167 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
6168 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
6170 g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
6172 g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
6173 g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
6174 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
6176 GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
6177 g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6179 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
6180 g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
6181 g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
6182 g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
6183 g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
6185 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
6186 GtkWidget *box = gtk_event_box_new ();
6187 gtk_container_add (GTK_CONTAINER (box), image);
6188 gtk_toolbar_append_widget( tbl, box, "", "");
6189 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
6190 GtkTooltips *tooltips = gtk_tooltips_new ();
6191 gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
6192 gtk_widget_hide (GTK_WIDGET (box));
6193 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
6195 ////////////Size
6196 gchar const *const sizes[] = {
6197 "4", "6", "8", "9", "10", "11", "12", "13", "14",
6198 "16", "18", "20", "22", "24", "28",
6199 "32", "36", "40", "48", "56", "64", "72", "144"
6200 };
6202 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
6203 for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
6204 gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
6205 }
6206 gtk_widget_set_size_request (cbox, 80, -1);
6207 gtk_toolbar_append_widget( tbl, cbox, "", "");
6208 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
6209 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
6210 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
6211 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
6213 ////////////Text anchor
6214 GtkWidget *group = gtk_radio_button_new (NULL);
6215 GtkWidget *row = gtk_hbox_new (FALSE, 4);
6216 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
6218 // left
6219 GtkWidget *rbutton = group;
6220 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6221 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
6222 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6224 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6225 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
6226 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
6227 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
6229 // center
6230 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6231 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6232 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
6233 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6235 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6236 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
6237 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
6238 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
6240 // right
6241 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6242 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6243 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
6244 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6246 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6247 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
6248 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
6249 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
6251 // fill
6252 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6253 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6254 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
6255 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6257 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6258 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
6259 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
6260 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
6262 gtk_toolbar_append_widget( tbl, row, "", "");
6264 //spacer
6265 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6267 ////////////Text style
6268 row = gtk_hbox_new (FALSE, 4);
6270 // bold
6271 rbutton = gtk_toggle_button_new ();
6272 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6273 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
6274 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6275 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
6277 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6278 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
6279 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
6281 // italic
6282 rbutton = gtk_toggle_button_new ();
6283 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6284 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
6285 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6286 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
6288 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6289 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
6290 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
6292 gtk_toolbar_append_widget( tbl, row, "", "");
6294 //spacer
6295 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6297 ////////////Text orientation
6298 group = gtk_radio_button_new (NULL);
6299 row = gtk_hbox_new (FALSE, 4);
6300 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
6302 // horizontal
6303 rbutton = group;
6304 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6305 gtk_container_add (GTK_CONTAINER (rbutton),
6306 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
6307 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6308 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
6310 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6311 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
6312 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
6314 // vertical
6315 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6316 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6317 gtk_container_add (GTK_CONTAINER (rbutton),
6318 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
6319 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6320 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
6322 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
6323 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
6324 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
6325 gtk_toolbar_append_widget( tbl, row, "", "" );
6328 //watch selection
6329 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
6331 sigc::connection *c_selection_changed =
6332 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
6333 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
6334 pool->add_connection ("selection-changed", c_selection_changed);
6336 sigc::connection *c_selection_modified =
6337 new sigc::connection (sp_desktop_selection (desktop)->connectModified
6338 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
6339 pool->add_connection ("selection-modified", c_selection_modified);
6341 sigc::connection *c_subselection_changed =
6342 new sigc::connection (desktop->connectToolSubselectionChanged
6343 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
6344 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
6346 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
6349 gtk_widget_show_all( GTK_WIDGET(tbl) );
6351 return GTK_WIDGET(tbl);
6352 } // end of sp_text_toolbox_new()
6354 }//<unnamed> namespace
6357 //#########################
6358 //## Connector ##
6359 //#########################
6361 static void sp_connector_path_set_avoid(void)
6362 {
6363 cc_selection_set_avoid(true);
6364 }
6367 static void sp_connector_path_set_ignore(void)
6368 {
6369 cc_selection_set_avoid(false);
6370 }
6374 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
6375 {
6376 // quit if run by the _changed callbacks
6377 if (g_object_get_data( tbl, "freeze" )) {
6378 return;
6379 }
6381 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
6382 SPDocument *doc = sp_desktop_document(desktop);
6384 if (!sp_document_get_undo_sensitive(doc))
6385 {
6386 return;
6387 }
6389 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6391 if ( repr->attribute("inkscape:connector-spacing") ) {
6392 gdouble priorValue = gtk_adjustment_get_value(adj);
6393 sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
6394 if ( priorValue == gtk_adjustment_get_value(adj) ) {
6395 return;
6396 }
6397 } else if ( adj->value == defaultConnSpacing ) {
6398 return;
6399 }
6401 // in turn, prevent callbacks from responding
6402 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6404 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
6405 SP_OBJECT(desktop->namedview)->updateRepr();
6407 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
6408 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
6409 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
6410 Geom::Matrix m = Geom::identity();
6411 avoid_item_move(&m, item);
6412 }
6414 if (items) {
6415 g_slist_free(items);
6416 }
6418 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
6419 _("Change connector spacing"));
6421 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6423 spinbutton_defocus(GTK_OBJECT(tbl));
6424 }
6426 static void sp_connector_graph_layout(void)
6427 {
6428 if (!SP_ACTIVE_DESKTOP) return;
6429 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6431 // hack for clones, see comment in align-and-distribute.cpp
6432 int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
6433 prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
6435 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
6437 prefs->setInt("/options/clonecompensation/value", saved_compensation);
6439 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
6440 }
6442 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6443 {
6444 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6445 prefs->setBool("/tools/connector/directedlayout",
6446 gtk_toggle_action_get_active( act ));
6447 }
6449 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6450 {
6451 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6452 prefs->setBool("/tools/connector/avoidoverlaplayout",
6453 gtk_toggle_action_get_active( act ));
6454 }
6457 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
6458 {
6459 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6460 prefs->setDouble("/tools/connector/length", adj->value);
6461 spinbutton_defocus(GTK_OBJECT(tbl));
6462 }
6464 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
6465 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
6466 bool /*is_interactive*/, gpointer data)
6467 {
6468 GtkWidget *tbl = GTK_WIDGET(data);
6470 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6471 return;
6472 }
6473 if (strcmp(name, "inkscape:connector-spacing") != 0) {
6474 return;
6475 }
6477 GtkAdjustment *adj = (GtkAdjustment*)
6478 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
6479 gdouble spacing = defaultConnSpacing;
6480 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
6482 gtk_adjustment_set_value(adj, spacing);
6483 }
6486 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
6487 NULL, /* child_added */
6488 NULL, /* child_removed */
6489 connector_tb_event_attr_changed,
6490 NULL, /* content_changed */
6491 NULL /* order_changed */
6492 };
6495 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
6496 {
6497 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6498 Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
6500 {
6501 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
6502 _("Avoid"),
6503 _("Make connectors avoid selected objects"),
6504 "connector_avoid",
6505 secondarySize );
6506 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
6507 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6508 }
6510 {
6511 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
6512 _("Ignore"),
6513 _("Make connectors ignore selected objects"),
6514 "connector_ignore",
6515 secondarySize );
6516 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
6517 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6518 }
6520 EgeAdjustmentAction* eact = 0;
6522 // Spacing spinbox
6523 eact = create_adjustment_action( "ConnectorSpacingAction",
6524 _("Connector Spacing"), _("Spacing:"),
6525 _("The amount of space left around objects by auto-routing connectors"),
6526 "/tools/connector/spacing", defaultConnSpacing,
6527 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
6528 0, 100, 1.0, 10.0,
6529 0, 0, 0,
6530 connector_spacing_changed, 1, 0 );
6531 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6533 // Graph (connector network) layout
6534 {
6535 InkAction* inky = ink_action_new( "ConnectorGraphAction",
6536 _("Graph"),
6537 _("Nicely arrange selected connector network"),
6538 "graph_layout",
6539 secondarySize );
6540 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
6541 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6542 }
6544 // Default connector length spinbox
6545 eact = create_adjustment_action( "ConnectorLengthAction",
6546 _("Connector Length"), _("Length:"),
6547 _("Ideal length for connectors when layout is applied"),
6548 "/tools/connector/length", 100,
6549 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
6550 10, 1000, 10.0, 100.0,
6551 0, 0, 0,
6552 connector_length_changed, 1, 0 );
6553 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6556 // Directed edges toggle button
6557 {
6558 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
6559 _("Downwards"),
6560 _("Make connectors with end-markers (arrows) point downwards"),
6561 "directed_graph",
6562 Inkscape::ICON_SIZE_DECORATION );
6563 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6565 bool tbuttonstate = prefs->getBool("/tools/connector/directedlayout");
6566 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
6568 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
6569 }
6571 // Avoid overlaps toggle button
6572 {
6573 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
6574 _("Remove overlaps"),
6575 _("Do not allow overlapping shapes"),
6576 "remove_overlaps",
6577 Inkscape::ICON_SIZE_DECORATION );
6578 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6580 bool tbuttonstate = prefs->getBool("/tools/connector/avoidoverlaplayout");
6581 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), (tbuttonstate ? TRUE : FALSE ));
6583 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
6584 }
6586 // Code to watch for changes to the connector-spacing attribute in
6587 // the XML.
6588 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6589 g_assert(repr != NULL);
6591 purge_repr_listener( holder, holder );
6593 if (repr) {
6594 g_object_set_data( holder, "repr", repr );
6595 Inkscape::GC::anchor(repr);
6596 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
6597 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
6598 }
6599 } // end of sp_connector_toolbox_prep()
6602 //#########################
6603 //## Paintbucket ##
6604 //#########################
6606 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
6607 {
6608 gint channels = ege_select_one_action_get_active( act );
6609 flood_channels_set_channels( channels );
6610 }
6612 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
6613 {
6614 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6615 prefs->setInt("/tools/paintbucket/threshold", (gint)adj->value);
6616 }
6618 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
6619 {
6620 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6621 prefs->setBool("/tools/paintbucket/autogap", ege_select_one_action_get_active( act ));
6622 }
6624 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
6625 {
6626 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
6627 SPUnit const *unit = tracker->getActiveUnit();
6628 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6630 prefs->setDouble("/tools/paintbucket/offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
6631 prefs->setString("/tools/paintbucket/offsetunits", sp_unit_get_abbreviation(unit));
6632 }
6634 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
6635 {
6636 // FIXME: make defaults settable via Inkscape Options
6637 struct KeyValue {
6638 char const *key;
6639 double value;
6640 } const key_values[] = {
6641 {"threshold", 15},
6642 {"offset", 0.0}
6643 };
6645 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
6646 KeyValue const &kv = key_values[i];
6647 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
6648 if ( adj ) {
6649 gtk_adjustment_set_value(adj, kv.value);
6650 }
6651 }
6653 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
6654 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
6655 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
6656 ege_select_one_action_set_active( autogap_action, 0 );
6657 }
6659 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6660 {
6661 EgeAdjustmentAction* eact = 0;
6662 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6664 {
6665 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6667 GList* items = 0;
6668 gint count = 0;
6669 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
6670 {
6671 GtkTreeIter iter;
6672 gtk_list_store_append( model, &iter );
6673 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6674 count++;
6675 }
6676 g_list_free( items );
6677 items = 0;
6678 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
6679 g_object_set( act1, "short_label", _("Fill by:"), NULL );
6680 ege_select_one_action_set_appearance( act1, "compact" );
6681 ege_select_one_action_set_active( act1, prefs->getInt("/tools/paintbucket/channels", 0) );
6682 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
6683 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
6684 g_object_set_data( holder, "channels_action", act1 );
6685 }
6687 // Spacing spinbox
6688 {
6689 eact = create_adjustment_action(
6690 "ThresholdAction",
6691 _("Fill Threshold"), _("Threshold:"),
6692 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
6693 "/tools/paintbucket/threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
6694 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
6695 0, 0, 0,
6696 paintbucket_threshold_changed, 1, 0 );
6698 ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6699 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6700 }
6702 // Create the units menu.
6703 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
6704 Glib::ustring stored_unit = prefs->getString("/tools/paintbucket/offsetunits");
6705 if (!stored_unit.empty())
6706 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit.data()));
6707 g_object_set_data( holder, "tracker", tracker );
6708 {
6709 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
6710 gtk_action_group_add_action( mainActions, act );
6711 }
6713 // Offset spinbox
6714 {
6715 eact = create_adjustment_action(
6716 "OffsetAction",
6717 _("Grow/shrink by"), _("Grow/shrink by:"),
6718 _("The amount to grow (positive) or shrink (negative) the created fill path"),
6719 "/tools/paintbucket/offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
6720 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
6721 0, 0, 0,
6722 paintbucket_offset_changed, 1, 2);
6723 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
6725 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6726 }
6728 /* Auto Gap */
6729 {
6730 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6732 GList* items = 0;
6733 gint count = 0;
6734 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
6735 {
6736 GtkTreeIter iter;
6737 gtk_list_store_append( model, &iter );
6738 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6739 count++;
6740 }
6741 g_list_free( items );
6742 items = 0;
6743 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
6744 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
6745 ege_select_one_action_set_appearance( act2, "compact" );
6746 ege_select_one_action_set_active( act2, prefs->getBool("/tools/paintbucket/autogap") );
6747 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
6748 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
6749 g_object_set_data( holder, "autogap_action", act2 );
6750 }
6752 /* Reset */
6753 {
6754 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
6755 _("Defaults"),
6756 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
6757 GTK_STOCK_CLEAR );
6758 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
6759 gtk_action_group_add_action( mainActions, act );
6760 gtk_action_set_sensitive( act, TRUE );
6761 }
6763 }
6765 /*
6766 Local Variables:
6767 mode:c++
6768 c-file-style:"stroustrup"
6769 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
6770 indent-tabs-mode:nil
6771 fill-column:99
6772 End:
6773 */
6774 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :